ddraw: Mark surfaces as lost when the device window is deactivated.
[wine.git] / dlls / wined3d / surface.c
blob29687c3639feb8d423f81b50a81b96fa1802b98d
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)
755 context = context_acquire(device, surface);
756 surface_load_location(surface, context, surface->container->resource.draw_binding);
757 context_release(context);
759 else if (surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
760 FIXME("Depth / stencil buffer locking is not implemented.\n");
763 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
765 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
766 return FALSE;
767 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
768 return FALSE;
769 return TRUE;
772 static void surface_depth_blt_fbo(const struct wined3d_device *device,
773 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
774 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
776 const struct wined3d_gl_info *gl_info;
777 struct wined3d_context *context;
778 DWORD src_mask, dst_mask;
779 GLbitfield gl_mask;
781 TRACE("device %p\n", device);
782 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
783 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
784 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
785 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
787 src_mask = src_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
788 dst_mask = dst_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
790 if (src_mask != dst_mask)
792 ERR("Incompatible formats %s and %s.\n",
793 debug_d3dformat(src_surface->resource.format->id),
794 debug_d3dformat(dst_surface->resource.format->id));
795 return;
798 if (!src_mask)
800 ERR("Not a depth / stencil format: %s.\n",
801 debug_d3dformat(src_surface->resource.format->id));
802 return;
805 gl_mask = 0;
806 if (src_mask & WINED3DFMT_FLAG_DEPTH)
807 gl_mask |= GL_DEPTH_BUFFER_BIT;
808 if (src_mask & WINED3DFMT_FLAG_STENCIL)
809 gl_mask |= GL_STENCIL_BUFFER_BIT;
811 context = context_acquire(device, NULL);
812 if (!context->valid)
814 context_release(context);
815 WARN("Invalid context, skipping blit.\n");
816 return;
819 /* Make sure the locations are up-to-date. Loading the destination
820 * surface isn't required if the entire surface is overwritten. */
821 surface_load_location(src_surface, context, src_location);
822 if (!surface_is_full_rect(dst_surface, dst_rect))
823 surface_load_location(dst_surface, context, dst_location);
824 else
825 wined3d_surface_prepare(dst_surface, context, dst_location);
827 gl_info = context->gl_info;
829 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
830 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
832 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
833 context_set_draw_buffer(context, GL_NONE);
834 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
835 context_invalidate_state(context, STATE_FRAMEBUFFER);
837 if (gl_mask & GL_DEPTH_BUFFER_BIT)
839 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
840 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
842 if (gl_mask & GL_STENCIL_BUFFER_BIT)
844 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
846 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
847 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
849 gl_info->gl_ops.gl.p_glStencilMask(~0U);
850 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
853 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
854 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
856 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
857 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
858 checkGLcall("glBlitFramebuffer()");
860 if (wined3d_settings.strict_draw_ordering)
861 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
863 context_release(context);
866 /* Blit between surface locations. Onscreen on different swapchains is not supported.
867 * Depth / stencil is not supported. Context activation is done by the caller. */
868 static void surface_blt_fbo(const struct wined3d_device *device,
869 struct wined3d_context *old_ctx, enum wined3d_texture_filter_type filter,
870 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
871 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
873 const struct wined3d_gl_info *gl_info;
874 struct wined3d_context *context = old_ctx;
875 struct wined3d_surface *required_rt, *restore_rt = NULL;
876 RECT src_rect, dst_rect;
877 GLenum gl_filter;
878 GLenum buffer;
880 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
881 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
882 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
883 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
884 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
886 src_rect = *src_rect_in;
887 dst_rect = *dst_rect_in;
889 switch (filter)
891 case WINED3D_TEXF_LINEAR:
892 gl_filter = GL_LINEAR;
893 break;
895 default:
896 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
897 case WINED3D_TEXF_NONE:
898 case WINED3D_TEXF_POINT:
899 gl_filter = GL_NEAREST;
900 break;
903 /* Resolve the source surface first if needed. */
904 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
905 && (src_surface->resource.format->id != dst_surface->resource.format->id
906 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
907 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
908 src_location = WINED3D_LOCATION_RB_RESOLVED;
910 /* Make sure the locations are up-to-date. Loading the destination
911 * surface isn't required if the entire surface is overwritten. (And is
912 * in fact harmful if we're being called by surface_load_location() with
913 * the purpose of loading the destination surface.) */
914 surface_load_location(src_surface, old_ctx, src_location);
915 if (!surface_is_full_rect(dst_surface, &dst_rect))
916 surface_load_location(dst_surface, old_ctx, dst_location);
917 else
918 wined3d_surface_prepare(dst_surface, old_ctx, dst_location);
921 if (src_location == WINED3D_LOCATION_DRAWABLE) required_rt = src_surface;
922 else if (dst_location == WINED3D_LOCATION_DRAWABLE) required_rt = dst_surface;
923 else required_rt = NULL;
925 if (required_rt && required_rt != old_ctx->current_rt)
927 restore_rt = old_ctx->current_rt;
928 context = context_acquire(device, required_rt);
931 if (!context->valid)
933 context_release(context);
934 WARN("Invalid context, skipping blit.\n");
935 return;
938 gl_info = context->gl_info;
940 if (src_location == WINED3D_LOCATION_DRAWABLE)
942 TRACE("Source surface %p is onscreen.\n", src_surface);
943 buffer = surface_get_gl_buffer(src_surface);
944 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
946 else
948 TRACE("Source surface %p is offscreen.\n", src_surface);
949 buffer = GL_COLOR_ATTACHMENT0;
952 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
953 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
954 checkGLcall("glReadBuffer()");
955 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
957 if (dst_location == WINED3D_LOCATION_DRAWABLE)
959 TRACE("Destination surface %p is onscreen.\n", dst_surface);
960 buffer = surface_get_gl_buffer(dst_surface);
961 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
963 else
965 TRACE("Destination surface %p is offscreen.\n", dst_surface);
966 buffer = GL_COLOR_ATTACHMENT0;
969 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
970 context_set_draw_buffer(context, buffer);
971 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
972 context_invalidate_state(context, STATE_FRAMEBUFFER);
974 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
975 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
976 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
977 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
978 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
980 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
981 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
983 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
984 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
985 checkGLcall("glBlitFramebuffer()");
987 if (wined3d_settings.strict_draw_ordering
988 || (dst_location == WINED3D_LOCATION_DRAWABLE
989 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
990 gl_info->gl_ops.gl.p_glFlush();
992 if (restore_rt)
993 context_restore(context, restore_rt);
996 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
997 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
998 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
1000 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1001 return FALSE;
1003 /* Source and/or destination need to be on the GL side */
1004 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
1005 return FALSE;
1007 switch (blit_op)
1009 case WINED3D_BLIT_OP_COLOR_BLIT:
1010 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
1011 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1012 return FALSE;
1013 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
1014 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1015 return FALSE;
1016 if (!(src_format->id == dst_format->id
1017 || (is_identity_fixup(src_format->color_fixup)
1018 && is_identity_fixup(dst_format->color_fixup))))
1019 return FALSE;
1020 break;
1022 case WINED3D_BLIT_OP_DEPTH_BLIT:
1023 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1024 return FALSE;
1025 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1026 return FALSE;
1027 /* Accept pure swizzle fixups for depth formats. In general we
1028 * ignore the stencil component (if present) at the moment and the
1029 * swizzle is not relevant with just the depth component. */
1030 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
1031 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
1032 return FALSE;
1033 break;
1035 default:
1036 return FALSE;
1039 return TRUE;
1042 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1044 const struct wined3d_format *format = surface->resource.format;
1046 switch (format->id)
1048 case WINED3DFMT_S1_UINT_D15_UNORM:
1049 *float_depth = depth / (float)0x00007fff;
1050 break;
1052 case WINED3DFMT_D16_UNORM:
1053 *float_depth = depth / (float)0x0000ffff;
1054 break;
1056 case WINED3DFMT_D24_UNORM_S8_UINT:
1057 case WINED3DFMT_X8D24_UNORM:
1058 *float_depth = depth / (float)0x00ffffff;
1059 break;
1061 case WINED3DFMT_D32_UNORM:
1062 *float_depth = depth / (float)0xffffffff;
1063 break;
1065 default:
1066 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1067 return FALSE;
1070 return TRUE;
1073 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1075 const struct wined3d_resource *resource = &surface->container->resource;
1076 struct wined3d_device *device = resource->device;
1077 const struct blit_shader *blitter;
1079 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, WINED3D_BLIT_OP_DEPTH_FILL,
1080 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1081 if (!blitter)
1083 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1084 return WINED3DERR_INVALIDCALL;
1087 return blitter->depth_fill(device, surface, rect, depth);
1090 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1091 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1093 struct wined3d_device *device = src_surface->resource.device;
1095 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1096 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1097 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1098 return WINED3DERR_INVALIDCALL;
1100 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1102 surface_modify_ds_location(dst_surface, dst_location,
1103 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1105 return WINED3D_OK;
1108 /* Context activation is done by the caller. */
1109 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1111 GL_EXTCALL(glDeleteBuffers(1, &surface->pbo));
1112 checkGLcall("glDeleteBuffers(1, &surface->pbo)");
1114 surface->pbo = 0;
1115 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1118 static ULONG surface_resource_incref(struct wined3d_resource *resource)
1120 return wined3d_surface_incref(surface_from_resource(resource));
1123 static ULONG surface_resource_decref(struct wined3d_resource *resource)
1125 return wined3d_surface_decref(surface_from_resource(resource));
1128 static void surface_unload(struct wined3d_resource *resource)
1130 struct wined3d_surface *surface = surface_from_resource(resource);
1131 struct wined3d_renderbuffer_entry *entry, *entry2;
1132 struct wined3d_device *device = resource->device;
1133 const struct wined3d_gl_info *gl_info;
1134 struct wined3d_context *context;
1136 TRACE("surface %p.\n", surface);
1138 context = context_acquire(device, NULL);
1139 gl_info = context->gl_info;
1141 if (resource->pool == WINED3D_POOL_DEFAULT)
1143 /* Default pool resources are supposed to be destroyed before Reset is called.
1144 * Implicit resources stay however. So this means we have an implicit render target
1145 * or depth stencil. The content may be destroyed, but we still have to tear down
1146 * opengl resources, so we cannot leave early.
1148 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1149 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1150 * or the depth stencil into an FBO the texture or render buffer will be removed
1151 * and all flags get lost */
1152 if (resource->usage & WINED3DUSAGE_DEPTHSTENCIL)
1154 surface_validate_location(surface, WINED3D_LOCATION_DISCARDED);
1155 surface_invalidate_location(surface, ~WINED3D_LOCATION_DISCARDED);
1157 else
1159 surface_prepare_system_memory(surface);
1160 memset(surface->resource.heap_memory, 0, surface->resource.size);
1161 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1162 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1165 else
1167 surface_prepare_map_memory(surface);
1168 surface_load_location(surface, context, surface->resource.map_binding);
1169 surface_invalidate_location(surface, ~surface->resource.map_binding);
1172 /* Destroy PBOs, but load them into real sysmem before */
1173 if (surface->pbo)
1174 surface_remove_pbo(surface, gl_info);
1176 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1177 * all application-created targets the application has to release the surface
1178 * before calling _Reset
1180 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1182 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1183 list_remove(&entry->entry);
1184 HeapFree(GetProcessHeap(), 0, entry);
1186 list_init(&surface->renderbuffers);
1187 surface->current_renderbuffer = NULL;
1189 if (surface->rb_multisample)
1191 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1192 surface->rb_multisample = 0;
1194 if (surface->rb_resolved)
1196 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1197 surface->rb_resolved = 0;
1200 context_release(context);
1202 resource_unload(resource);
1205 static HRESULT surface_resource_sub_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx,
1206 struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
1208 ERR("Not supported on sub-resources.\n");
1209 return WINED3DERR_INVALIDCALL;
1212 static HRESULT surface_resource_sub_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx)
1214 ERR("Not supported on sub-resources.\n");
1215 return WINED3DERR_INVALIDCALL;
1218 static const struct wined3d_resource_ops surface_resource_ops =
1220 surface_resource_incref,
1221 surface_resource_decref,
1222 surface_unload,
1223 surface_resource_sub_resource_map,
1224 surface_resource_sub_resource_unmap,
1227 static const struct wined3d_surface_ops surface_ops =
1229 surface_private_setup,
1230 surface_unmap,
1233 /*****************************************************************************
1234 * Initializes the GDI surface, aka creates the DIB section we render to
1235 * The DIB section creation is done by calling GetDC, which will create the
1236 * section and releasing the dc to allow the app to use it. The dib section
1237 * will stay until the surface is released
1239 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1240 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1241 * avoid confusion in the shared surface code.
1243 * Returns:
1244 * WINED3D_OK on success
1245 * The return values of called methods on failure
1247 *****************************************************************************/
1248 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1250 HRESULT hr;
1252 TRACE("surface %p.\n", surface);
1254 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1256 ERR("Overlays not yet supported by GDI surfaces.\n");
1257 return WINED3DERR_INVALIDCALL;
1260 /* Sysmem textures have memory already allocated - release it,
1261 * this avoids an unnecessary memcpy. */
1262 hr = surface_create_dib_section(surface);
1263 if (FAILED(hr))
1264 return hr;
1265 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1267 /* We don't mind the nonpow2 stuff in GDI. */
1268 surface->pow2Width = surface->resource.width;
1269 surface->pow2Height = surface->resource.height;
1271 return WINED3D_OK;
1274 static void gdi_surface_unmap(struct wined3d_surface *surface)
1276 TRACE("surface %p.\n", surface);
1278 /* Tell the swapchain to update the screen. */
1279 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
1280 x11_copy_to_screen(surface->container->swapchain, &surface->lockedRect);
1282 memset(&surface->lockedRect, 0, sizeof(RECT));
1285 static const struct wined3d_surface_ops gdi_surface_ops =
1287 gdi_surface_private_setup,
1288 gdi_surface_unmap,
1291 /* This call just downloads data, the caller is responsible for binding the
1292 * correct texture. */
1293 /* Context activation is done by the caller. */
1294 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1295 DWORD dst_location)
1297 const struct wined3d_format *format = surface->resource.format;
1298 struct wined3d_bo_address data;
1300 /* Only support read back of converted P8 surfaces. */
1301 if (surface->container->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1303 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1304 return;
1307 surface_get_memory(surface, &data, dst_location);
1309 if (surface->container->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
1311 TRACE("(%p) : Calling glGetCompressedTexImage level %d, format %#x, type %#x, data %p.\n",
1312 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1314 if (data.buffer_object)
1316 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1317 checkGLcall("glBindBuffer");
1318 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, NULL));
1319 checkGLcall("glGetCompressedTexImage");
1320 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1321 checkGLcall("glBindBuffer");
1323 else
1325 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target,
1326 surface->texture_level, data.addr));
1327 checkGLcall("glGetCompressedTexImage");
1330 else
1332 void *mem;
1333 GLenum gl_format = format->glFormat;
1334 GLenum gl_type = format->glType;
1335 int src_pitch = 0;
1336 int dst_pitch = 0;
1338 if (surface->flags & SFLAG_NONPOW2)
1340 unsigned char alignment = surface->resource.device->surface_alignment;
1341 src_pitch = format->byte_count * surface->pow2Width;
1342 dst_pitch = wined3d_surface_get_pitch(surface);
1343 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1344 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1346 else
1348 mem = data.addr;
1351 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1352 surface, surface->texture_level, gl_format, gl_type, mem);
1354 if (data.buffer_object)
1356 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1357 checkGLcall("glBindBuffer");
1359 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1360 gl_format, gl_type, NULL);
1361 checkGLcall("glGetTexImage");
1363 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1364 checkGLcall("glBindBuffer");
1366 else
1368 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1369 gl_format, gl_type, mem);
1370 checkGLcall("glGetTexImage");
1373 if (surface->flags & SFLAG_NONPOW2)
1375 const BYTE *src_data;
1376 BYTE *dst_data;
1377 UINT y;
1379 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1380 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1381 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1383 * We're doing this...
1385 * instead of boxing the texture :
1386 * |<-texture width ->| -->pow2width| /\
1387 * |111111111111111111| | |
1388 * |222 Texture 222222| boxed empty | texture height
1389 * |3333 Data 33333333| | |
1390 * |444444444444444444| | \/
1391 * ----------------------------------- |
1392 * | boxed empty | boxed empty | pow2height
1393 * | | | \/
1394 * -----------------------------------
1397 * we're repacking the data to the expected texture width
1399 * |<-texture width ->| -->pow2width| /\
1400 * |111111111111111111222222222222222| |
1401 * |222333333333333333333444444444444| texture height
1402 * |444444 | |
1403 * | | \/
1404 * | | |
1405 * | empty | pow2height
1406 * | | \/
1407 * -----------------------------------
1409 * == is the same as
1411 * |<-texture width ->| /\
1412 * |111111111111111111|
1413 * |222222222222222222|texture height
1414 * |333333333333333333|
1415 * |444444444444444444| \/
1416 * --------------------
1418 * This also means that any references to surface memory should work with the data as if it were a
1419 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1421 * internally the texture is still stored in a boxed format so any references to textureName will
1422 * get a boxed texture with width pow2width and not a texture of width resource.width.
1424 * Performance should not be an issue, because applications normally do not lock the surfaces when
1425 * rendering. If an app does, the WINED3D_TEXTURE_DYNAMIC_MAP flag will kick in and the memory copy
1426 * won't be released, and doesn't have to be re-read. */
1427 src_data = mem;
1428 dst_data = data.addr;
1429 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1430 for (y = 0; y < surface->resource.height; ++y)
1432 memcpy(dst_data, src_data, dst_pitch);
1433 src_data += src_pitch;
1434 dst_data += dst_pitch;
1437 HeapFree(GetProcessHeap(), 0, mem);
1442 /* This call just uploads data, the caller is responsible for binding the
1443 * correct texture. */
1444 /* Context activation is done by the caller. */
1445 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1446 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1447 BOOL srgb, const struct wined3d_const_bo_address *data)
1449 UINT update_w = src_rect->right - src_rect->left;
1450 UINT update_h = src_rect->bottom - src_rect->top;
1452 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1453 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1454 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1456 if (surface->resource.map_count)
1458 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
1459 surface->container->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
1462 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
1464 update_h *= format->height_scale.numerator;
1465 update_h /= format->height_scale.denominator;
1468 if (data->buffer_object)
1470 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
1471 checkGLcall("glBindBuffer");
1474 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
1476 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1477 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1478 const BYTE *addr = data->addr;
1479 GLenum internal;
1481 addr += (src_rect->top / format->block_height) * src_pitch;
1482 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1484 if (srgb)
1485 internal = format->glGammaInternal;
1486 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1487 && wined3d_resource_is_offscreen(&surface->container->resource))
1488 internal = format->rtInternal;
1489 else
1490 internal = format->glInternal;
1492 TRACE("glCompressedTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, "
1493 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1494 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1496 if (row_length == src_pitch)
1498 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1499 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1501 else
1503 UINT row, y;
1505 /* glCompressedTexSubImage2D() ignores pixel store state, so we
1506 * can't use the unpack row length like for glTexSubImage2D. */
1507 for (row = 0, y = dst_point->y; row < row_count; ++row)
1509 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1510 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1511 y += format->block_height;
1512 addr += src_pitch;
1515 checkGLcall("glCompressedTexSubImage2D");
1517 else
1519 const BYTE *addr = data->addr;
1521 addr += src_rect->top * src_pitch;
1522 addr += src_rect->left * format->byte_count;
1524 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1525 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1526 update_w, update_h, format->glFormat, format->glType, addr);
1528 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1529 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1530 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1531 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1532 checkGLcall("glTexSubImage2D");
1535 if (data->buffer_object)
1537 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1538 checkGLcall("glBindBuffer");
1541 if (wined3d_settings.strict_draw_ordering)
1542 gl_info->gl_ops.gl.p_glFlush();
1544 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1546 struct wined3d_device *device = surface->resource.device;
1547 unsigned int i;
1549 for (i = 0; i < device->context_count; ++i)
1551 context_surface_update(device->contexts[i], surface);
1556 static BOOL surface_check_block_align(struct wined3d_surface *surface, const struct wined3d_box *box)
1558 UINT width_mask, height_mask;
1560 if (!box->left && !box->top
1561 && box->right == surface->resource.width
1562 && box->bottom == surface->resource.height)
1563 return TRUE;
1565 /* This assumes power of two block sizes, but NPOT block sizes would be
1566 * silly anyway. */
1567 width_mask = surface->resource.format->block_width - 1;
1568 height_mask = surface->resource.format->block_height - 1;
1570 if (!(box->left & width_mask) && !(box->top & height_mask)
1571 && !(box->right & width_mask) && !(box->bottom & height_mask))
1572 return TRUE;
1574 return FALSE;
1577 static BOOL surface_check_block_align_rect(struct wined3d_surface *surface, const RECT *rect)
1579 struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
1581 return surface_check_block_align(surface, &box);
1584 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1585 struct wined3d_surface *src_surface, const RECT *src_rect)
1587 const struct wined3d_format *src_format;
1588 const struct wined3d_format *dst_format;
1589 unsigned int src_fmt_flags, dst_fmt_flags;
1590 const struct wined3d_gl_info *gl_info;
1591 struct wined3d_context *context;
1592 struct wined3d_bo_address data;
1593 UINT update_w, update_h;
1594 UINT dst_w, dst_h;
1595 RECT r, dst_rect;
1596 UINT src_pitch;
1597 POINT p;
1599 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1600 dst_surface, wine_dbgstr_point(dst_point),
1601 src_surface, wine_dbgstr_rect(src_rect));
1603 src_format = src_surface->resource.format;
1604 dst_format = dst_surface->resource.format;
1605 src_fmt_flags = src_surface->container->resource.format_flags;
1606 dst_fmt_flags = dst_surface->container->resource.format_flags;
1608 if (src_format->id != dst_format->id)
1610 WARN("Source and destination surfaces should have the same format.\n");
1611 return WINED3DERR_INVALIDCALL;
1614 if (!dst_point)
1616 p.x = 0;
1617 p.y = 0;
1618 dst_point = &p;
1620 else if (dst_point->x < 0 || dst_point->y < 0)
1622 WARN("Invalid destination point.\n");
1623 return WINED3DERR_INVALIDCALL;
1626 if (!src_rect)
1628 r.left = 0;
1629 r.top = 0;
1630 r.right = src_surface->resource.width;
1631 r.bottom = src_surface->resource.height;
1632 src_rect = &r;
1634 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1635 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1637 WARN("Invalid source rectangle.\n");
1638 return WINED3DERR_INVALIDCALL;
1641 dst_w = dst_surface->resource.width;
1642 dst_h = dst_surface->resource.height;
1644 update_w = src_rect->right - src_rect->left;
1645 update_h = src_rect->bottom - src_rect->top;
1647 if (update_w > dst_w || dst_point->x > dst_w - update_w
1648 || update_h > dst_h || dst_point->y > dst_h - update_h)
1650 WARN("Destination out of bounds.\n");
1651 return WINED3DERR_INVALIDCALL;
1654 if ((src_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(src_surface, src_rect))
1656 WARN("Source rectangle not block-aligned.\n");
1657 return WINED3DERR_INVALIDCALL;
1660 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1661 if ((dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(dst_surface, &dst_rect))
1663 WARN("Destination rectangle not block-aligned.\n");
1664 return WINED3DERR_INVALIDCALL;
1667 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1668 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_surface->container, FALSE))
1669 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1671 context = context_acquire(dst_surface->resource.device, NULL);
1672 gl_info = context->gl_info;
1674 /* Only load the surface for partial updates. For newly allocated texture
1675 * the texture wouldn't be the current location, and we'd upload zeroes
1676 * just to overwrite them again. */
1677 if (update_w == dst_w && update_h == dst_h)
1678 wined3d_texture_prepare_texture(dst_surface->container, context, FALSE);
1679 else
1680 surface_load_location(dst_surface, context, WINED3D_LOCATION_TEXTURE_RGB);
1681 wined3d_texture_bind_and_dirtify(dst_surface->container, context, FALSE);
1683 surface_get_memory(src_surface, &data, src_surface->locations);
1684 src_pitch = wined3d_surface_get_pitch(src_surface);
1686 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1687 src_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1689 context_release(context);
1691 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1692 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1694 return WINED3D_OK;
1697 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1698 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1699 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1700 /* Context activation is done by the caller. */
1701 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1703 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1704 struct wined3d_renderbuffer_entry *entry;
1705 GLuint renderbuffer = 0;
1706 unsigned int src_width, src_height;
1707 unsigned int width, height;
1709 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1711 width = rt->pow2Width;
1712 height = rt->pow2Height;
1714 else
1716 width = surface->pow2Width;
1717 height = surface->pow2Height;
1720 src_width = surface->pow2Width;
1721 src_height = surface->pow2Height;
1723 /* A depth stencil smaller than the render target is not valid */
1724 if (width > src_width || height > src_height) return;
1726 /* Remove any renderbuffer set if the sizes match */
1727 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1728 || (width == src_width && height == src_height))
1730 surface->current_renderbuffer = NULL;
1731 return;
1734 /* Look if we've already got a renderbuffer of the correct dimensions */
1735 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1737 if (entry->width == width && entry->height == height)
1739 renderbuffer = entry->id;
1740 surface->current_renderbuffer = entry;
1741 break;
1745 if (!renderbuffer)
1747 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1748 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1749 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1750 surface->resource.format->glInternal, width, height);
1752 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1753 entry->width = width;
1754 entry->height = height;
1755 entry->id = renderbuffer;
1756 list_add_head(&surface->renderbuffers, &entry->entry);
1758 surface->current_renderbuffer = entry;
1761 checkGLcall("set_compatible_renderbuffer");
1764 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1766 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1768 TRACE("surface %p.\n", surface);
1770 if (!swapchain)
1772 ERR("Surface %p is not on a swapchain.\n", surface);
1773 return GL_NONE;
1776 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
1778 if (swapchain->render_to_fbo)
1780 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1781 return GL_COLOR_ATTACHMENT0;
1783 TRACE("Returning GL_BACK\n");
1784 return GL_BACK;
1786 else if (surface->container == swapchain->front_buffer)
1788 TRACE("Returning GL_FRONT\n");
1789 return GL_FRONT;
1792 FIXME("Higher back buffer, returning GL_BACK\n");
1793 return GL_BACK;
1796 /* Context activation is done by the caller. */
1797 void surface_load(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
1799 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1801 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1803 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
1804 ERR("Not supported on scratch surfaces.\n");
1806 if (surface->locations & location)
1808 TRACE("surface is already in texture\n");
1809 return;
1811 TRACE("Reloading because surface is dirty.\n");
1813 surface_load_location(surface, context, location);
1814 surface_evict_sysmem(surface);
1817 /* See also float_16_to_32() in wined3d_private.h */
1818 static inline unsigned short float_32_to_16(const float *in)
1820 int exp = 0;
1821 float tmp = fabsf(*in);
1822 unsigned int mantissa;
1823 unsigned short ret;
1825 /* Deal with special numbers */
1826 if (*in == 0.0f)
1827 return 0x0000;
1828 if (isnan(*in))
1829 return 0x7c01;
1830 if (isinf(*in))
1831 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1833 if (tmp < (float)(1u << 10))
1837 tmp = tmp * 2.0f;
1838 exp--;
1839 } while (tmp < (float)(1u << 10));
1841 else if (tmp >= (float)(1u << 11))
1845 tmp /= 2.0f;
1846 exp++;
1847 } while (tmp >= (float)(1u << 11));
1850 mantissa = (unsigned int)tmp;
1851 if (tmp - mantissa >= 0.5f)
1852 ++mantissa; /* Round to nearest, away from zero. */
1854 exp += 10; /* Normalize the mantissa. */
1855 exp += 15; /* Exponent is encoded with excess 15. */
1857 if (exp > 30) /* too big */
1859 ret = 0x7c00; /* INF */
1861 else if (exp <= 0)
1863 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1864 while (exp <= 0)
1866 mantissa = mantissa >> 1;
1867 ++exp;
1869 ret = mantissa & 0x3ff;
1871 else
1873 ret = (exp << 10) | (mantissa & 0x3ff);
1876 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1877 return ret;
1880 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
1882 TRACE("surface %p, container %p.\n", surface, surface->container);
1884 return wined3d_texture_incref(surface->container);
1887 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
1889 TRACE("surface %p, container %p.\n", surface, surface->container);
1891 return wined3d_texture_decref(surface->container);
1894 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
1896 TRACE("surface %p.\n", surface);
1898 if (!surface->resource.device->d3d_initialized)
1900 ERR("D3D not initialized.\n");
1901 return;
1904 wined3d_texture_preload(surface->container);
1907 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
1909 TRACE("surface %p.\n", surface);
1911 return surface->resource.parent;
1914 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
1916 TRACE("surface %p.\n", surface);
1918 return &surface->resource;
1921 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
1923 unsigned int alignment;
1924 DWORD pitch;
1926 TRACE("surface %p.\n", surface);
1928 if (surface->pitch)
1929 return surface->pitch;
1931 alignment = surface->resource.device->surface_alignment;
1932 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
1933 pitch = (pitch + alignment - 1) & ~(alignment - 1);
1935 TRACE("Returning %u.\n", pitch);
1937 return pitch;
1940 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
1942 LONG w, h;
1944 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
1946 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1948 WARN("Not an overlay surface.\n");
1949 return WINEDDERR_NOTAOVERLAYSURFACE;
1952 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
1953 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
1954 surface->overlay_destrect.left = x;
1955 surface->overlay_destrect.top = y;
1956 surface->overlay_destrect.right = x + w;
1957 surface->overlay_destrect.bottom = y + h;
1959 return WINED3D_OK;
1962 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
1964 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
1966 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1968 TRACE("Not an overlay surface.\n");
1969 return WINEDDERR_NOTAOVERLAYSURFACE;
1972 if (!surface->overlay_dest)
1974 TRACE("Overlay not visible.\n");
1975 *x = 0;
1976 *y = 0;
1977 return WINEDDERR_OVERLAYNOTVISIBLE;
1980 *x = surface->overlay_destrect.left;
1981 *y = surface->overlay_destrect.top;
1983 TRACE("Returning position %d, %d.\n", *x, *y);
1985 return WINED3D_OK;
1988 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
1989 DWORD flags, struct wined3d_surface *ref)
1991 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
1993 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1995 TRACE("Not an overlay surface.\n");
1996 return WINEDDERR_NOTAOVERLAYSURFACE;
1999 return WINED3D_OK;
2002 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2003 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2005 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2006 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2008 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2010 WARN("Not an overlay surface.\n");
2011 return WINEDDERR_NOTAOVERLAYSURFACE;
2013 else if (!dst_surface)
2015 WARN("Dest surface is NULL.\n");
2016 return WINED3DERR_INVALIDCALL;
2019 surface_get_rect(surface, src_rect, &surface->overlay_srcrect);
2020 surface_get_rect(dst_surface, dst_rect, &surface->overlay_destrect);
2022 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2024 surface->overlay_dest = NULL;
2025 list_remove(&surface->overlay_entry);
2028 if (flags & WINEDDOVER_SHOW)
2030 if (surface->overlay_dest != dst_surface)
2032 surface->overlay_dest = dst_surface;
2033 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2036 else if (flags & WINEDDOVER_HIDE)
2038 /* tests show that the rectangles are erased on hide */
2039 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2040 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2041 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2042 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2043 surface->overlay_dest = NULL;
2046 return WINED3D_OK;
2049 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface,
2050 const struct wined3d_gl_info *gl_info, void *mem, unsigned int pitch)
2052 struct wined3d_resource *texture_resource = &surface->container->resource;
2053 unsigned int width, height;
2054 BOOL create_dib = FALSE;
2055 DWORD valid_location = 0;
2056 HRESULT hr;
2058 if (surface->flags & SFLAG_DIBSECTION)
2060 DeleteDC(surface->hDC);
2061 DeleteObject(surface->dib.DIBsection);
2062 surface->dib.bitmap_data = NULL;
2063 surface->flags &= ~SFLAG_DIBSECTION;
2064 create_dib = TRUE;
2067 surface->locations = 0;
2068 wined3d_resource_free_sysmem(&surface->resource);
2070 width = texture_resource->width;
2071 height = texture_resource->height;
2072 surface->resource.width = width;
2073 surface->resource.height = height;
2074 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2075 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2077 surface->pow2Width = width;
2078 surface->pow2Height = height;
2080 else
2082 surface->pow2Width = surface->pow2Height = 1;
2083 while (surface->pow2Width < width)
2084 surface->pow2Width <<= 1;
2085 while (surface->pow2Height < height)
2086 surface->pow2Height <<= 1;
2089 if (surface->pow2Width != width || surface->pow2Height != height)
2090 surface->flags |= SFLAG_NONPOW2;
2091 else
2092 surface->flags &= ~SFLAG_NONPOW2;
2094 if ((surface->user_memory = mem))
2096 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
2097 valid_location = WINED3D_LOCATION_USER_MEMORY;
2099 surface->pitch = pitch;
2100 surface->resource.format = texture_resource->format;
2101 surface->resource.multisample_type = texture_resource->multisample_type;
2102 surface->resource.multisample_quality = texture_resource->multisample_quality;
2103 if (surface->pitch)
2105 surface->resource.size = height * surface->pitch;
2107 else
2109 /* User memory surfaces don't have the regular surface alignment. */
2110 surface->resource.size = wined3d_format_calculate_size(texture_resource->format,
2111 1, width, height, 1);
2112 surface->pitch = wined3d_format_calculate_pitch(texture_resource->format, width);
2115 /* The format might be changed to a format that needs conversion.
2116 * If the surface didn't use PBOs previously but could now, don't
2117 * change it - whatever made us not use PBOs might come back, e.g.
2118 * color keys. */
2119 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2120 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2122 if (create_dib)
2124 if (FAILED(hr = surface_create_dib_section(surface)))
2126 ERR("Failed to create dib section, hr %#x.\n", hr);
2127 return hr;
2129 if (!valid_location)
2130 valid_location = WINED3D_LOCATION_DIB;
2133 if (!valid_location)
2135 surface_prepare_system_memory(surface);
2136 valid_location = WINED3D_LOCATION_SYSMEM;
2139 surface_validate_location(surface, valid_location);
2141 return WINED3D_OK;
2144 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2145 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2147 unsigned short *dst_s;
2148 const float *src_f;
2149 unsigned int x, y;
2151 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2153 for (y = 0; y < h; ++y)
2155 src_f = (const float *)(src + y * pitch_in);
2156 dst_s = (unsigned short *) (dst + y * pitch_out);
2157 for (x = 0; x < w; ++x)
2159 dst_s[x] = float_32_to_16(src_f + x);
2164 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2165 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2167 static const unsigned char convert_5to8[] =
2169 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2170 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2171 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2172 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2174 static const unsigned char convert_6to8[] =
2176 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2177 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2178 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2179 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2180 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2181 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2182 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2183 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2185 unsigned int x, y;
2187 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2189 for (y = 0; y < h; ++y)
2191 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2192 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2193 for (x = 0; x < w; ++x)
2195 WORD pixel = src_line[x];
2196 dst_line[x] = 0xff000000u
2197 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
2198 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
2199 | convert_5to8[(pixel & 0x001fu)];
2204 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2205 * in both cases we're just setting the X / Alpha channel to 0xff. */
2206 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2207 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2209 unsigned int x, y;
2211 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2213 for (y = 0; y < h; ++y)
2215 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2216 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2218 for (x = 0; x < w; ++x)
2220 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2225 static inline BYTE cliptobyte(int x)
2227 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2230 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2231 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2233 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2234 unsigned int x, y;
2236 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2238 for (y = 0; y < h; ++y)
2240 const BYTE *src_line = src + y * pitch_in;
2241 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2242 for (x = 0; x < w; ++x)
2244 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2245 * C = Y - 16; D = U - 128; E = V - 128;
2246 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2247 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2248 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2249 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2250 * U and V are shared between the pixels. */
2251 if (!(x & 1)) /* For every even pixel, read new U and V. */
2253 d = (int) src_line[1] - 128;
2254 e = (int) src_line[3] - 128;
2255 r2 = 409 * e + 128;
2256 g2 = - 100 * d - 208 * e + 128;
2257 b2 = 516 * d + 128;
2259 c2 = 298 * ((int) src_line[0] - 16);
2260 dst_line[x] = 0xff000000
2261 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2262 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2263 | cliptobyte((c2 + b2) >> 8); /* blue */
2264 /* Scale RGB values to 0..255 range,
2265 * then clip them if still not in range (may be negative),
2266 * then shift them within DWORD if necessary. */
2267 src_line += 2;
2272 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2273 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2275 unsigned int x, y;
2276 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2278 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2280 for (y = 0; y < h; ++y)
2282 const BYTE *src_line = src + y * pitch_in;
2283 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2284 for (x = 0; x < w; ++x)
2286 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2287 * C = Y - 16; D = U - 128; E = V - 128;
2288 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2289 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2290 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2291 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2292 * U and V are shared between the pixels. */
2293 if (!(x & 1)) /* For every even pixel, read new U and V. */
2295 d = (int) src_line[1] - 128;
2296 e = (int) src_line[3] - 128;
2297 r2 = 409 * e + 128;
2298 g2 = - 100 * d - 208 * e + 128;
2299 b2 = 516 * d + 128;
2301 c2 = 298 * ((int) src_line[0] - 16);
2302 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2303 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2304 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2305 /* Scale RGB values to 0..255 range,
2306 * then clip them if still not in range (may be negative),
2307 * then shift them within DWORD if necessary. */
2308 src_line += 2;
2313 struct d3dfmt_converter_desc
2315 enum wined3d_format_id from, to;
2316 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2319 static const struct d3dfmt_converter_desc converters[] =
2321 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2322 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2323 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2324 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2325 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2326 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2329 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2330 enum wined3d_format_id to)
2332 unsigned int i;
2334 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2336 if (converters[i].from == from && converters[i].to == to)
2337 return &converters[i];
2340 return NULL;
2343 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2345 struct wined3d_map_desc src_map, dst_map;
2346 const struct d3dfmt_converter_desc *conv;
2347 struct wined3d_texture *ret = NULL;
2348 struct wined3d_resource_desc desc;
2349 struct wined3d_surface *dst;
2351 conv = find_converter(source->resource.format->id, to_fmt);
2352 if (!conv)
2354 FIXME("Cannot find a conversion function from format %s to %s.\n",
2355 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2356 return NULL;
2359 /* FIXME: Multisampled conversion? */
2360 wined3d_resource_get_desc(&source->resource, &desc);
2361 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2362 desc.format = to_fmt;
2363 desc.usage = 0;
2364 desc.pool = WINED3D_POOL_SCRATCH;
2365 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2366 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, NULL, &wined3d_null_parent_ops, &ret)))
2368 ERR("Failed to create a destination surface for conversion.\n");
2369 return NULL;
2371 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2373 memset(&src_map, 0, sizeof(src_map));
2374 memset(&dst_map, 0, sizeof(dst_map));
2376 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2378 ERR("Failed to lock the source surface.\n");
2379 wined3d_texture_decref(ret);
2380 return NULL;
2382 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2384 ERR("Failed to lock the destination surface.\n");
2385 wined3d_surface_unmap(source);
2386 wined3d_texture_decref(ret);
2387 return NULL;
2390 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2391 source->resource.width, source->resource.height);
2393 wined3d_surface_unmap(dst);
2394 wined3d_surface_unmap(source);
2396 return ret;
2399 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2400 unsigned int bpp, UINT pitch, DWORD color)
2402 BYTE *first;
2403 unsigned int x, y;
2405 /* Do first row */
2407 #define COLORFILL_ROW(type) \
2408 do { \
2409 type *d = (type *)buf; \
2410 for (x = 0; x < width; ++x) \
2411 d[x] = (type)color; \
2412 } while(0)
2414 switch (bpp)
2416 case 1:
2417 COLORFILL_ROW(BYTE);
2418 break;
2420 case 2:
2421 COLORFILL_ROW(WORD);
2422 break;
2424 case 3:
2426 BYTE *d = buf;
2427 for (x = 0; x < width; ++x, d += 3)
2429 d[0] = (color ) & 0xff;
2430 d[1] = (color >> 8) & 0xff;
2431 d[2] = (color >> 16) & 0xff;
2433 break;
2435 case 4:
2436 COLORFILL_ROW(DWORD);
2437 break;
2439 default:
2440 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2441 return WINED3DERR_NOTAVAILABLE;
2444 #undef COLORFILL_ROW
2446 /* Now copy first row. */
2447 first = buf;
2448 for (y = 1; y < height; ++y)
2450 buf += pitch;
2451 memcpy(buf, first, width * bpp);
2454 return WINED3D_OK;
2457 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2459 return surface_from_resource(resource);
2462 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2464 TRACE("surface %p.\n", surface);
2466 if (!surface->resource.map_count)
2468 WARN("Trying to unmap unmapped surface.\n");
2469 return WINEDDERR_NOTLOCKED;
2471 --surface->resource.map_count;
2473 surface->surface_ops->surface_unmap(surface);
2475 return WINED3D_OK;
2478 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2479 struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
2481 const struct wined3d_format *format = surface->resource.format;
2482 unsigned int fmt_flags = surface->container->resource.format_flags;
2483 struct wined3d_device *device = surface->resource.device;
2484 struct wined3d_context *context;
2485 const struct wined3d_gl_info *gl_info;
2486 BYTE *base_memory;
2488 TRACE("surface %p, map_desc %p, box %p, flags %#x.\n",
2489 surface, map_desc, box, flags);
2491 if (surface->resource.map_count)
2493 WARN("Surface is already mapped.\n");
2494 return WINED3DERR_INVALIDCALL;
2497 if ((fmt_flags & WINED3DFMT_FLAG_BLOCKS) && box
2498 && !surface_check_block_align(surface, box))
2500 WARN("Map rect %p is misaligned for %ux%u blocks.\n",
2501 box, format->block_width, format->block_height);
2503 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2504 return WINED3DERR_INVALIDCALL;
2507 ++surface->resource.map_count;
2509 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2510 WARN("Trying to lock unlockable surface.\n");
2512 /* Performance optimization: Count how often a surface is mapped, if it is
2513 * mapped regularly do not throw away the system memory copy. This avoids
2514 * the need to download the surface from OpenGL all the time. The surface
2515 * is still downloaded if the OpenGL texture is changed. Note that this
2516 * only really makes sense for managed textures.*/
2517 if (!(surface->container->flags & WINED3D_TEXTURE_DYNAMIC_MAP)
2518 && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2520 if (++surface->lockCount > MAXLOCKCOUNT)
2522 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2523 surface->container->flags |= WINED3D_TEXTURE_DYNAMIC_MAP;
2527 surface_prepare_map_memory(surface);
2528 if (flags & WINED3D_MAP_DISCARD)
2530 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2531 wined3d_debug_location(surface->resource.map_binding));
2532 surface_validate_location(surface, surface->resource.map_binding);
2534 else
2536 struct wined3d_context *context = NULL;
2538 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2539 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2541 if (surface->resource.device->d3d_initialized)
2542 context = context_acquire(surface->resource.device, NULL);
2543 surface_load_location(surface, context, surface->resource.map_binding);
2544 if (context)
2545 context_release(context);
2548 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2549 surface_invalidate_location(surface, ~surface->resource.map_binding);
2551 switch (surface->resource.map_binding)
2553 case WINED3D_LOCATION_SYSMEM:
2554 base_memory = surface->resource.heap_memory;
2555 break;
2557 case WINED3D_LOCATION_USER_MEMORY:
2558 base_memory = surface->user_memory;
2559 break;
2561 case WINED3D_LOCATION_DIB:
2562 base_memory = surface->dib.bitmap_data;
2563 break;
2565 case WINED3D_LOCATION_BUFFER:
2566 context = context_acquire(device, NULL);
2567 gl_info = context->gl_info;
2569 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
2570 base_memory = GL_EXTCALL(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE));
2571 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
2572 checkGLcall("map PBO");
2574 context_release(context);
2575 break;
2577 default:
2578 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2579 base_memory = NULL;
2582 if (fmt_flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2583 map_desc->row_pitch = surface->resource.width * format->byte_count;
2584 else
2585 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2586 map_desc->slice_pitch = surface->resource.height * map_desc->row_pitch;
2588 if (!box)
2590 map_desc->data = base_memory;
2591 surface->lockedRect.left = 0;
2592 surface->lockedRect.top = 0;
2593 surface->lockedRect.right = surface->resource.width;
2594 surface->lockedRect.bottom = surface->resource.height;
2596 else
2598 if ((fmt_flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2600 /* Compressed textures are block based, so calculate the offset of
2601 * the block that contains the top-left pixel of the locked rectangle. */
2602 map_desc->data = base_memory
2603 + ((box->top / format->block_height) * map_desc->row_pitch)
2604 + ((box->left / format->block_width) * format->block_byte_count);
2606 else
2608 map_desc->data = base_memory
2609 + (map_desc->row_pitch * box->top)
2610 + (box->left * format->byte_count);
2612 surface->lockedRect.left = box->left;
2613 surface->lockedRect.top = box->top;
2614 surface->lockedRect.right = box->right;
2615 surface->lockedRect.bottom = box->bottom;
2618 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2619 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2621 return WINED3D_OK;
2624 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
2626 HRESULT hr;
2627 struct wined3d_device *device = surface->resource.device;
2628 struct wined3d_context *context = NULL;
2630 TRACE("surface %p, dc %p.\n", surface, dc);
2632 /* Give more detailed info for ddraw. */
2633 if (surface->flags & SFLAG_DCINUSE)
2634 return WINEDDERR_DCALREADYCREATED;
2636 /* Can't GetDC if the surface is locked. */
2637 if (surface->resource.map_count)
2638 return WINED3DERR_INVALIDCALL;
2640 if (device->d3d_initialized)
2641 context = context_acquire(surface->resource.device, NULL);
2643 /* Create a DIB section if there isn't a dc yet. */
2644 if (!surface->hDC)
2646 if (surface->flags & SFLAG_CLIENT)
2648 surface_load_location(surface, context, WINED3D_LOCATION_SYSMEM);
2649 surface_release_client_storage(surface);
2651 hr = surface_create_dib_section(surface);
2652 if (FAILED(hr))
2654 if (context)
2655 context_release(context);
2656 return WINED3DERR_INVALIDCALL;
2658 if (!(surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2659 || surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2660 || surface->pbo))
2661 surface->resource.map_binding = WINED3D_LOCATION_DIB;
2664 surface_load_location(surface, context, WINED3D_LOCATION_DIB);
2665 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
2667 if (context)
2668 context_release(context);
2670 surface->flags |= SFLAG_DCINUSE;
2671 surface->resource.map_count++;
2673 *dc = surface->hDC;
2674 TRACE("Returning dc %p.\n", *dc);
2676 return WINED3D_OK;
2679 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
2681 TRACE("surface %p, dc %p.\n", surface, dc);
2683 if (!(surface->flags & SFLAG_DCINUSE))
2684 return WINEDDERR_NODC;
2686 if (surface->hDC != dc)
2688 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
2689 dc, surface->hDC);
2690 return WINEDDERR_NODC;
2693 surface->resource.map_count--;
2694 surface->flags &= ~SFLAG_DCINUSE;
2696 if (surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2697 || (surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2698 && surface->resource.map_binding != WINED3D_LOCATION_DIB))
2700 /* The game Salammbo modifies the surface contents without mapping the surface between
2701 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
2702 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
2703 * Do not only copy the DIB to the map location, but also make sure the map location is
2704 * copied back to the DIB in the next getdc call.
2706 * The same consideration applies to user memory surfaces. */
2707 struct wined3d_device *device = surface->resource.device;
2708 struct wined3d_context *context = NULL;
2710 if (device->d3d_initialized)
2711 context = context_acquire(device, NULL);
2713 surface_load_location(surface, context, surface->resource.map_binding);
2714 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
2715 if (context)
2716 context_release(context);
2719 return WINED3D_OK;
2722 static void read_from_framebuffer(struct wined3d_surface *surface,
2723 struct wined3d_context *old_ctx, DWORD dst_location)
2725 struct wined3d_device *device = surface->resource.device;
2726 const struct wined3d_gl_info *gl_info;
2727 struct wined3d_context *context = old_ctx;
2728 struct wined3d_surface *restore_rt = NULL;
2729 BYTE *mem;
2730 BYTE *row, *top, *bottom;
2731 int i;
2732 BOOL srcIsUpsideDown;
2733 struct wined3d_bo_address data;
2735 surface_get_memory(surface, &data, dst_location);
2737 if (surface != old_ctx->current_rt)
2739 restore_rt = old_ctx->current_rt;
2740 context = context_acquire(device, surface);
2743 context_apply_blit_state(context, device);
2744 gl_info = context->gl_info;
2746 /* Select the correct read buffer, and give some debug output.
2747 * There is no need to keep track of the current read buffer or reset it, every part of the code
2748 * that reads sets the read buffer as desired.
2750 if (wined3d_resource_is_offscreen(&surface->container->resource))
2752 /* Mapping the primary render target which is not on a swapchain.
2753 * Read from the back buffer. */
2754 TRACE("Mapping offscreen render target.\n");
2755 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2756 srcIsUpsideDown = TRUE;
2758 else
2760 /* Onscreen surfaces are always part of a swapchain */
2761 GLenum buffer = surface_get_gl_buffer(surface);
2762 TRACE("Mapping %#x buffer.\n", buffer);
2763 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2764 checkGLcall("glReadBuffer");
2765 srcIsUpsideDown = FALSE;
2768 if (data.buffer_object)
2770 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
2771 checkGLcall("glBindBuffer");
2774 /* Setup pixel store pack state -- to glReadPixels into the correct place */
2775 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH,
2776 wined3d_surface_get_pitch(surface) / surface->resource.format->byte_count);
2777 checkGLcall("glPixelStorei");
2779 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
2780 surface->resource.width, surface->resource.height,
2781 surface->resource.format->glFormat,
2782 surface->resource.format->glType, data.addr);
2783 checkGLcall("glReadPixels");
2785 /* Reset previous pixel store pack state */
2786 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
2787 checkGLcall("glPixelStorei");
2789 if (!srcIsUpsideDown)
2791 /* glReadPixels returns the image upside down, and there is no way to prevent this.
2792 * Flip the lines in software. */
2793 UINT pitch = wined3d_surface_get_pitch(surface);
2795 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
2796 goto error;
2798 if (data.buffer_object)
2800 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
2801 checkGLcall("glMapBuffer");
2803 else
2804 mem = data.addr;
2806 top = mem;
2807 bottom = mem + pitch * (surface->resource.height - 1);
2808 for (i = 0; i < surface->resource.height / 2; i++)
2810 memcpy(row, top, pitch);
2811 memcpy(top, bottom, pitch);
2812 memcpy(bottom, row, pitch);
2813 top += pitch;
2814 bottom -= pitch;
2816 HeapFree(GetProcessHeap(), 0, row);
2818 if (data.buffer_object)
2819 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
2822 error:
2823 if (data.buffer_object)
2825 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2826 checkGLcall("glBindBuffer");
2829 if (restore_rt)
2830 context_restore(context, restore_rt);
2833 /* Read the framebuffer contents into a texture. Note that this function
2834 * doesn't do any kind of flipping. Using this on an onscreen surface will
2835 * result in a flipped D3D texture.
2837 * Context activation is done by the caller. This function may temporarily
2838 * switch to a different context and restore the original one before return. */
2839 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
2841 struct wined3d_device *device = surface->resource.device;
2842 const struct wined3d_gl_info *gl_info;
2843 struct wined3d_context *context = old_ctx;
2844 struct wined3d_surface *restore_rt = NULL;
2846 if (old_ctx->current_rt != surface)
2848 restore_rt = old_ctx->current_rt;
2849 context = context_acquire(device, surface);
2852 gl_info = context->gl_info;
2853 device_invalidate_state(device, STATE_FRAMEBUFFER);
2855 wined3d_texture_prepare_texture(surface->container, context, srgb);
2856 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
2858 TRACE("Reading back offscreen render target %p.\n", surface);
2860 if (wined3d_resource_is_offscreen(&surface->container->resource))
2861 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2862 else
2863 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
2864 checkGLcall("glReadBuffer");
2866 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
2867 0, 0, 0, 0, surface->resource.width, surface->resource.height);
2868 checkGLcall("glCopyTexSubImage2D");
2870 if (restore_rt)
2871 context_restore(context, restore_rt);
2874 static void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2876 if (multisample)
2878 DWORD samples;
2880 if (surface->rb_multisample)
2881 return;
2883 /* TODO: Nvidia exposes their Coverage Sample Anti-Aliasing (CSAA) feature
2884 * through type == MULTISAMPLE_XX and quality != 0. This could be mapped
2885 * to GL_NV_framebuffer_multisample_coverage.
2887 * AMD has a similar feature called Enhanced Quality Anti-Aliasing (EQAA),
2888 * but it does not have an equivalent OpenGL extension. */
2889 if (surface->resource.multisample_type == WINED3D_MULTISAMPLE_NON_MASKABLE)
2890 samples = surface->resource.multisample_quality;
2891 else
2892 samples = surface->resource.multisample_type;
2894 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2895 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2896 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
2897 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
2898 checkGLcall("glRenderbufferStorageMultisample()");
2899 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2901 else
2903 if (surface->rb_resolved)
2904 return;
2906 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2907 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2908 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
2909 surface->pow2Width, surface->pow2Height);
2910 checkGLcall("glRenderbufferStorage()");
2911 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2915 /* Does a direct frame buffer -> texture copy. Stretching is done with single
2916 * pixel copy calls. */
2917 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2918 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2920 struct wined3d_device *device = dst_surface->resource.device;
2921 const struct wined3d_gl_info *gl_info;
2922 float xrel, yrel;
2923 struct wined3d_context *context;
2924 BOOL upsidedown = FALSE;
2925 RECT dst_rect = *dst_rect_in;
2927 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2928 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2930 if(dst_rect.top > dst_rect.bottom) {
2931 UINT tmp = dst_rect.bottom;
2932 dst_rect.bottom = dst_rect.top;
2933 dst_rect.top = tmp;
2934 upsidedown = TRUE;
2937 context = context_acquire(device, src_surface);
2938 gl_info = context->gl_info;
2939 context_apply_blit_state(context, device);
2940 wined3d_texture_load(dst_surface->container, context, FALSE);
2942 /* Bind the target texture */
2943 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
2944 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
2946 TRACE("Reading from an offscreen target\n");
2947 upsidedown = !upsidedown;
2948 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2950 else
2952 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
2954 checkGLcall("glReadBuffer");
2956 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
2957 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
2959 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2961 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2963 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2964 ERR("Texture filtering not supported in direct blit.\n");
2966 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2967 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2969 ERR("Texture filtering not supported in direct blit\n");
2972 if (upsidedown
2973 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2974 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2976 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
2977 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2978 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
2979 src_rect->left, src_surface->resource.height - src_rect->bottom,
2980 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2982 else
2984 LONG row;
2985 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
2986 /* I have to process this row by row to swap the image,
2987 * otherwise it would be upside down, so stretching in y direction
2988 * doesn't cost extra time
2990 * However, stretching in x direction can be avoided if not necessary
2992 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
2993 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2995 /* Well, that stuff works, but it's very slow.
2996 * find a better way instead
2998 LONG col;
3000 for (col = dst_rect.left; col < dst_rect.right; ++col)
3002 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3003 dst_rect.left + col /* x offset */, row /* y offset */,
3004 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3007 else
3009 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3010 dst_rect.left /* x offset */, row /* y offset */,
3011 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3015 checkGLcall("glCopyTexSubImage2D");
3017 context_release(context);
3019 /* The texture is now most up to date - If the surface is a render target
3020 * and has a drawable, this path is never entered. */
3021 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3022 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3025 /* Uses the hardware to stretch and flip the image */
3026 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3027 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3029 struct wined3d_device *device = dst_surface->resource.device;
3030 GLuint src, backup = 0;
3031 float left, right, top, bottom; /* Texture coordinates */
3032 UINT fbwidth = src_surface->resource.width;
3033 UINT fbheight = src_surface->resource.height;
3034 const struct wined3d_gl_info *gl_info;
3035 struct wined3d_context *context;
3036 GLenum drawBuffer = GL_BACK;
3037 GLenum texture_target;
3038 BOOL noBackBufferBackup;
3039 BOOL src_offscreen;
3040 BOOL upsidedown = FALSE;
3041 RECT dst_rect = *dst_rect_in;
3043 TRACE("Using hwstretch blit\n");
3044 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3045 context = context_acquire(device, src_surface);
3046 gl_info = context->gl_info;
3047 context_apply_blit_state(context, device);
3048 wined3d_texture_load(dst_surface->container, context, FALSE);
3050 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
3051 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3052 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3054 /* Get it a description */
3055 wined3d_texture_load(src_surface->container, context, FALSE);
3058 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3059 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3061 if (context->aux_buffers >= 2)
3063 /* Got more than one aux buffer? Use the 2nd aux buffer */
3064 drawBuffer = GL_AUX1;
3066 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3068 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3069 drawBuffer = GL_AUX0;
3072 if (noBackBufferBackup)
3074 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3075 checkGLcall("glGenTextures");
3076 context_bind_texture(context, GL_TEXTURE_2D, backup);
3077 texture_target = GL_TEXTURE_2D;
3079 else
3081 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3082 * we are reading from the back buffer, the backup can be used as source texture
3084 texture_target = src_surface->texture_target;
3085 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3086 gl_info->gl_ops.gl.p_glEnable(texture_target);
3087 checkGLcall("glEnable(texture_target)");
3089 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3090 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3093 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3094 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3096 if(dst_rect.top > dst_rect.bottom) {
3097 UINT tmp = dst_rect.bottom;
3098 dst_rect.bottom = dst_rect.top;
3099 dst_rect.top = tmp;
3100 upsidedown = TRUE;
3103 if (src_offscreen)
3105 TRACE("Reading from an offscreen target\n");
3106 upsidedown = !upsidedown;
3107 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3109 else
3111 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3114 /* TODO: Only back up the part that will be overwritten */
3115 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3117 checkGLcall("glCopyTexSubImage2D");
3119 /* No issue with overriding these - the sampler is dirty due to blit usage */
3120 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
3121 checkGLcall("glTexParameteri");
3122 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3123 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
3124 checkGLcall("glTexParameteri");
3126 if (!src_surface->container->swapchain
3127 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
3129 src = backup ? backup : src_surface->container->texture_rgb.name;
3131 else
3133 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3134 checkGLcall("glReadBuffer(GL_FRONT)");
3136 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3137 checkGLcall("glGenTextures(1, &src)");
3138 context_bind_texture(context, GL_TEXTURE_2D, src);
3140 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3141 * out for power of 2 sizes
3143 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3144 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3145 checkGLcall("glTexImage2D");
3146 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3148 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3149 checkGLcall("glTexParameteri");
3150 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3151 checkGLcall("glTexParameteri");
3153 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3154 checkGLcall("glReadBuffer(GL_BACK)");
3156 if (texture_target != GL_TEXTURE_2D)
3158 gl_info->gl_ops.gl.p_glDisable(texture_target);
3159 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3160 texture_target = GL_TEXTURE_2D;
3163 checkGLcall("glEnd and previous");
3165 left = src_rect->left;
3166 right = src_rect->right;
3168 if (!upsidedown)
3170 top = src_surface->resource.height - src_rect->top;
3171 bottom = src_surface->resource.height - src_rect->bottom;
3173 else
3175 top = src_surface->resource.height - src_rect->bottom;
3176 bottom = src_surface->resource.height - src_rect->top;
3179 if (src_surface->container->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
3181 left /= src_surface->pow2Width;
3182 right /= src_surface->pow2Width;
3183 top /= src_surface->pow2Height;
3184 bottom /= src_surface->pow2Height;
3187 /* draw the source texture stretched and upside down. The correct surface is bound already */
3188 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3189 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3191 context_set_draw_buffer(context, drawBuffer);
3192 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3194 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3195 /* bottom left */
3196 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3197 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3199 /* top left */
3200 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3201 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3203 /* top right */
3204 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3205 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3207 /* bottom right */
3208 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3209 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3210 gl_info->gl_ops.gl.p_glEnd();
3211 checkGLcall("glEnd and previous");
3213 if (texture_target != dst_surface->texture_target)
3215 gl_info->gl_ops.gl.p_glDisable(texture_target);
3216 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3217 texture_target = dst_surface->texture_target;
3220 /* Now read the stretched and upside down image into the destination texture */
3221 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
3222 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
3224 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3225 0, 0, /* We blitted the image to the origin */
3226 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3227 checkGLcall("glCopyTexSubImage2D");
3229 if (drawBuffer == GL_BACK)
3231 /* Write the back buffer backup back. */
3232 if (backup)
3234 if (texture_target != GL_TEXTURE_2D)
3236 gl_info->gl_ops.gl.p_glDisable(texture_target);
3237 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3238 texture_target = GL_TEXTURE_2D;
3240 context_bind_texture(context, GL_TEXTURE_2D, backup);
3242 else
3244 if (texture_target != src_surface->texture_target)
3246 gl_info->gl_ops.gl.p_glDisable(texture_target);
3247 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3248 texture_target = src_surface->texture_target;
3250 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3253 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3254 /* top left */
3255 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3256 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3258 /* bottom left */
3259 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3260 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3262 /* bottom right */
3263 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3264 (float)fbheight / (float)src_surface->pow2Height);
3265 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3267 /* top right */
3268 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3269 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3270 gl_info->gl_ops.gl.p_glEnd();
3272 gl_info->gl_ops.gl.p_glDisable(texture_target);
3273 checkGLcall("glDisable(texture_target)");
3275 /* Cleanup */
3276 if (src != src_surface->container->texture_rgb.name && src != backup)
3278 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3279 checkGLcall("glDeleteTextures(1, &src)");
3281 if (backup)
3283 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3284 checkGLcall("glDeleteTextures(1, &backup)");
3287 if (wined3d_settings.strict_draw_ordering)
3288 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3290 context_release(context);
3292 /* The texture is now most up to date - If the surface is a render target
3293 * and has a drawable, this path is never entered. */
3294 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3295 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3298 /* Front buffer coordinates are always full screen coordinates, but our GL
3299 * drawable is limited to the window's client area. The sysmem and texture
3300 * copies do have the full screen size. Note that GL has a bottom-left
3301 * origin, while D3D has a top-left origin. */
3302 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3304 UINT drawable_height;
3306 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3308 POINT offset = {0, 0};
3309 RECT windowsize;
3311 ScreenToClient(window, &offset);
3312 OffsetRect(rect, offset.x, offset.y);
3314 GetClientRect(window, &windowsize);
3315 drawable_height = windowsize.bottom - windowsize.top;
3317 else
3319 drawable_height = surface->resource.height;
3322 rect->top = drawable_height - rect->top;
3323 rect->bottom = drawable_height - rect->bottom;
3326 /* Context activation is done by the caller. */
3327 static void surface_blt_to_drawable(const struct wined3d_device *device,
3328 struct wined3d_context *old_ctx,
3329 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3330 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3331 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3333 const struct wined3d_gl_info *gl_info;
3334 struct wined3d_context *context = old_ctx;
3335 struct wined3d_surface *restore_rt = NULL;
3336 RECT src_rect, dst_rect;
3338 src_rect = *src_rect_in;
3339 dst_rect = *dst_rect_in;
3342 if (old_ctx->current_rt != dst_surface)
3344 restore_rt = old_ctx->current_rt;
3345 context = context_acquire(device, dst_surface);
3348 gl_info = context->gl_info;
3350 /* Make sure the surface is up-to-date. This should probably use
3351 * surface_load_location() and worry about the destination surface too,
3352 * unless we're overwriting it completely. */
3353 wined3d_texture_load(src_surface->container, context, FALSE);
3355 /* Activate the destination context, set it up for blitting */
3356 context_apply_blit_state(context, device);
3358 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3359 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3361 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
3363 if (alpha_test)
3365 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3366 checkGLcall("glEnable(GL_ALPHA_TEST)");
3368 /* For P8 surfaces, the alpha component contains the palette index.
3369 * Which means that the colorkey is one of the palette entries. In
3370 * other cases pixels that should be masked away have alpha set to 0. */
3371 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3372 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3373 (float)src_surface->container->async.src_blt_color_key.color_space_low_value / 255.0f);
3374 else
3375 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3376 checkGLcall("glAlphaFunc");
3378 else
3380 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3381 checkGLcall("glDisable(GL_ALPHA_TEST)");
3384 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3386 if (alpha_test)
3388 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3389 checkGLcall("glDisable(GL_ALPHA_TEST)");
3392 /* Leave the opengl state valid for blitting */
3393 device->blitter->unset_shader(context->gl_info);
3395 if (wined3d_settings.strict_draw_ordering
3396 || (dst_surface->container->swapchain
3397 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3398 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3400 if (restore_rt)
3401 context_restore(context, restore_rt);
3404 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3406 struct wined3d_device *device = s->resource.device;
3407 const struct blit_shader *blitter;
3409 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, WINED3D_BLIT_OP_COLOR_FILL,
3410 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
3411 if (!blitter)
3413 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3414 return WINED3DERR_INVALIDCALL;
3417 return blitter->color_fill(device, s, rect, color);
3420 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3421 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3422 enum wined3d_texture_filter_type filter)
3424 struct wined3d_device *device = dst_surface->resource.device;
3425 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3426 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3428 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3429 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3430 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3432 /* Get the swapchain. One of the surfaces has to be a primary surface */
3433 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3435 WARN("Destination is in sysmem, rejecting gl blt\n");
3436 return WINED3DERR_INVALIDCALL;
3439 dst_swapchain = dst_surface->container->swapchain;
3441 if (src_surface)
3443 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3445 WARN("Src is in sysmem, rejecting gl blt\n");
3446 return WINED3DERR_INVALIDCALL;
3449 src_swapchain = src_surface->container->swapchain;
3451 else
3453 src_swapchain = NULL;
3456 /* Early sort out of cases where no render target is used */
3457 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
3459 TRACE("No surface is render target, not using hardware blit.\n");
3460 return WINED3DERR_INVALIDCALL;
3463 /* No destination color keying supported */
3464 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
3466 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3467 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3468 return WINED3DERR_INVALIDCALL;
3471 if (dst_swapchain && dst_swapchain == src_swapchain)
3473 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3474 return WINED3DERR_INVALIDCALL;
3477 if (dst_swapchain && src_swapchain)
3479 FIXME("Implement hardware blit between two different swapchains\n");
3480 return WINED3DERR_INVALIDCALL;
3483 if (dst_swapchain)
3485 /* Handled with regular texture -> swapchain blit */
3486 if (src_surface == rt)
3487 TRACE("Blit from active render target to a swapchain\n");
3489 else if (src_swapchain && dst_surface == rt)
3491 FIXME("Implement blit from a swapchain to the active render target\n");
3492 return WINED3DERR_INVALIDCALL;
3495 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
3497 /* Blit from render target to texture */
3498 BOOL stretchx;
3500 /* P8 read back is not implemented */
3501 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
3502 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
3504 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3505 return WINED3DERR_INVALIDCALL;
3508 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3510 TRACE("Color keying not supported by frame buffer to texture blit\n");
3511 return WINED3DERR_INVALIDCALL;
3512 /* Destination color key is checked above */
3515 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
3516 stretchx = TRUE;
3517 else
3518 stretchx = FALSE;
3520 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3521 * flip the image nor scale it.
3523 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3524 * -> If the app wants an image width an unscaled width, copy it line per line
3525 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
3526 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3527 * back buffer. This is slower than reading line per line, thus not used for flipping
3528 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3529 * pixel by pixel. */
3530 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
3531 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
3533 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
3534 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
3536 else
3538 TRACE("Using hardware stretching to flip / stretch the texture.\n");
3539 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
3542 surface_evict_sysmem(dst_surface);
3544 return WINED3D_OK;
3547 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3548 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3549 return WINED3DERR_INVALIDCALL;
3552 /* Context activation is done by the caller. */
3553 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
3554 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
3556 struct wined3d_device *device = surface->resource.device;
3557 const struct wined3d_gl_info *gl_info = context->gl_info;
3558 GLint compare_mode = GL_NONE;
3559 struct blt_info info;
3560 GLint old_binding = 0;
3561 RECT rect;
3563 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
3565 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
3566 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
3567 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3568 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
3569 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
3570 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
3571 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
3572 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
3573 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3574 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
3575 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
3577 SetRect(&rect, 0, h, w, 0);
3578 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
3579 context_active_texture(context, context->gl_info, 0);
3580 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
3581 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
3582 if (gl_info->supported[ARB_SHADOW])
3584 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
3585 if (compare_mode != GL_NONE)
3586 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
3589 device->shader_backend->shader_select_depth_blt(device->shader_priv,
3590 gl_info, info.tex_type, &surface->ds_current_size);
3592 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
3593 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
3594 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
3595 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
3596 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
3597 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
3598 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
3599 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
3600 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
3601 gl_info->gl_ops.gl.p_glEnd();
3603 if (compare_mode != GL_NONE)
3604 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
3605 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
3607 gl_info->gl_ops.gl.p_glPopAttrib();
3609 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
3612 void surface_modify_ds_location(struct wined3d_surface *surface,
3613 DWORD location, UINT w, UINT h)
3615 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
3617 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
3618 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
3619 wined3d_texture_set_dirty(surface->container);
3621 surface->ds_current_size.cx = w;
3622 surface->ds_current_size.cy = h;
3623 surface->locations = location;
3626 /* Context activation is done by the caller. */
3627 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3629 const struct wined3d_gl_info *gl_info = context->gl_info;
3630 struct wined3d_device *device = surface->resource.device;
3631 GLsizei w, h;
3633 TRACE("surface %p, context %p, new location %#x.\n", surface, context, location);
3635 /* TODO: Make this work for modes other than FBO */
3636 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3638 if (!(surface->locations & location))
3640 w = surface->ds_current_size.cx;
3641 h = surface->ds_current_size.cy;
3642 surface->ds_current_size.cx = 0;
3643 surface->ds_current_size.cy = 0;
3645 else
3647 w = surface->resource.width;
3648 h = surface->resource.height;
3651 if (surface->ds_current_size.cx == surface->resource.width
3652 && surface->ds_current_size.cy == surface->resource.height)
3654 TRACE("Location (%#x) is already up to date.\n", location);
3655 return;
3658 if (surface->current_renderbuffer)
3660 FIXME("Not supported with fixed up depth stencil.\n");
3661 return;
3664 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3666 TRACE("Surface was discarded, no need copy data.\n");
3667 wined3d_surface_prepare(surface, context, location);
3668 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
3669 surface->locations |= location;
3670 surface->ds_current_size.cx = surface->resource.width;
3671 surface->ds_current_size.cy = surface->resource.height;
3672 return;
3675 if (!surface->locations)
3677 FIXME("No up to date depth stencil location.\n");
3678 surface->locations |= location;
3679 surface->ds_current_size.cx = surface->resource.width;
3680 surface->ds_current_size.cy = surface->resource.height;
3681 return;
3684 if (location == WINED3D_LOCATION_TEXTURE_RGB)
3686 GLint old_binding = 0;
3687 GLenum bind_target;
3689 /* The render target is allowed to be smaller than the depth/stencil
3690 * buffer, so the onscreen depth/stencil buffer is potentially smaller
3691 * than the offscreen surface. Don't overwrite the offscreen surface
3692 * with undefined data. */
3693 w = min(w, context->swapchain->desc.backbuffer_width);
3694 h = min(h, context->swapchain->desc.backbuffer_height);
3696 TRACE("Copying onscreen depth buffer to depth texture.\n");
3698 if (!device->depth_blt_texture)
3699 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
3701 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3702 * directly on the FBO texture. That's because we need to flip. */
3703 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3704 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3705 NULL, WINED3D_LOCATION_DRAWABLE);
3706 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
3708 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
3709 bind_target = GL_TEXTURE_RECTANGLE_ARB;
3711 else
3713 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3714 bind_target = GL_TEXTURE_2D;
3716 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
3717 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
3718 * internal format, because the internal format might include stencil
3719 * data. In principle we should copy stencil data as well, but unless
3720 * the driver supports stencil export it's hard to do, and doesn't
3721 * seem to be needed in practice. If the hardware doesn't support
3722 * writing stencil data, the glCopyTexImage2D() call might trigger
3723 * software fallbacks. */
3724 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
3725 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3726 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3727 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3728 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3729 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
3730 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
3732 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3733 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
3734 context_set_draw_buffer(context, GL_NONE);
3736 /* Do the actual blit */
3737 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
3738 checkGLcall("depth_blt");
3740 context_invalidate_state(context, STATE_FRAMEBUFFER);
3742 if (wined3d_settings.strict_draw_ordering)
3743 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3745 else if (location == WINED3D_LOCATION_DRAWABLE)
3747 TRACE("Copying depth texture to onscreen depth buffer.\n");
3749 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3750 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3751 NULL, WINED3D_LOCATION_DRAWABLE);
3752 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
3753 0, surface->pow2Height - h, w, h, surface->texture_target);
3754 checkGLcall("depth_blt");
3756 context_invalidate_state(context, STATE_FRAMEBUFFER);
3758 if (wined3d_settings.strict_draw_ordering)
3759 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3761 else
3763 ERR("Invalid location (%#x) specified.\n", location);
3766 surface->locations |= location;
3767 surface->ds_current_size.cx = surface->resource.width;
3768 surface->ds_current_size.cy = surface->resource.height;
3771 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
3773 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3775 surface->locations |= location;
3778 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
3780 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3782 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3783 wined3d_texture_set_dirty(surface->container);
3784 surface->locations &= ~location;
3786 if (!surface->locations)
3787 ERR("Surface %p does not have any up to date location.\n", surface);
3790 static DWORD resource_access_from_location(DWORD location)
3792 switch (location)
3794 case WINED3D_LOCATION_SYSMEM:
3795 case WINED3D_LOCATION_USER_MEMORY:
3796 case WINED3D_LOCATION_DIB:
3797 case WINED3D_LOCATION_BUFFER:
3798 return WINED3D_RESOURCE_ACCESS_CPU;
3800 case WINED3D_LOCATION_DRAWABLE:
3801 case WINED3D_LOCATION_TEXTURE_SRGB:
3802 case WINED3D_LOCATION_TEXTURE_RGB:
3803 case WINED3D_LOCATION_RB_MULTISAMPLE:
3804 case WINED3D_LOCATION_RB_RESOLVED:
3805 return WINED3D_RESOURCE_ACCESS_GPU;
3807 default:
3808 FIXME("Unhandled location %#x.\n", location);
3809 return 0;
3813 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
3815 struct wined3d_device *device = surface->resource.device;
3816 struct wined3d_context *context;
3817 const struct wined3d_gl_info *gl_info;
3818 struct wined3d_bo_address dst, src;
3819 UINT size = surface->resource.size;
3821 surface_get_memory(surface, &dst, location);
3822 surface_get_memory(surface, &src, surface->locations);
3824 if (dst.buffer_object)
3826 context = context_acquire(device, NULL);
3827 gl_info = context->gl_info;
3828 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
3829 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr));
3830 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
3831 checkGLcall("Upload PBO");
3832 context_release(context);
3833 return;
3835 if (src.buffer_object)
3837 context = context_acquire(device, NULL);
3838 gl_info = context->gl_info;
3839 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
3840 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr));
3841 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
3842 checkGLcall("Download PBO");
3843 context_release(context);
3844 return;
3846 memcpy(dst.addr, src.addr, size);
3849 /* Context activation is done by the caller. */
3850 static void surface_load_sysmem(struct wined3d_surface *surface,
3851 struct wined3d_context *context, DWORD dst_location)
3853 const struct wined3d_gl_info *gl_info = context->gl_info;
3855 if (surface->locations & surface_simple_locations)
3857 surface_copy_simple_location(surface, dst_location);
3858 return;
3861 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
3862 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3864 /* Download the surface to system memory. */
3865 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3867 wined3d_texture_bind_and_dirtify(surface->container, context,
3868 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
3869 surface_download_data(surface, gl_info, dst_location);
3871 return;
3874 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
3876 read_from_framebuffer(surface, context, dst_location);
3877 return;
3880 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
3881 surface, wined3d_debug_location(surface->locations));
3884 /* Context activation is done by the caller. */
3885 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
3886 struct wined3d_context *context)
3888 RECT r;
3890 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3891 && wined3d_resource_is_offscreen(&surface->container->resource))
3893 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
3894 return WINED3DERR_INVALIDCALL;
3897 surface_get_rect(surface, NULL, &r);
3898 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3899 surface_blt_to_drawable(surface->resource.device, context,
3900 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
3902 return WINED3D_OK;
3905 static HRESULT surface_load_texture(struct wined3d_surface *surface,
3906 struct wined3d_context *context, BOOL srgb)
3908 const struct wined3d_gl_info *gl_info = context->gl_info;
3909 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
3910 struct wined3d_device *device = surface->resource.device;
3911 const struct wined3d_color_key_conversion *conversion;
3912 struct wined3d_texture *texture = surface->container;
3913 UINT width, src_pitch, dst_pitch;
3914 struct wined3d_bo_address data;
3915 struct wined3d_format format;
3916 POINT dst_point = {0, 0};
3917 BYTE *mem = NULL;
3919 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
3920 && wined3d_resource_is_offscreen(&texture->resource)
3921 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
3923 surface_load_fb_texture(surface, srgb, context);
3925 return WINED3D_OK;
3928 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
3929 && (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
3930 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3931 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3932 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3934 if (srgb)
3935 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
3936 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
3937 else
3938 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
3939 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
3941 return WINED3D_OK;
3944 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
3945 && (!srgb || (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
3946 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3947 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3948 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3950 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
3951 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
3952 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
3953 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3955 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
3956 &rect, surface, dst_location, &rect);
3958 return WINED3D_OK;
3961 /* Upload from system memory */
3963 if (srgb)
3965 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
3966 == WINED3D_LOCATION_TEXTURE_RGB)
3968 /* Performance warning... */
3969 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
3970 surface_prepare_map_memory(surface);
3971 surface_load_location(surface, context, surface->resource.map_binding);
3974 else
3976 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
3977 == WINED3D_LOCATION_TEXTURE_SRGB)
3979 /* Performance warning... */
3980 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
3981 surface_prepare_map_memory(surface);
3982 surface_load_location(surface, context, surface->resource.map_binding);
3986 if (!(surface->locations & surface_simple_locations))
3988 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
3989 /* Lets hope we get it from somewhere... */
3990 surface_prepare_system_memory(surface);
3991 surface_load_location(surface, context, WINED3D_LOCATION_SYSMEM);
3994 wined3d_texture_prepare_texture(texture, context, srgb);
3995 wined3d_texture_bind_and_dirtify(texture, context, srgb);
3997 width = surface->resource.width;
3998 src_pitch = wined3d_surface_get_pitch(surface);
4000 format = *texture->resource.format;
4001 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
4002 format = *wined3d_get_format(gl_info, conversion->dst_format);
4004 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
4005 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
4006 * getting called. */
4007 if ((format.convert || conversion) && surface->pbo)
4009 TRACE("Removing the pbo attached to surface %p.\n", surface);
4011 if (surface->flags & SFLAG_DIBSECTION)
4012 surface->resource.map_binding = WINED3D_LOCATION_DIB;
4013 else
4014 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
4016 surface_prepare_map_memory(surface);
4017 surface_load_location(surface, context, surface->resource.map_binding);
4018 surface_remove_pbo(surface, gl_info);
4021 surface_get_memory(surface, &data, surface->locations);
4022 if (format.convert)
4024 /* This code is entered for texture formats which need a fixup. */
4025 UINT height = surface->resource.height;
4027 format.byte_count = format.conv_byte_count;
4028 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4030 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4032 ERR("Out of memory (%u).\n", dst_pitch * height);
4033 context_release(context);
4034 return E_OUTOFMEMORY;
4036 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4037 dst_pitch, dst_pitch * height, width, height, 1);
4038 src_pitch = dst_pitch;
4039 data.addr = mem;
4041 else if (conversion)
4043 /* This code is only entered for color keying fixups */
4044 struct wined3d_palette *palette = NULL;
4045 UINT height = surface->resource.height;
4047 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4048 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4050 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4052 ERR("Out of memory (%u).\n", dst_pitch * height);
4053 context_release(context);
4054 return E_OUTOFMEMORY;
4056 if (texture->swapchain && texture->swapchain->palette)
4057 palette = texture->swapchain->palette;
4058 conversion->convert(data.addr, src_pitch, mem, dst_pitch,
4059 width, height, palette, &texture->async.gl_color_key);
4060 src_pitch = dst_pitch;
4061 data.addr = mem;
4064 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
4065 src_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
4067 HeapFree(GetProcessHeap(), 0, mem);
4069 return WINED3D_OK;
4072 /* Context activation is done by the caller. */
4073 static void surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
4074 DWORD dst_location)
4076 const RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4077 DWORD src_location;
4079 if (surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE)
4080 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
4081 else if (surface->locations & WINED3D_LOCATION_RB_RESOLVED)
4082 src_location = WINED3D_LOCATION_RB_RESOLVED;
4083 else if (surface->locations & WINED3D_LOCATION_TEXTURE_SRGB)
4084 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
4085 else /* surface_blt_fbo will load the source location if necessary. */
4086 src_location = WINED3D_LOCATION_TEXTURE_RGB;
4088 surface_blt_fbo(surface->resource.device, context, WINED3D_TEXF_POINT,
4089 surface, src_location, &rect, surface, dst_location, &rect);
4092 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
4093 HRESULT surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4095 HRESULT hr;
4097 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4099 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4101 if (location == WINED3D_LOCATION_TEXTURE_RGB
4102 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4104 surface_load_ds_location(surface, context, location);
4105 return WINED3D_OK;
4107 else if (location & surface->locations
4108 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
4110 /* Already up to date, nothing to do. */
4111 return WINED3D_OK;
4113 else
4115 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4116 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4117 return WINED3DERR_INVALIDCALL;
4121 if (surface->locations & location)
4123 TRACE("Location already up to date.\n");
4124 return WINED3D_OK;
4127 if (WARN_ON(d3d_surface))
4129 DWORD required_access = resource_access_from_location(location);
4130 if ((surface->resource.access_flags & required_access) != required_access)
4131 WARN("Operation requires %#x access, but surface only has %#x.\n",
4132 required_access, surface->resource.access_flags);
4135 if (!surface->locations)
4137 ERR("Surface %p does not have any up to date location.\n", surface);
4138 return WINED3DERR_INVALIDCALL;
4141 switch (location)
4143 case WINED3D_LOCATION_DIB:
4144 case WINED3D_LOCATION_USER_MEMORY:
4145 case WINED3D_LOCATION_SYSMEM:
4146 case WINED3D_LOCATION_BUFFER:
4147 surface_load_sysmem(surface, context, location);
4148 break;
4150 case WINED3D_LOCATION_DRAWABLE:
4151 if (FAILED(hr = surface_load_drawable(surface, context)))
4152 return hr;
4153 break;
4155 case WINED3D_LOCATION_RB_RESOLVED:
4156 case WINED3D_LOCATION_RB_MULTISAMPLE:
4157 surface_load_renderbuffer(surface, context, location);
4158 break;
4160 case WINED3D_LOCATION_TEXTURE_RGB:
4161 case WINED3D_LOCATION_TEXTURE_SRGB:
4162 if (FAILED(hr = surface_load_texture(surface, context,
4163 location == WINED3D_LOCATION_TEXTURE_SRGB)))
4164 return hr;
4165 break;
4167 default:
4168 ERR("Don't know how to handle location %#x.\n", location);
4169 break;
4172 surface_validate_location(surface, location);
4174 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
4175 surface_evict_sysmem(surface);
4177 return WINED3D_OK;
4180 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
4181 /* Context activation is done by the caller. */
4182 static void ffp_blit_free(struct wined3d_device *device) { }
4184 /* Context activation is done by the caller. */
4185 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4186 const struct wined3d_color_key *color_key)
4188 const struct wined3d_gl_info *gl_info = context->gl_info;
4190 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
4191 checkGLcall("glEnable(target)");
4193 return WINED3D_OK;
4196 /* Context activation is done by the caller. */
4197 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
4199 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
4200 checkGLcall("glDisable(GL_TEXTURE_2D)");
4201 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4203 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4204 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4206 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4208 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
4209 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4213 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
4214 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4215 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4216 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4218 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4220 TRACE("Source or destination is in system memory.\n");
4221 return FALSE;
4224 switch (blit_op)
4226 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
4227 if (d3d_info->shader_color_key)
4229 TRACE("Color keying requires converted textures.\n");
4230 return FALSE;
4232 case WINED3D_BLIT_OP_COLOR_BLIT:
4233 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4235 TRACE("Checking support for fixup:\n");
4236 dump_color_fixup_desc(src_format->color_fixup);
4239 /* We only support identity conversions. */
4240 if (!is_identity_fixup(src_format->color_fixup)
4241 || !is_identity_fixup(dst_format->color_fixup))
4243 TRACE("Fixups are not supported.\n");
4244 return FALSE;
4247 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4249 TRACE("Can only blit to render targets.\n");
4250 return FALSE;
4252 return TRUE;
4254 case WINED3D_BLIT_OP_COLOR_FILL:
4255 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4257 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
4258 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4259 return FALSE;
4261 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4263 TRACE("Color fill not supported\n");
4264 return FALSE;
4267 /* FIXME: We should reject color fills on formats with fixups,
4268 * but this would break P8 color fills for example. */
4270 return TRUE;
4272 case WINED3D_BLIT_OP_DEPTH_FILL:
4273 return TRUE;
4275 default:
4276 TRACE("Unsupported blit_op=%d\n", blit_op);
4277 return FALSE;
4281 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4282 const RECT *dst_rect, const struct wined3d_color *color)
4284 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4285 struct wined3d_rendertarget_view *view;
4286 struct wined3d_fb_state fb = {&view, NULL};
4287 HRESULT hr;
4289 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4290 NULL, &wined3d_null_parent_ops, &view)))
4292 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4293 return hr;
4296 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4297 wined3d_rendertarget_view_decref(view);
4299 return WINED3D_OK;
4302 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4303 const RECT *dst_rect, float depth)
4305 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4306 struct wined3d_fb_state fb = {NULL, NULL};
4307 HRESULT hr;
4309 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4310 NULL, &wined3d_null_parent_ops, &fb.depth_stencil)))
4312 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4313 return hr;
4316 device_clear_render_targets(device, 0, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4317 wined3d_rendertarget_view_decref(fb.depth_stencil);
4319 return WINED3D_OK;
4322 static void ffp_blit_blit_surface(struct wined3d_device *device, DWORD filter,
4323 struct wined3d_surface *src_surface, const RECT *src_rect,
4324 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4325 const struct wined3d_color_key *color_key)
4327 struct wined3d_context *context;
4329 /* Blit from offscreen surface to render target */
4330 struct wined3d_color_key old_blt_key = src_surface->container->async.src_blt_color_key;
4331 DWORD old_color_key_flags = src_surface->container->async.color_key_flags;
4333 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4335 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT, color_key);
4337 context = context_acquire(device, dst_surface);
4338 surface_blt_to_drawable(device, context, filter,
4339 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
4340 context_release(context);
4342 /* Restore the color key parameters */
4343 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT,
4344 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
4346 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
4347 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
4350 const struct blit_shader ffp_blit = {
4351 ffp_blit_alloc,
4352 ffp_blit_free,
4353 ffp_blit_set,
4354 ffp_blit_unset,
4355 ffp_blit_supported,
4356 ffp_blit_color_fill,
4357 ffp_blit_depth_fill,
4358 ffp_blit_blit_surface,
4361 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4363 return WINED3D_OK;
4366 /* Context activation is done by the caller. */
4367 static void cpu_blit_free(struct wined3d_device *device)
4371 /* Context activation is done by the caller. */
4372 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4373 const struct wined3d_color_key *color_key)
4375 return WINED3D_OK;
4378 /* Context activation is done by the caller. */
4379 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4383 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
4384 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4385 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4386 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4388 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4390 return TRUE;
4393 return FALSE;
4396 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4397 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4398 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4400 UINT row_block_count;
4401 const BYTE *src_row;
4402 BYTE *dst_row;
4403 UINT x, y;
4405 src_row = src_data;
4406 dst_row = dst_data;
4408 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4410 if (!flags)
4412 for (y = 0; y < update_h; y += format->block_height)
4414 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4415 src_row += src_pitch;
4416 dst_row += dst_pitch;
4419 return WINED3D_OK;
4422 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4424 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4426 switch (format->id)
4428 case WINED3DFMT_DXT1:
4429 for (y = 0; y < update_h; y += format->block_height)
4431 struct block
4433 WORD color[2];
4434 BYTE control_row[4];
4437 const struct block *s = (const struct block *)src_row;
4438 struct block *d = (struct block *)dst_row;
4440 for (x = 0; x < row_block_count; ++x)
4442 d[x].color[0] = s[x].color[0];
4443 d[x].color[1] = s[x].color[1];
4444 d[x].control_row[0] = s[x].control_row[3];
4445 d[x].control_row[1] = s[x].control_row[2];
4446 d[x].control_row[2] = s[x].control_row[1];
4447 d[x].control_row[3] = s[x].control_row[0];
4449 src_row -= src_pitch;
4450 dst_row += dst_pitch;
4452 return WINED3D_OK;
4454 case WINED3DFMT_DXT2:
4455 case WINED3DFMT_DXT3:
4456 for (y = 0; y < update_h; y += format->block_height)
4458 struct block
4460 WORD alpha_row[4];
4461 WORD color[2];
4462 BYTE control_row[4];
4465 const struct block *s = (const struct block *)src_row;
4466 struct block *d = (struct block *)dst_row;
4468 for (x = 0; x < row_block_count; ++x)
4470 d[x].alpha_row[0] = s[x].alpha_row[3];
4471 d[x].alpha_row[1] = s[x].alpha_row[2];
4472 d[x].alpha_row[2] = s[x].alpha_row[1];
4473 d[x].alpha_row[3] = s[x].alpha_row[0];
4474 d[x].color[0] = s[x].color[0];
4475 d[x].color[1] = s[x].color[1];
4476 d[x].control_row[0] = s[x].control_row[3];
4477 d[x].control_row[1] = s[x].control_row[2];
4478 d[x].control_row[2] = s[x].control_row[1];
4479 d[x].control_row[3] = s[x].control_row[0];
4481 src_row -= src_pitch;
4482 dst_row += dst_pitch;
4484 return WINED3D_OK;
4486 default:
4487 FIXME("Compressed flip not implemented for format %s.\n",
4488 debug_d3dformat(format->id));
4489 return E_NOTIMPL;
4493 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
4494 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
4496 return E_NOTIMPL;
4499 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4500 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4501 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4503 const struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
4504 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
4505 const struct wined3d_format *src_format, *dst_format;
4506 unsigned int src_fmt_flags, dst_fmt_flags;
4507 struct wined3d_texture *src_texture = NULL;
4508 struct wined3d_map_desc dst_map, src_map;
4509 const BYTE *sbase = NULL;
4510 HRESULT hr = WINED3D_OK;
4511 const BYTE *sbuf;
4512 BYTE *dbuf;
4513 int x, y;
4515 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4516 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4517 flags, fx, debug_d3dtexturefiltertype(filter));
4519 if (src_surface == dst_surface)
4521 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
4522 src_map = dst_map;
4523 src_format = dst_surface->resource.format;
4524 dst_format = src_format;
4525 dst_fmt_flags = dst_surface->container->resource.format_flags;
4526 src_fmt_flags = dst_fmt_flags;
4528 else
4530 dst_format = dst_surface->resource.format;
4531 dst_fmt_flags = dst_surface->container->resource.format_flags;
4532 if (src_surface)
4534 if (dst_surface->resource.format->id != src_surface->resource.format->id)
4536 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
4538 /* The conv function writes a FIXME */
4539 WARN("Cannot convert source surface format to dest format.\n");
4540 goto release;
4542 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
4544 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
4545 src_format = src_surface->resource.format;
4546 src_fmt_flags = src_surface->container->resource.format_flags;
4548 else
4550 src_format = dst_format;
4551 src_fmt_flags = dst_fmt_flags;
4554 wined3d_surface_map(dst_surface, &dst_map, &dst_box, 0);
4557 bpp = dst_surface->resource.format->byte_count;
4558 srcheight = src_rect->bottom - src_rect->top;
4559 srcwidth = src_rect->right - src_rect->left;
4560 dstheight = dst_rect->bottom - dst_rect->top;
4561 dstwidth = dst_rect->right - dst_rect->left;
4562 width = (dst_rect->right - dst_rect->left) * bpp;
4564 if (src_surface)
4565 sbase = (BYTE *)src_map.data
4566 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
4567 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
4568 if (src_surface != dst_surface)
4569 dbuf = dst_map.data;
4570 else
4571 dbuf = (BYTE *)dst_map.data
4572 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
4573 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
4575 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
4577 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
4579 if (src_surface == dst_surface)
4581 FIXME("Only plain blits supported on compressed surfaces.\n");
4582 hr = E_NOTIMPL;
4583 goto release;
4586 if (srcheight != dstheight || srcwidth != dstwidth)
4588 WARN("Stretching not supported on compressed surfaces.\n");
4589 hr = WINED3DERR_INVALIDCALL;
4590 goto release;
4593 if (!surface_check_block_align_rect(src_surface, src_rect))
4595 WARN("Source rectangle not block-aligned.\n");
4596 hr = WINED3DERR_INVALIDCALL;
4597 goto release;
4600 if (!surface_check_block_align_rect(dst_surface, dst_rect))
4602 WARN("Destination rectangle not block-aligned.\n");
4603 hr = WINED3DERR_INVALIDCALL;
4604 goto release;
4607 hr = surface_cpu_blt_compressed(sbase, dbuf,
4608 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
4609 src_format, flags, fx);
4610 goto release;
4613 /* First, all the 'source-less' blits */
4614 if (flags & WINEDDBLT_COLORFILL)
4616 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
4617 flags &= ~WINEDDBLT_COLORFILL;
4620 if (flags & WINEDDBLT_DEPTHFILL)
4622 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
4624 if (flags & WINEDDBLT_DDROPS)
4626 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
4628 /* Now the 'with source' blits. */
4629 if (src_surface)
4631 int sx, xinc, sy, yinc;
4633 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
4634 goto release;
4636 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
4637 && (srcwidth != dstwidth || srcheight != dstheight))
4639 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
4640 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
4643 xinc = (srcwidth << 16) / dstwidth;
4644 yinc = (srcheight << 16) / dstheight;
4646 if (!flags)
4648 /* No effects, we can cheat here. */
4649 if (dstwidth == srcwidth)
4651 if (dstheight == srcheight)
4653 /* No stretching in either direction. This needs to be as
4654 * fast as possible. */
4655 sbuf = sbase;
4657 /* Check for overlapping surfaces. */
4658 if (src_surface != dst_surface || dst_rect->top < src_rect->top
4659 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
4661 /* No overlap, or dst above src, so copy from top downwards. */
4662 for (y = 0; y < dstheight; ++y)
4664 memcpy(dbuf, sbuf, width);
4665 sbuf += src_map.row_pitch;
4666 dbuf += dst_map.row_pitch;
4669 else if (dst_rect->top > src_rect->top)
4671 /* Copy from bottom upwards. */
4672 sbuf += src_map.row_pitch * dstheight;
4673 dbuf += dst_map.row_pitch * dstheight;
4674 for (y = 0; y < dstheight; ++y)
4676 sbuf -= src_map.row_pitch;
4677 dbuf -= dst_map.row_pitch;
4678 memcpy(dbuf, sbuf, width);
4681 else
4683 /* Src and dst overlapping on the same line, use memmove. */
4684 for (y = 0; y < dstheight; ++y)
4686 memmove(dbuf, sbuf, width);
4687 sbuf += src_map.row_pitch;
4688 dbuf += dst_map.row_pitch;
4692 else
4694 /* Stretching in y direction only. */
4695 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4697 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4698 memcpy(dbuf, sbuf, width);
4699 dbuf += dst_map.row_pitch;
4703 else
4705 /* Stretching in X direction. */
4706 int last_sy = -1;
4707 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4709 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4711 if ((sy >> 16) == (last_sy >> 16))
4713 /* This source row is the same as last source row -
4714 * Copy the already stretched row. */
4715 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
4717 else
4719 #define STRETCH_ROW(type) \
4720 do { \
4721 const type *s = (const type *)sbuf; \
4722 type *d = (type *)dbuf; \
4723 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4724 d[x] = s[sx >> 16]; \
4725 } while(0)
4727 switch(bpp)
4729 case 1:
4730 STRETCH_ROW(BYTE);
4731 break;
4732 case 2:
4733 STRETCH_ROW(WORD);
4734 break;
4735 case 4:
4736 STRETCH_ROW(DWORD);
4737 break;
4738 case 3:
4740 const BYTE *s;
4741 BYTE *d = dbuf;
4742 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
4744 DWORD pixel;
4746 s = sbuf + 3 * (sx >> 16);
4747 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4748 d[0] = (pixel ) & 0xff;
4749 d[1] = (pixel >> 8) & 0xff;
4750 d[2] = (pixel >> 16) & 0xff;
4751 d += 3;
4753 break;
4755 default:
4756 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
4757 hr = WINED3DERR_NOTAVAILABLE;
4758 goto error;
4760 #undef STRETCH_ROW
4762 dbuf += dst_map.row_pitch;
4763 last_sy = sy;
4767 else
4769 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
4770 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
4771 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
4772 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
4774 /* The color keying flags are checked for correctness in ddraw */
4775 if (flags & WINEDDBLT_KEYSRC)
4777 keylow = src_surface->container->async.src_blt_color_key.color_space_low_value;
4778 keyhigh = src_surface->container->async.src_blt_color_key.color_space_high_value;
4780 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4782 keylow = fx->ddckSrcColorkey.color_space_low_value;
4783 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
4786 if (flags & WINEDDBLT_KEYDEST)
4788 /* Destination color keys are taken from the source surface! */
4789 destkeylow = src_surface->container->async.dst_blt_color_key.color_space_low_value;
4790 destkeyhigh = src_surface->container->async.dst_blt_color_key.color_space_high_value;
4792 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
4794 destkeylow = fx->ddckDestColorkey.color_space_low_value;
4795 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
4798 if (bpp == 1)
4800 keymask = 0xff;
4802 else
4804 DWORD masks[3];
4805 get_color_masks(src_format, masks);
4806 keymask = masks[0]
4807 | masks[1]
4808 | masks[2];
4810 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
4813 if (flags & WINEDDBLT_DDFX)
4815 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
4816 LONG tmpxy;
4817 dTopLeft = dbuf;
4818 dTopRight = dbuf + ((dstwidth - 1) * bpp);
4819 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
4820 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
4822 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
4824 /* I don't think we need to do anything about this flag */
4825 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
4827 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
4829 tmp = dTopRight;
4830 dTopRight = dTopLeft;
4831 dTopLeft = tmp;
4832 tmp = dBottomRight;
4833 dBottomRight = dBottomLeft;
4834 dBottomLeft = tmp;
4835 dstxinc = dstxinc * -1;
4837 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
4839 tmp = dTopLeft;
4840 dTopLeft = dBottomLeft;
4841 dBottomLeft = tmp;
4842 tmp = dTopRight;
4843 dTopRight = dBottomRight;
4844 dBottomRight = tmp;
4845 dstyinc = dstyinc * -1;
4847 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
4849 /* I don't think we need to do anything about this flag */
4850 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
4852 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
4854 tmp = dBottomRight;
4855 dBottomRight = dTopLeft;
4856 dTopLeft = tmp;
4857 tmp = dBottomLeft;
4858 dBottomLeft = dTopRight;
4859 dTopRight = tmp;
4860 dstxinc = dstxinc * -1;
4861 dstyinc = dstyinc * -1;
4863 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
4865 tmp = dTopLeft;
4866 dTopLeft = dBottomLeft;
4867 dBottomLeft = dBottomRight;
4868 dBottomRight = dTopRight;
4869 dTopRight = tmp;
4870 tmpxy = dstxinc;
4871 dstxinc = dstyinc;
4872 dstyinc = tmpxy;
4873 dstxinc = dstxinc * -1;
4875 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
4877 tmp = dTopLeft;
4878 dTopLeft = dTopRight;
4879 dTopRight = dBottomRight;
4880 dBottomRight = dBottomLeft;
4881 dBottomLeft = tmp;
4882 tmpxy = dstxinc;
4883 dstxinc = dstyinc;
4884 dstyinc = tmpxy;
4885 dstyinc = dstyinc * -1;
4887 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
4889 /* I don't think we need to do anything about this flag */
4890 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
4892 dbuf = dTopLeft;
4893 flags &= ~(WINEDDBLT_DDFX);
4896 #define COPY_COLORKEY_FX(type) \
4897 do { \
4898 const type *s; \
4899 type *d = (type *)dbuf, *dx, tmp; \
4900 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
4902 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
4903 dx = d; \
4904 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4906 tmp = s[sx >> 16]; \
4907 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
4908 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
4910 dx[0] = tmp; \
4912 dx = (type *)(((BYTE *)dx) + dstxinc); \
4914 d = (type *)(((BYTE *)d) + dstyinc); \
4916 } while(0)
4918 switch (bpp)
4920 case 1:
4921 COPY_COLORKEY_FX(BYTE);
4922 break;
4923 case 2:
4924 COPY_COLORKEY_FX(WORD);
4925 break;
4926 case 4:
4927 COPY_COLORKEY_FX(DWORD);
4928 break;
4929 case 3:
4931 const BYTE *s;
4932 BYTE *d = dbuf, *dx;
4933 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4935 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4936 dx = d;
4937 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
4939 DWORD pixel, dpixel = 0;
4940 s = sbuf + 3 * (sx>>16);
4941 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4942 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
4943 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
4944 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
4946 dx[0] = (pixel ) & 0xff;
4947 dx[1] = (pixel >> 8) & 0xff;
4948 dx[2] = (pixel >> 16) & 0xff;
4950 dx += dstxinc;
4952 d += dstyinc;
4954 break;
4956 default:
4957 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
4958 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
4959 hr = WINED3DERR_NOTAVAILABLE;
4960 goto error;
4961 #undef COPY_COLORKEY_FX
4966 error:
4967 if (flags && FIXME_ON(d3d_surface))
4969 FIXME("\tUnsupported flags: %#x.\n", flags);
4972 release:
4973 wined3d_surface_unmap(dst_surface);
4974 if (src_surface && src_surface != dst_surface)
4975 wined3d_surface_unmap(src_surface);
4976 /* Release the converted surface, if any. */
4977 if (src_texture)
4978 wined3d_texture_decref(src_texture);
4980 return hr;
4983 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4984 const RECT *dst_rect, const struct wined3d_color *color)
4986 static const RECT src_rect;
4987 WINEDDBLTFX BltFx;
4989 memset(&BltFx, 0, sizeof(BltFx));
4990 BltFx.dwSize = sizeof(BltFx);
4991 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
4992 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
4993 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
4996 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
4997 struct wined3d_surface *surface, const RECT *rect, float depth)
4999 FIXME("Depth filling not implemented by cpu_blit.\n");
5000 return WINED3DERR_INVALIDCALL;
5003 static void cpu_blit_blit_surface(struct wined3d_device *device, DWORD filter,
5004 struct wined3d_surface *src_surface, const RECT *src_rect,
5005 struct wined3d_surface *dst_surface, const RECT *dst_rect,
5006 const struct wined3d_color_key *color_key)
5008 /* FIXME: Remove error returns from surface_blt_cpu. */
5009 ERR("Blit method not implemented by cpu_blit.\n");
5012 const struct blit_shader cpu_blit = {
5013 cpu_blit_alloc,
5014 cpu_blit_free,
5015 cpu_blit_set,
5016 cpu_blit_unset,
5017 cpu_blit_supported,
5018 cpu_blit_color_fill,
5019 cpu_blit_depth_fill,
5020 cpu_blit_blit_surface,
5023 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5024 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5025 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5027 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5028 struct wined3d_device *device = dst_surface->resource.device;
5029 DWORD src_ds_flags, dst_ds_flags;
5030 RECT src_rect, dst_rect;
5031 BOOL scale, convert;
5033 static const DWORD simple_blit = WINEDDBLT_ASYNC
5034 | WINEDDBLT_COLORFILL
5035 | WINEDDBLT_KEYSRC
5036 | WINEDDBLT_KEYSRCOVERRIDE
5037 | WINEDDBLT_WAIT
5038 | WINEDDBLT_DEPTHFILL
5039 | WINEDDBLT_DONOTWAIT;
5041 TRACE("dst_surface %p, dst_rect_in %s, src_surface %p, src_rect_in %s, flags %#x, fx %p, filter %s.\n",
5042 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
5043 flags, fx, debug_d3dtexturefiltertype(filter));
5044 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5046 if (fx)
5048 TRACE("dwSize %#x.\n", fx->dwSize);
5049 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5050 TRACE("dwROP %#x.\n", fx->dwROP);
5051 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5052 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5053 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5054 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5055 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5056 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5057 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5058 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5059 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5060 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5061 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5062 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5063 TRACE("dwReserved %#x.\n", fx->dwReserved);
5064 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5065 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5066 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5067 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5068 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5069 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5070 fx->ddckDestColorkey.color_space_low_value,
5071 fx->ddckDestColorkey.color_space_high_value);
5072 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5073 fx->ddckSrcColorkey.color_space_low_value,
5074 fx->ddckSrcColorkey.color_space_high_value);
5077 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5079 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5080 return WINEDDERR_SURFACEBUSY;
5083 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5085 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5086 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5087 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5088 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5089 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5091 WARN("The application gave us a bad destination rectangle.\n");
5092 return WINEDDERR_INVALIDRECT;
5095 if (src_surface)
5097 surface_get_rect(src_surface, src_rect_in, &src_rect);
5099 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5100 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5101 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5102 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5103 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5105 WARN("Application gave us bad source rectangle for Blt.\n");
5106 return WINEDDERR_INVALIDRECT;
5109 else
5111 memset(&src_rect, 0, sizeof(src_rect));
5114 if (!fx || !(fx->dwDDFX))
5115 flags &= ~WINEDDBLT_DDFX;
5117 if (flags & WINEDDBLT_WAIT)
5118 flags &= ~WINEDDBLT_WAIT;
5120 if (flags & WINEDDBLT_ASYNC)
5122 static unsigned int once;
5124 if (!once++)
5125 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5126 flags &= ~WINEDDBLT_ASYNC;
5129 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5130 if (flags & WINEDDBLT_DONOTWAIT)
5132 static unsigned int once;
5134 if (!once++)
5135 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5136 flags &= ~WINEDDBLT_DONOTWAIT;
5139 if (!device->d3d_initialized)
5141 WARN("D3D not initialized, using fallback.\n");
5142 goto cpu;
5145 /* We want to avoid invalidating the sysmem location for converted
5146 * surfaces, since otherwise we'd have to convert the data back when
5147 * locking them. */
5148 if (dst_surface->container->flags & WINED3D_TEXTURE_CONVERTED
5149 || dst_surface->container->resource.format->convert
5150 || wined3d_format_get_color_key_conversion(dst_surface->container, TRUE))
5152 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5153 goto cpu;
5156 if (flags & ~simple_blit)
5158 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5159 goto fallback;
5162 if (src_surface)
5163 src_swapchain = src_surface->container->swapchain;
5164 else
5165 src_swapchain = NULL;
5167 dst_swapchain = dst_surface->container->swapchain;
5169 /* This isn't strictly needed. FBO blits for example could deal with
5170 * cross-swapchain blits by first downloading the source to a texture
5171 * before switching to the destination context. We just have this here to
5172 * not have to deal with the issue, since cross-swapchain blits should be
5173 * rare. */
5174 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5176 FIXME("Using fallback for cross-swapchain blit.\n");
5177 goto fallback;
5180 scale = src_surface
5181 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5182 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5183 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5185 dst_ds_flags = dst_surface->container->resource.format_flags
5186 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5187 if (src_surface)
5188 src_ds_flags = src_surface->container->resource.format_flags
5189 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5190 else
5191 src_ds_flags = 0;
5193 if (src_ds_flags || dst_ds_flags)
5195 if (flags & WINEDDBLT_DEPTHFILL)
5197 float depth;
5199 TRACE("Depth fill.\n");
5201 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5202 return WINED3DERR_INVALIDCALL;
5204 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
5205 return WINED3D_OK;
5207 else
5209 if (src_ds_flags != dst_ds_flags)
5211 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
5212 return WINED3DERR_INVALIDCALL;
5215 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
5216 &src_rect, dst_surface, dst_surface->container->resource.draw_binding, &dst_rect)))
5217 return WINED3D_OK;
5220 else
5222 const struct blit_shader *blitter;
5224 /* In principle this would apply to depth blits as well, but we don't
5225 * implement those in the CPU blitter at the moment. */
5226 if ((dst_surface->locations & dst_surface->resource.map_binding)
5227 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
5229 if (scale)
5230 TRACE("Not doing sysmem blit because of scaling.\n");
5231 else if (convert)
5232 TRACE("Not doing sysmem blit because of format conversion.\n");
5233 else
5234 goto cpu;
5237 if (flags & WINEDDBLT_COLORFILL)
5239 struct wined3d_color color;
5240 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
5242 TRACE("Color fill.\n");
5244 if (!wined3d_format_convert_color_to_float(dst_surface->resource.format,
5245 palette, fx->u5.dwFillColor, &color))
5246 goto fallback;
5248 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
5249 return WINED3D_OK;
5251 else
5253 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
5254 const struct wined3d_color_key *color_key = NULL;
5256 TRACE("Color blit.\n");
5257 if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5259 color_key = &fx->ddckSrcColorkey;
5260 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5262 else if (flags & WINEDDBLT_KEYSRC)
5264 color_key = &src_surface->container->async.src_blt_color_key;
5265 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5267 else if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5268 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5270 /* Upload */
5271 if (scale)
5272 TRACE("Not doing upload because of scaling.\n");
5273 else if (convert)
5274 TRACE("Not doing upload because of format conversion.\n");
5275 else
5277 POINT dst_point = {dst_rect.left, dst_rect.top};
5279 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
5281 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
5283 struct wined3d_context *context = context_acquire(device, dst_surface);
5284 surface_load_location(dst_surface, context, dst_surface->container->resource.draw_binding);
5285 context_release(context);
5287 return WINED3D_OK;
5291 else if (dst_swapchain && dst_swapchain->back_buffers
5292 && dst_surface->container == dst_swapchain->front_buffer
5293 && src_surface->container == dst_swapchain->back_buffers[0])
5295 /* Use present for back -> front blits. The idea behind this is
5296 * that present is potentially faster than a blit, in particular
5297 * when FBO blits aren't available. Some ddraw applications like
5298 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5299 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5300 * applications can't blit directly to the frontbuffer. */
5301 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5303 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5305 /* Set the swap effect to COPY, we don't want the backbuffer
5306 * to become undefined. */
5307 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5308 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5309 dst_swapchain->desc.swap_effect = swap_effect;
5311 return WINED3D_OK;
5314 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
5315 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5316 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5318 struct wined3d_context *context;
5319 TRACE("Using FBO blit.\n");
5321 context = context_acquire(device, NULL);
5322 surface_blt_fbo(device, context, filter,
5323 src_surface, src_surface->container->resource.draw_binding, &src_rect,
5324 dst_surface, dst_surface->container->resource.draw_binding, &dst_rect);
5325 context_release(context);
5327 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5328 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5330 return WINED3D_OK;
5333 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
5334 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5335 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format);
5336 if (blitter)
5338 blitter->blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect, color_key);
5339 return WINED3D_OK;
5344 fallback:
5345 /* Special cases for render targets. */
5346 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
5347 return WINED3D_OK;
5349 cpu:
5351 /* For the rest call the X11 surface implementation. For render targets
5352 * this should be implemented OpenGL accelerated in surface_blt_special(),
5353 * other blits are rather rare. */
5354 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
5357 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5358 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5360 struct wined3d_device *device = container->resource.device;
5361 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5362 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5363 UINT multisample_quality = desc->multisample_quality;
5364 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
5365 unsigned int resource_size;
5366 HRESULT hr;
5368 /* Quick lockable sanity check.
5369 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5370 * this function is too deep to need to care about things like this.
5371 * Levels need to be checked too, since they all affect what can be done. */
5372 switch (desc->pool)
5374 case WINED3D_POOL_MANAGED:
5375 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5376 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5377 break;
5379 case WINED3D_POOL_DEFAULT:
5380 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5381 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5382 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5383 break;
5385 case WINED3D_POOL_SCRATCH:
5386 case WINED3D_POOL_SYSTEM_MEM:
5387 break;
5389 default:
5390 FIXME("Unknown pool %#x.\n", desc->pool);
5391 break;
5394 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5395 FIXME("Trying to create a render target that isn't in the default pool.\n");
5397 /* FIXME: Check that the format is supported by the device. */
5399 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5400 if (!resource_size)
5401 return WINED3DERR_INVALIDCALL;
5403 if (device->wined3d->flags & WINED3D_NO3D)
5404 surface->surface_ops = &gdi_surface_ops;
5405 else
5406 surface->surface_ops = &surface_ops;
5408 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE,
5409 format, desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height,
5410 1, resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5412 WARN("Failed to initialize resource, returning %#x.\n", hr);
5413 return hr;
5416 surface->container = container;
5417 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5418 list_init(&surface->renderbuffers);
5419 list_init(&surface->overlays);
5421 /* Flags */
5422 if (flags & WINED3D_SURFACE_DISCARD)
5423 surface->flags |= SFLAG_DISCARD;
5424 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5425 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5427 surface->texture_target = target;
5428 surface->texture_level = level;
5429 surface->texture_layer = layer;
5431 /* Call the private setup routine */
5432 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5434 ERR("Private setup failed, hr %#x.\n", hr);
5435 surface_cleanup(surface);
5436 return hr;
5439 /* Similar to lockable rendertargets above, creating the DIB section
5440 * during surface initialization prevents the sysmem pointer from changing
5441 * after a wined3d_surface_getdc() call. */
5442 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5443 && SUCCEEDED(surface_create_dib_section(surface)))
5444 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5446 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5448 wined3d_resource_free_sysmem(&surface->resource);
5449 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5450 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5453 return hr;
5456 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5457 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
5459 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
5460 const struct wined3d_parent_ops *parent_ops;
5461 struct wined3d_surface *object;
5462 void *parent;
5463 HRESULT hr;
5465 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
5466 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
5467 container, desc->width, desc->height, debug_d3dformat(desc->format),
5468 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
5469 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
5471 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
5472 return E_OUTOFMEMORY;
5474 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
5476 WARN("Failed to initialize surface, returning %#x.\n", hr);
5477 HeapFree(GetProcessHeap(), 0, object);
5478 return hr;
5481 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
5482 container, layer * container->level_count + level, object, &parent, &parent_ops)))
5484 WARN("Failed to create surface parent, hr %#x.\n", hr);
5485 wined3d_surface_destroy(object);
5486 return hr;
5489 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
5491 object->resource.parent = parent;
5492 object->resource.parent_ops = parent_ops;
5493 *surface = object;
5495 return hr;
5498 /* Context activation is done by the caller. */
5499 void wined3d_surface_prepare(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5501 switch (location)
5503 case WINED3D_LOCATION_TEXTURE_RGB:
5504 wined3d_texture_prepare_texture(surface->container, context, FALSE);
5505 break;
5507 case WINED3D_LOCATION_TEXTURE_SRGB:
5508 wined3d_texture_prepare_texture(surface->container, context, TRUE);
5509 break;
5511 case WINED3D_LOCATION_RB_MULTISAMPLE:
5512 surface_prepare_rb(surface, context->gl_info, TRUE);
5513 break;
5515 case WINED3D_LOCATION_RB_RESOLVED:
5516 surface_prepare_rb(surface, context->gl_info, FALSE);
5517 break;