riched20: Implement IsEqual() for ranges.
[wine.git] / dlls / wined3d / surface.c
blob359db9b1f3ec0619c246ae441a9bcec32f13eac6
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] = ((1 << format->red_size) - 1) << format->red_offset;
355 masks[1] = ((1 << format->green_size) - 1) << format->green_offset;
356 masks[2] = ((1 << 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->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) * (1 << (format->byte_count * 8)));
392 break;
395 if (!b_info)
396 return E_OUTOFMEMORY;
398 /* Some applications access the surface in via DWORDs, and do not take
399 * the necessary care at the end of the surface. So we need at least
400 * 4 extra bytes at the end of the surface. Check against the page size,
401 * if the last page used for the surface has at least 4 spare bytes we're
402 * safe, otherwise add an extra line to the DIB section. */
403 GetSystemInfo(&sysInfo);
404 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
406 extraline = 1;
407 TRACE("Adding an extra line to the DIB section.\n");
410 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
411 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
412 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
413 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
414 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
415 * wined3d_surface_get_pitch(surface);
416 b_info->bmiHeader.biPlanes = 1;
417 b_info->bmiHeader.biBitCount = format->byte_count * 8;
419 b_info->bmiHeader.biXPelsPerMeter = 0;
420 b_info->bmiHeader.biYPelsPerMeter = 0;
421 b_info->bmiHeader.biClrUsed = 0;
422 b_info->bmiHeader.biClrImportant = 0;
424 /* Get the bit masks */
425 masks = (DWORD *)b_info->bmiColors;
426 switch (surface->resource.format->id)
428 case WINED3DFMT_B8G8R8_UNORM:
429 b_info->bmiHeader.biCompression = BI_RGB;
430 break;
432 case WINED3DFMT_B5G5R5X1_UNORM:
433 case WINED3DFMT_B5G5R5A1_UNORM:
434 case WINED3DFMT_B4G4R4A4_UNORM:
435 case WINED3DFMT_B4G4R4X4_UNORM:
436 case WINED3DFMT_B2G3R3_UNORM:
437 case WINED3DFMT_B2G3R3A8_UNORM:
438 case WINED3DFMT_R10G10B10A2_UNORM:
439 case WINED3DFMT_R8G8B8A8_UNORM:
440 case WINED3DFMT_R8G8B8X8_UNORM:
441 case WINED3DFMT_B10G10R10A2_UNORM:
442 case WINED3DFMT_B5G6R5_UNORM:
443 case WINED3DFMT_R16G16B16A16_UNORM:
444 b_info->bmiHeader.biCompression = BI_BITFIELDS;
445 get_color_masks(format, masks);
446 break;
448 default:
449 /* Don't know palette */
450 b_info->bmiHeader.biCompression = BI_RGB;
451 break;
454 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
455 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
456 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
457 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
459 if (!surface->dib.DIBsection)
461 ERR("Failed to create DIB section.\n");
462 HeapFree(GetProcessHeap(), 0, b_info);
463 return HRESULT_FROM_WIN32(GetLastError());
466 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
467 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
469 HeapFree(GetProcessHeap(), 0, b_info);
471 /* Now allocate a DC. */
472 surface->hDC = CreateCompatibleDC(0);
473 SelectObject(surface->hDC, surface->dib.DIBsection);
475 surface->flags |= SFLAG_DIBSECTION;
477 return WINED3D_OK;
480 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
481 DWORD location)
483 if (location & WINED3D_LOCATION_BUFFER)
485 data->addr = NULL;
486 data->buffer_object = surface->pbo;
487 return;
489 if (location & WINED3D_LOCATION_USER_MEMORY)
491 data->addr = surface->user_memory;
492 data->buffer_object = 0;
493 return;
495 if (location & WINED3D_LOCATION_DIB)
497 data->addr = surface->dib.bitmap_data;
498 data->buffer_object = 0;
499 return;
501 if (location & WINED3D_LOCATION_SYSMEM)
503 data->addr = surface->resource.heap_memory;
504 data->buffer_object = 0;
505 return;
508 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
509 data->addr = NULL;
510 data->buffer_object = 0;
513 static void surface_prepare_buffer(struct wined3d_surface *surface)
515 struct wined3d_context *context;
516 GLenum error;
517 const struct wined3d_gl_info *gl_info;
519 if (surface->pbo)
520 return;
522 context = context_acquire(surface->resource.device, NULL);
523 gl_info = context->gl_info;
525 GL_EXTCALL(glGenBuffers(1, &surface->pbo));
526 error = gl_info->gl_ops.gl.p_glGetError();
527 if (!surface->pbo || error != GL_NO_ERROR)
528 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
530 TRACE("Binding PBO %u.\n", surface->pbo);
532 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
533 checkGLcall("glBindBuffer");
535 GL_EXTCALL(glBufferData(GL_PIXEL_UNPACK_BUFFER, surface->resource.size + 4,
536 NULL, GL_STREAM_DRAW));
537 checkGLcall("glBufferData");
539 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
540 checkGLcall("glBindBuffer");
542 context_release(context);
545 static void surface_prepare_system_memory(struct wined3d_surface *surface)
547 TRACE("surface %p.\n", surface);
549 if (surface->resource.heap_memory)
550 return;
552 /* Whatever surface we have, make sure that there is memory allocated
553 * for the downloaded copy, or a PBO to map. */
554 if (!wined3d_resource_allocate_sysmem(&surface->resource))
555 ERR("Failed to allocate system memory.\n");
557 if (surface->locations & WINED3D_LOCATION_SYSMEM)
558 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
561 void surface_prepare_map_memory(struct wined3d_surface *surface)
563 switch (surface->resource.map_binding)
565 case WINED3D_LOCATION_SYSMEM:
566 surface_prepare_system_memory(surface);
567 break;
569 case WINED3D_LOCATION_USER_MEMORY:
570 if (!surface->user_memory)
571 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
572 break;
574 case WINED3D_LOCATION_DIB:
575 if (!surface->dib.bitmap_data)
576 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
577 break;
579 case WINED3D_LOCATION_BUFFER:
580 surface_prepare_buffer(surface);
581 break;
583 default:
584 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
588 static void surface_evict_sysmem(struct wined3d_surface *surface)
590 /* In some conditions the surface memory must not be freed:
591 * WINED3D_TEXTURE_CONVERTED: Converting the data back would take too long
592 * WINED3D_TEXTURE_DYNAMIC_MAP: Avoid freeing the data for performance
593 * SFLAG_CLIENT: OpenGL uses our memory as backup */
594 if (surface->resource.map_count || surface->flags & SFLAG_CLIENT
595 || surface->container->flags & (WINED3D_TEXTURE_CONVERTED | WINED3D_TEXTURE_PIN_SYSMEM
596 | WINED3D_TEXTURE_DYNAMIC_MAP))
597 return;
599 wined3d_resource_free_sysmem(&surface->resource);
600 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
603 static void surface_release_client_storage(struct wined3d_surface *surface)
605 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
606 const struct wined3d_gl_info *gl_info = context->gl_info;
608 if (surface->container->texture_rgb.name)
610 wined3d_texture_bind_and_dirtify(surface->container, context, FALSE);
611 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
612 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
614 if (surface->container->texture_srgb.name)
616 wined3d_texture_bind_and_dirtify(surface->container, context, TRUE);
617 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
618 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
620 wined3d_texture_force_reload(surface->container);
622 context_release(context);
625 static BOOL surface_use_pbo(const struct wined3d_surface *surface)
627 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
628 struct wined3d_texture *texture = surface->container;
630 return texture->resource.pool == WINED3D_POOL_DEFAULT
631 && surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU
632 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
633 && !texture->resource.format->convert
634 && !(texture->flags & WINED3D_TEXTURE_PIN_SYSMEM)
635 && !(surface->flags & SFLAG_NONPOW2);
638 static HRESULT surface_private_setup(struct wined3d_surface *surface)
640 /* TODO: Check against the maximum texture sizes supported by the video card. */
641 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
642 unsigned int pow2Width, pow2Height;
644 TRACE("surface %p.\n", surface);
646 /* Non-power2 support */
647 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
648 || gl_info->supported[ARB_TEXTURE_RECTANGLE])
650 pow2Width = surface->resource.width;
651 pow2Height = surface->resource.height;
653 else
655 /* Find the nearest pow2 match */
656 pow2Width = pow2Height = 1;
657 while (pow2Width < surface->resource.width)
658 pow2Width <<= 1;
659 while (pow2Height < surface->resource.height)
660 pow2Height <<= 1;
662 surface->pow2Width = pow2Width;
663 surface->pow2Height = pow2Height;
665 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
667 /* TODO: Add support for non power two compressed textures. */
668 if (surface->resource.format_flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE))
670 FIXME("(%p) Compressed or height scaled non-power-two textures are not supported w(%d) h(%d)\n",
671 surface, surface->resource.width, surface->resource.height);
672 return WINED3DERR_NOTAVAILABLE;
676 if (pow2Width != surface->resource.width
677 || pow2Height != surface->resource.height)
679 surface->flags |= SFLAG_NONPOW2;
682 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
683 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
685 /* One of three options:
686 * 1: Do the same as we do with NPOT and scale the texture, (any
687 * texture ops would require the texture to be scaled which is
688 * potentially slow)
689 * 2: Set the texture to the maximum size (bad idea).
690 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
691 * 4: Create the surface, but allow it to be used only for DirectDraw
692 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
693 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
694 * the render target. */
695 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
697 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
698 return WINED3DERR_NOTAVAILABLE;
701 /* We should never use this surface in combination with OpenGL! */
702 TRACE("Creating an oversized surface: %ux%u.\n",
703 surface->pow2Width, surface->pow2Height);
706 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
707 surface->locations = WINED3D_LOCATION_DISCARDED;
709 if (surface_use_pbo(surface))
710 surface->resource.map_binding = WINED3D_LOCATION_BUFFER;
712 return WINED3D_OK;
715 static void surface_unmap(struct wined3d_surface *surface)
717 struct wined3d_device *device = surface->resource.device;
718 const struct wined3d_gl_info *gl_info;
719 struct wined3d_context *context;
721 TRACE("surface %p.\n", surface);
723 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
725 switch (surface->resource.map_binding)
727 case WINED3D_LOCATION_SYSMEM:
728 case WINED3D_LOCATION_USER_MEMORY:
729 case WINED3D_LOCATION_DIB:
730 break;
732 case WINED3D_LOCATION_BUFFER:
733 context = context_acquire(device, NULL);
734 gl_info = context->gl_info;
736 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
737 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
738 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
739 checkGLcall("glUnmapBuffer");
740 context_release(context);
741 break;
743 default:
744 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
747 if (surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_TEXTURE_RGB))
749 TRACE("Not dirtified, nothing to do.\n");
750 return;
753 if (surface->container->swapchain && surface->container->swapchain->front_buffer == surface->container)
754 surface_load_location(surface, surface->container->resource.draw_binding);
755 else if (surface->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
756 FIXME("Depth / stencil buffer locking is not implemented.\n");
759 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
761 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
762 return FALSE;
763 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
764 return FALSE;
765 return TRUE;
768 static void surface_depth_blt_fbo(const struct wined3d_device *device,
769 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
770 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
772 const struct wined3d_gl_info *gl_info;
773 struct wined3d_context *context;
774 DWORD src_mask, dst_mask;
775 GLbitfield gl_mask;
777 TRACE("device %p\n", device);
778 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
779 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
780 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
781 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
783 src_mask = src_surface->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
784 dst_mask = dst_surface->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
786 if (src_mask != dst_mask)
788 ERR("Incompatible formats %s and %s.\n",
789 debug_d3dformat(src_surface->resource.format->id),
790 debug_d3dformat(dst_surface->resource.format->id));
791 return;
794 if (!src_mask)
796 ERR("Not a depth / stencil format: %s.\n",
797 debug_d3dformat(src_surface->resource.format->id));
798 return;
801 gl_mask = 0;
802 if (src_mask & WINED3DFMT_FLAG_DEPTH)
803 gl_mask |= GL_DEPTH_BUFFER_BIT;
804 if (src_mask & WINED3DFMT_FLAG_STENCIL)
805 gl_mask |= GL_STENCIL_BUFFER_BIT;
807 /* Make sure the locations are up-to-date. Loading the destination
808 * surface isn't required if the entire surface is overwritten. */
809 surface_load_location(src_surface, src_location);
810 if (!surface_is_full_rect(dst_surface, dst_rect))
811 surface_load_location(dst_surface, dst_location);
813 context = context_acquire(device, NULL);
814 if (!context->valid)
816 context_release(context);
817 WARN("Invalid context, skipping blit.\n");
818 return;
821 gl_info = context->gl_info;
823 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
824 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
826 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
827 context_set_draw_buffer(context, GL_NONE);
828 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
829 context_invalidate_state(context, STATE_FRAMEBUFFER);
831 if (gl_mask & GL_DEPTH_BUFFER_BIT)
833 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
834 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
836 if (gl_mask & GL_STENCIL_BUFFER_BIT)
838 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
840 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
841 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
843 gl_info->gl_ops.gl.p_glStencilMask(~0U);
844 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
847 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
848 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
850 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
851 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
852 checkGLcall("glBlitFramebuffer()");
854 if (wined3d_settings.strict_draw_ordering)
855 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
857 context_release(context);
860 /* Blit between surface locations. Onscreen on different swapchains is not supported.
861 * Depth / stencil is not supported. */
862 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
863 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
864 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
866 const struct wined3d_gl_info *gl_info;
867 struct wined3d_context *context;
868 RECT src_rect, dst_rect;
869 GLenum gl_filter;
870 GLenum buffer;
872 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
873 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
874 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
875 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
876 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
878 src_rect = *src_rect_in;
879 dst_rect = *dst_rect_in;
881 switch (filter)
883 case WINED3D_TEXF_LINEAR:
884 gl_filter = GL_LINEAR;
885 break;
887 default:
888 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
889 case WINED3D_TEXF_NONE:
890 case WINED3D_TEXF_POINT:
891 gl_filter = GL_NEAREST;
892 break;
895 /* Resolve the source surface first if needed. */
896 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
897 && (src_surface->resource.format->id != dst_surface->resource.format->id
898 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
899 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
900 src_location = WINED3D_LOCATION_RB_RESOLVED;
902 /* Make sure the locations are up-to-date. Loading the destination
903 * surface isn't required if the entire surface is overwritten. (And is
904 * in fact harmful if we're being called by surface_load_location() with
905 * the purpose of loading the destination surface.) */
906 surface_load_location(src_surface, src_location);
907 if (!surface_is_full_rect(dst_surface, &dst_rect))
908 surface_load_location(dst_surface, dst_location);
910 if (src_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, src_surface);
911 else if (dst_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, dst_surface);
912 else context = context_acquire(device, NULL);
914 if (!context->valid)
916 context_release(context);
917 WARN("Invalid context, skipping blit.\n");
918 return;
921 gl_info = context->gl_info;
923 if (src_location == WINED3D_LOCATION_DRAWABLE)
925 TRACE("Source surface %p is onscreen.\n", src_surface);
926 buffer = surface_get_gl_buffer(src_surface);
927 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
929 else
931 TRACE("Source surface %p is offscreen.\n", src_surface);
932 buffer = GL_COLOR_ATTACHMENT0;
935 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
936 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
937 checkGLcall("glReadBuffer()");
938 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
940 if (dst_location == WINED3D_LOCATION_DRAWABLE)
942 TRACE("Destination surface %p is onscreen.\n", dst_surface);
943 buffer = surface_get_gl_buffer(dst_surface);
944 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
946 else
948 TRACE("Destination surface %p is offscreen.\n", dst_surface);
949 buffer = GL_COLOR_ATTACHMENT0;
952 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
953 context_set_draw_buffer(context, buffer);
954 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
955 context_invalidate_state(context, STATE_FRAMEBUFFER);
957 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
958 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
959 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
960 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
961 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
963 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
964 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
966 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
967 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
968 checkGLcall("glBlitFramebuffer()");
970 if (wined3d_settings.strict_draw_ordering
971 || (dst_location == WINED3D_LOCATION_DRAWABLE
972 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
973 gl_info->gl_ops.gl.p_glFlush();
975 context_release(context);
978 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
979 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
980 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
982 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
983 return FALSE;
985 /* Source and/or destination need to be on the GL side */
986 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
987 return FALSE;
989 switch (blit_op)
991 case WINED3D_BLIT_OP_COLOR_BLIT:
992 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
993 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
994 return FALSE;
995 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
996 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
997 return FALSE;
998 break;
1000 case WINED3D_BLIT_OP_DEPTH_BLIT:
1001 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1002 return FALSE;
1003 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1004 return FALSE;
1005 break;
1007 default:
1008 return FALSE;
1011 if (!(src_format->id == dst_format->id
1012 || (is_identity_fixup(src_format->color_fixup)
1013 && is_identity_fixup(dst_format->color_fixup))))
1014 return FALSE;
1016 return TRUE;
1019 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1021 const struct wined3d_format *format = surface->resource.format;
1023 switch (format->id)
1025 case WINED3DFMT_S1_UINT_D15_UNORM:
1026 *float_depth = depth / (float)0x00007fff;
1027 break;
1029 case WINED3DFMT_D16_UNORM:
1030 *float_depth = depth / (float)0x0000ffff;
1031 break;
1033 case WINED3DFMT_D24_UNORM_S8_UINT:
1034 case WINED3DFMT_X8D24_UNORM:
1035 *float_depth = depth / (float)0x00ffffff;
1036 break;
1038 case WINED3DFMT_D32_UNORM:
1039 *float_depth = depth / (float)0xffffffff;
1040 break;
1042 default:
1043 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1044 return FALSE;
1047 return TRUE;
1050 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1052 const struct wined3d_resource *resource = &surface->container->resource;
1053 struct wined3d_device *device = resource->device;
1054 const struct blit_shader *blitter;
1056 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, WINED3D_BLIT_OP_DEPTH_FILL,
1057 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1058 if (!blitter)
1060 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1061 return WINED3DERR_INVALIDCALL;
1064 return blitter->depth_fill(device, surface, rect, depth);
1067 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1068 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1070 struct wined3d_device *device = src_surface->resource.device;
1072 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1073 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1074 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1075 return WINED3DERR_INVALIDCALL;
1077 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1079 surface_modify_ds_location(dst_surface, dst_location,
1080 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1082 return WINED3D_OK;
1085 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1086 struct wined3d_surface *render_target)
1088 TRACE("surface %p, render_target %p.\n", surface, render_target);
1090 /* TODO: Check surface sizes, pools, etc. */
1092 if (render_target->resource.multisample_type)
1093 return WINED3DERR_INVALIDCALL;
1095 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1098 /* Context activation is done by the caller. */
1099 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1101 GL_EXTCALL(glDeleteBuffers(1, &surface->pbo));
1102 checkGLcall("glDeleteBuffers(1, &surface->pbo)");
1104 surface->pbo = 0;
1105 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1108 static ULONG surface_resource_incref(struct wined3d_resource *resource)
1110 return wined3d_surface_incref(surface_from_resource(resource));
1113 static ULONG surface_resource_decref(struct wined3d_resource *resource)
1115 return wined3d_surface_decref(surface_from_resource(resource));
1118 static void surface_unload(struct wined3d_resource *resource)
1120 struct wined3d_surface *surface = surface_from_resource(resource);
1121 struct wined3d_renderbuffer_entry *entry, *entry2;
1122 struct wined3d_device *device = resource->device;
1123 const struct wined3d_gl_info *gl_info;
1124 struct wined3d_context *context;
1126 TRACE("surface %p.\n", surface);
1128 if (resource->pool == WINED3D_POOL_DEFAULT)
1130 /* Default pool resources are supposed to be destroyed before Reset is called.
1131 * Implicit resources stay however. So this means we have an implicit render target
1132 * or depth stencil. The content may be destroyed, but we still have to tear down
1133 * opengl resources, so we cannot leave early.
1135 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1136 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1137 * or the depth stencil into an FBO the texture or render buffer will be removed
1138 * and all flags get lost */
1139 surface_prepare_system_memory(surface);
1140 memset(surface->resource.heap_memory, 0, surface->resource.size);
1141 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1142 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1144 /* We also get here when the ddraw swapchain is destroyed, for example
1145 * for a mode switch. In this case this surface won't necessarily be
1146 * an implicit surface. We have to mark it lost so that the
1147 * application can restore it after the mode switch. */
1148 surface->flags |= SFLAG_LOST;
1150 else
1152 surface_prepare_map_memory(surface);
1153 surface_load_location(surface, surface->resource.map_binding);
1154 surface_invalidate_location(surface, ~surface->resource.map_binding);
1157 context = context_acquire(device, NULL);
1158 gl_info = context->gl_info;
1160 /* Destroy PBOs, but load them into real sysmem before */
1161 if (surface->pbo)
1162 surface_remove_pbo(surface, gl_info);
1164 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1165 * all application-created targets the application has to release the surface
1166 * before calling _Reset
1168 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1170 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1171 list_remove(&entry->entry);
1172 HeapFree(GetProcessHeap(), 0, entry);
1174 list_init(&surface->renderbuffers);
1175 surface->current_renderbuffer = NULL;
1177 if (surface->rb_multisample)
1179 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1180 surface->rb_multisample = 0;
1182 if (surface->rb_resolved)
1184 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1185 surface->rb_resolved = 0;
1188 context_release(context);
1190 resource_unload(resource);
1193 static const struct wined3d_resource_ops surface_resource_ops =
1195 surface_resource_incref,
1196 surface_resource_decref,
1197 surface_unload,
1200 static const struct wined3d_surface_ops surface_ops =
1202 surface_private_setup,
1203 surface_unmap,
1206 /*****************************************************************************
1207 * Initializes the GDI surface, aka creates the DIB section we render to
1208 * The DIB section creation is done by calling GetDC, which will create the
1209 * section and releasing the dc to allow the app to use it. The dib section
1210 * will stay until the surface is released
1212 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1213 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1214 * avoid confusion in the shared surface code.
1216 * Returns:
1217 * WINED3D_OK on success
1218 * The return values of called methods on failure
1220 *****************************************************************************/
1221 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1223 HRESULT hr;
1225 TRACE("surface %p.\n", surface);
1227 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1229 ERR("Overlays not yet supported by GDI surfaces.\n");
1230 return WINED3DERR_INVALIDCALL;
1233 /* Sysmem textures have memory already allocated - release it,
1234 * this avoids an unnecessary memcpy. */
1235 hr = surface_create_dib_section(surface);
1236 if (FAILED(hr))
1237 return hr;
1238 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1240 /* We don't mind the nonpow2 stuff in GDI. */
1241 surface->pow2Width = surface->resource.width;
1242 surface->pow2Height = surface->resource.height;
1244 return WINED3D_OK;
1247 static void gdi_surface_unmap(struct wined3d_surface *surface)
1249 TRACE("surface %p.\n", surface);
1251 /* Tell the swapchain to update the screen. */
1252 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
1253 x11_copy_to_screen(surface->container->swapchain, &surface->lockedRect);
1255 memset(&surface->lockedRect, 0, sizeof(RECT));
1258 static const struct wined3d_surface_ops gdi_surface_ops =
1260 gdi_surface_private_setup,
1261 gdi_surface_unmap,
1264 /* This call just downloads data, the caller is responsible for binding the
1265 * correct texture. */
1266 /* Context activation is done by the caller. */
1267 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1268 DWORD dst_location)
1270 const struct wined3d_format *format = surface->resource.format;
1271 struct wined3d_bo_address data;
1273 /* Only support read back of converted P8 surfaces. */
1274 if (surface->container->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1276 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1277 return;
1280 surface_get_memory(surface, &data, dst_location);
1282 if (surface->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
1284 TRACE("(%p) : Calling glGetCompressedTexImage level %d, format %#x, type %#x, data %p.\n",
1285 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1287 if (data.buffer_object)
1289 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1290 checkGLcall("glBindBuffer");
1291 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, NULL));
1292 checkGLcall("glGetCompressedTexImage");
1293 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1294 checkGLcall("glBindBuffer");
1296 else
1298 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target,
1299 surface->texture_level, data.addr));
1300 checkGLcall("glGetCompressedTexImage");
1303 else
1305 void *mem;
1306 GLenum gl_format = format->glFormat;
1307 GLenum gl_type = format->glType;
1308 int src_pitch = 0;
1309 int dst_pitch = 0;
1311 if (surface->flags & SFLAG_NONPOW2)
1313 unsigned char alignment = surface->resource.device->surface_alignment;
1314 src_pitch = format->byte_count * surface->pow2Width;
1315 dst_pitch = wined3d_surface_get_pitch(surface);
1316 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1317 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1319 else
1321 mem = data.addr;
1324 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1325 surface, surface->texture_level, gl_format, gl_type, mem);
1327 if (data.buffer_object)
1329 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1330 checkGLcall("glBindBuffer");
1332 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1333 gl_format, gl_type, NULL);
1334 checkGLcall("glGetTexImage");
1336 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1337 checkGLcall("glBindBuffer");
1339 else
1341 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1342 gl_format, gl_type, mem);
1343 checkGLcall("glGetTexImage");
1346 if (surface->flags & SFLAG_NONPOW2)
1348 const BYTE *src_data;
1349 BYTE *dst_data;
1350 UINT y;
1352 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1353 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1354 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1356 * We're doing this...
1358 * instead of boxing the texture :
1359 * |<-texture width ->| -->pow2width| /\
1360 * |111111111111111111| | |
1361 * |222 Texture 222222| boxed empty | texture height
1362 * |3333 Data 33333333| | |
1363 * |444444444444444444| | \/
1364 * ----------------------------------- |
1365 * | boxed empty | boxed empty | pow2height
1366 * | | | \/
1367 * -----------------------------------
1370 * we're repacking the data to the expected texture width
1372 * |<-texture width ->| -->pow2width| /\
1373 * |111111111111111111222222222222222| |
1374 * |222333333333333333333444444444444| texture height
1375 * |444444 | |
1376 * | | \/
1377 * | | |
1378 * | empty | pow2height
1379 * | | \/
1380 * -----------------------------------
1382 * == is the same as
1384 * |<-texture width ->| /\
1385 * |111111111111111111|
1386 * |222222222222222222|texture height
1387 * |333333333333333333|
1388 * |444444444444444444| \/
1389 * --------------------
1391 * This also means that any references to surface memory should work with the data as if it were a
1392 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1394 * internally the texture is still stored in a boxed format so any references to textureName will
1395 * get a boxed texture with width pow2width and not a texture of width resource.width.
1397 * Performance should not be an issue, because applications normally do not lock the surfaces when
1398 * rendering. If an app does, the WINED3D_TEXTURE_DYNAMIC_MAP flag will kick in and the memory copy
1399 * won't be released, and doesn't have to be re-read. */
1400 src_data = mem;
1401 dst_data = data.addr;
1402 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1403 for (y = 0; y < surface->resource.height; ++y)
1405 memcpy(dst_data, src_data, dst_pitch);
1406 src_data += src_pitch;
1407 dst_data += dst_pitch;
1410 HeapFree(GetProcessHeap(), 0, mem);
1415 /* This call just uploads data, the caller is responsible for binding the
1416 * correct texture. */
1417 /* Context activation is done by the caller. */
1418 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1419 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1420 BOOL srgb, const struct wined3d_const_bo_address *data)
1422 UINT update_w = src_rect->right - src_rect->left;
1423 UINT update_h = src_rect->bottom - src_rect->top;
1425 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1426 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1427 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1429 if (surface->resource.map_count)
1431 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
1432 surface->container->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
1435 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
1437 update_h *= format->height_scale.numerator;
1438 update_h /= format->height_scale.denominator;
1441 if (data->buffer_object)
1443 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
1444 checkGLcall("glBindBuffer");
1447 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
1449 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1450 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1451 const BYTE *addr = data->addr;
1452 GLenum internal;
1454 addr += (src_rect->top / format->block_height) * src_pitch;
1455 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1457 if (srgb)
1458 internal = format->glGammaInternal;
1459 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1460 && wined3d_resource_is_offscreen(&surface->container->resource))
1461 internal = format->rtInternal;
1462 else
1463 internal = format->glInternal;
1465 TRACE("glCompressedTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, "
1466 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1467 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1469 if (row_length == src_pitch)
1471 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1472 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1474 else
1476 UINT row, y;
1478 /* glCompressedTexSubImage2D() ignores pixel store state, so we
1479 * can't use the unpack row length like for glTexSubImage2D. */
1480 for (row = 0, y = dst_point->y; row < row_count; ++row)
1482 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1483 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1484 y += format->block_height;
1485 addr += src_pitch;
1488 checkGLcall("glCompressedTexSubImage2D");
1490 else
1492 const BYTE *addr = data->addr;
1494 addr += src_rect->top * src_pitch;
1495 addr += src_rect->left * format->byte_count;
1497 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1498 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1499 update_w, update_h, format->glFormat, format->glType, addr);
1501 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1502 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1503 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1504 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1505 checkGLcall("glTexSubImage2D");
1508 if (data->buffer_object)
1510 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1511 checkGLcall("glBindBuffer");
1514 if (wined3d_settings.strict_draw_ordering)
1515 gl_info->gl_ops.gl.p_glFlush();
1517 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1519 struct wined3d_device *device = surface->resource.device;
1520 unsigned int i;
1522 for (i = 0; i < device->context_count; ++i)
1524 context_surface_update(device->contexts[i], surface);
1529 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
1531 UINT width_mask, height_mask;
1533 if (!rect->left && !rect->top
1534 && rect->right == surface->resource.width
1535 && rect->bottom == surface->resource.height)
1536 return TRUE;
1538 /* This assumes power of two block sizes, but NPOT block sizes would be
1539 * silly anyway. */
1540 width_mask = surface->resource.format->block_width - 1;
1541 height_mask = surface->resource.format->block_height - 1;
1543 if (!(rect->left & width_mask) && !(rect->top & height_mask)
1544 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
1545 return TRUE;
1547 return FALSE;
1550 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1551 struct wined3d_surface *src_surface, const RECT *src_rect)
1553 const struct wined3d_format *src_format;
1554 const struct wined3d_format *dst_format;
1555 unsigned int src_fmt_flags, dst_fmt_flags;
1556 const struct wined3d_gl_info *gl_info;
1557 struct wined3d_context *context;
1558 struct wined3d_bo_address data;
1559 UINT update_w, update_h;
1560 UINT dst_w, dst_h;
1561 RECT r, dst_rect;
1562 UINT src_pitch;
1563 POINT p;
1565 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1566 dst_surface, wine_dbgstr_point(dst_point),
1567 src_surface, wine_dbgstr_rect(src_rect));
1569 src_format = src_surface->resource.format;
1570 dst_format = dst_surface->resource.format;
1571 src_fmt_flags = src_surface->resource.format_flags;
1572 dst_fmt_flags = dst_surface->resource.format_flags;
1574 if (src_format->id != dst_format->id)
1576 WARN("Source and destination surfaces should have the same format.\n");
1577 return WINED3DERR_INVALIDCALL;
1580 if (!dst_point)
1582 p.x = 0;
1583 p.y = 0;
1584 dst_point = &p;
1586 else if (dst_point->x < 0 || dst_point->y < 0)
1588 WARN("Invalid destination point.\n");
1589 return WINED3DERR_INVALIDCALL;
1592 if (!src_rect)
1594 r.left = 0;
1595 r.top = 0;
1596 r.right = src_surface->resource.width;
1597 r.bottom = src_surface->resource.height;
1598 src_rect = &r;
1600 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1601 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1603 WARN("Invalid source rectangle.\n");
1604 return WINED3DERR_INVALIDCALL;
1607 dst_w = dst_surface->resource.width;
1608 dst_h = dst_surface->resource.height;
1610 update_w = src_rect->right - src_rect->left;
1611 update_h = src_rect->bottom - src_rect->top;
1613 if (update_w > dst_w || dst_point->x > dst_w - update_w
1614 || update_h > dst_h || dst_point->y > dst_h - update_h)
1616 WARN("Destination out of bounds.\n");
1617 return WINED3DERR_INVALIDCALL;
1620 if ((src_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
1622 WARN("Source rectangle not block-aligned.\n");
1623 return WINED3DERR_INVALIDCALL;
1626 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1627 if ((dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
1629 WARN("Destination rectangle not block-aligned.\n");
1630 return WINED3DERR_INVALIDCALL;
1633 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1634 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_surface->container, FALSE))
1635 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1637 context = context_acquire(dst_surface->resource.device, NULL);
1638 gl_info = context->gl_info;
1640 /* Only load the surface for partial updates. For newly allocated texture
1641 * the texture wouldn't be the current location, and we'd upload zeroes
1642 * just to overwrite them again. */
1643 if (update_w == dst_w && update_h == dst_h)
1644 wined3d_texture_prepare_texture(dst_surface->container, context, FALSE);
1645 else
1646 surface_load_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1647 wined3d_texture_bind_and_dirtify(dst_surface->container, context, FALSE);
1649 surface_get_memory(src_surface, &data, src_surface->locations);
1650 src_pitch = wined3d_surface_get_pitch(src_surface);
1652 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1653 src_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1655 context_release(context);
1657 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1658 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1660 return WINED3D_OK;
1663 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1664 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1665 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1666 /* Context activation is done by the caller. */
1667 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1669 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1670 struct wined3d_renderbuffer_entry *entry;
1671 GLuint renderbuffer = 0;
1672 unsigned int src_width, src_height;
1673 unsigned int width, height;
1675 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1677 width = rt->pow2Width;
1678 height = rt->pow2Height;
1680 else
1682 width = surface->pow2Width;
1683 height = surface->pow2Height;
1686 src_width = surface->pow2Width;
1687 src_height = surface->pow2Height;
1689 /* A depth stencil smaller than the render target is not valid */
1690 if (width > src_width || height > src_height) return;
1692 /* Remove any renderbuffer set if the sizes match */
1693 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1694 || (width == src_width && height == src_height))
1696 surface->current_renderbuffer = NULL;
1697 return;
1700 /* Look if we've already got a renderbuffer of the correct dimensions */
1701 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1703 if (entry->width == width && entry->height == height)
1705 renderbuffer = entry->id;
1706 surface->current_renderbuffer = entry;
1707 break;
1711 if (!renderbuffer)
1713 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1714 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1715 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1716 surface->resource.format->glInternal, width, height);
1718 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1719 entry->width = width;
1720 entry->height = height;
1721 entry->id = renderbuffer;
1722 list_add_head(&surface->renderbuffers, &entry->entry);
1724 surface->current_renderbuffer = entry;
1727 checkGLcall("set_compatible_renderbuffer");
1730 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1732 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1734 TRACE("surface %p.\n", surface);
1736 if (!swapchain)
1738 ERR("Surface %p is not on a swapchain.\n", surface);
1739 return GL_NONE;
1742 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
1744 if (swapchain->render_to_fbo)
1746 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1747 return GL_COLOR_ATTACHMENT0;
1749 TRACE("Returning GL_BACK\n");
1750 return GL_BACK;
1752 else if (surface->container == swapchain->front_buffer)
1754 TRACE("Returning GL_FRONT\n");
1755 return GL_FRONT;
1758 FIXME("Higher back buffer, returning GL_BACK\n");
1759 return GL_BACK;
1762 void surface_load(struct wined3d_surface *surface, BOOL srgb)
1764 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1766 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1768 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
1769 ERR("Not supported on scratch surfaces.\n");
1771 if (surface->locations & location)
1773 TRACE("surface is already in texture\n");
1774 return;
1776 TRACE("Reloading because surface is dirty.\n");
1778 surface_load_location(surface, location);
1779 surface_evict_sysmem(surface);
1782 /* See also float_16_to_32() in wined3d_private.h */
1783 static inline unsigned short float_32_to_16(const float *in)
1785 int exp = 0;
1786 float tmp = fabsf(*in);
1787 unsigned int mantissa;
1788 unsigned short ret;
1790 /* Deal with special numbers */
1791 if (*in == 0.0f)
1792 return 0x0000;
1793 if (isnan(*in))
1794 return 0x7c01;
1795 if (isinf(*in))
1796 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1798 if (tmp < powf(2, 10))
1802 tmp = tmp * 2.0f;
1803 exp--;
1804 } while (tmp < powf(2, 10));
1806 else if (tmp >= powf(2, 11))
1810 tmp /= 2.0f;
1811 exp++;
1812 } while (tmp >= powf(2, 11));
1815 mantissa = (unsigned int)tmp;
1816 if (tmp - mantissa >= 0.5f)
1817 ++mantissa; /* Round to nearest, away from zero. */
1819 exp += 10; /* Normalize the mantissa. */
1820 exp += 15; /* Exponent is encoded with excess 15. */
1822 if (exp > 30) /* too big */
1824 ret = 0x7c00; /* INF */
1826 else if (exp <= 0)
1828 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1829 while (exp <= 0)
1831 mantissa = mantissa >> 1;
1832 ++exp;
1834 ret = mantissa & 0x3ff;
1836 else
1838 ret = (exp << 10) | (mantissa & 0x3ff);
1841 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1842 return ret;
1845 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
1847 TRACE("surface %p, container %p.\n", surface, surface->container);
1849 return wined3d_texture_incref(surface->container);
1852 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
1854 TRACE("surface %p, container %p.\n", surface, surface->container);
1856 return wined3d_texture_decref(surface->container);
1859 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
1861 TRACE("surface %p.\n", surface);
1863 if (!surface->resource.device->d3d_initialized)
1865 ERR("D3D not initialized.\n");
1866 return;
1869 wined3d_texture_preload(surface->container);
1872 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
1874 TRACE("surface %p.\n", surface);
1876 return surface->resource.parent;
1879 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
1881 TRACE("surface %p.\n", surface);
1883 return &surface->resource;
1886 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
1888 TRACE("surface %p, flags %#x.\n", surface, flags);
1890 switch (flags)
1892 case WINEDDGBS_CANBLT:
1893 case WINEDDGBS_ISBLTDONE:
1894 return WINED3D_OK;
1896 default:
1897 return WINED3DERR_INVALIDCALL;
1901 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
1903 TRACE("surface %p, flags %#x.\n", surface, flags);
1905 /* XXX: DDERR_INVALIDSURFACETYPE */
1907 switch (flags)
1909 case WINEDDGFS_CANFLIP:
1910 case WINEDDGFS_ISFLIPDONE:
1911 return WINED3D_OK;
1913 default:
1914 return WINED3DERR_INVALIDCALL;
1918 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
1920 TRACE("surface %p.\n", surface);
1922 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
1923 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
1926 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
1928 TRACE("surface %p.\n", surface);
1930 surface->flags &= ~SFLAG_LOST;
1931 return WINED3D_OK;
1934 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
1936 unsigned int alignment;
1937 DWORD pitch;
1939 TRACE("surface %p.\n", surface);
1941 if (surface->pitch)
1942 return surface->pitch;
1944 alignment = surface->resource.device->surface_alignment;
1945 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
1946 pitch = (pitch + alignment - 1) & ~(alignment - 1);
1948 TRACE("Returning %u.\n", pitch);
1950 return pitch;
1953 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
1955 LONG w, h;
1957 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
1959 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1961 WARN("Not an overlay surface.\n");
1962 return WINEDDERR_NOTAOVERLAYSURFACE;
1965 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
1966 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
1967 surface->overlay_destrect.left = x;
1968 surface->overlay_destrect.top = y;
1969 surface->overlay_destrect.right = x + w;
1970 surface->overlay_destrect.bottom = y + h;
1972 return WINED3D_OK;
1975 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
1977 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
1979 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1981 TRACE("Not an overlay surface.\n");
1982 return WINEDDERR_NOTAOVERLAYSURFACE;
1985 if (!surface->overlay_dest)
1987 TRACE("Overlay not visible.\n");
1988 *x = 0;
1989 *y = 0;
1990 return WINEDDERR_OVERLAYNOTVISIBLE;
1993 *x = surface->overlay_destrect.left;
1994 *y = surface->overlay_destrect.top;
1996 TRACE("Returning position %d, %d.\n", *x, *y);
1998 return WINED3D_OK;
2001 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2002 DWORD flags, struct wined3d_surface *ref)
2004 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2006 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2008 TRACE("Not an overlay surface.\n");
2009 return WINEDDERR_NOTAOVERLAYSURFACE;
2012 return WINED3D_OK;
2015 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2016 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2018 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2019 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2021 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2023 WARN("Not an overlay surface.\n");
2024 return WINEDDERR_NOTAOVERLAYSURFACE;
2026 else if (!dst_surface)
2028 WARN("Dest surface is NULL.\n");
2029 return WINED3DERR_INVALIDCALL;
2032 if (src_rect)
2034 surface->overlay_srcrect = *src_rect;
2036 else
2038 surface->overlay_srcrect.left = 0;
2039 surface->overlay_srcrect.top = 0;
2040 surface->overlay_srcrect.right = surface->resource.width;
2041 surface->overlay_srcrect.bottom = surface->resource.height;
2044 if (dst_rect)
2046 surface->overlay_destrect = *dst_rect;
2048 else
2050 surface->overlay_destrect.left = 0;
2051 surface->overlay_destrect.top = 0;
2052 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2053 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2056 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2058 surface->overlay_dest = NULL;
2059 list_remove(&surface->overlay_entry);
2062 if (flags & WINEDDOVER_SHOW)
2064 if (surface->overlay_dest != dst_surface)
2066 surface->overlay_dest = dst_surface;
2067 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2070 else if (flags & WINEDDOVER_HIDE)
2072 /* tests show that the rectangles are erased on hide */
2073 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2074 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2075 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2076 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2077 surface->overlay_dest = NULL;
2080 return WINED3D_OK;
2083 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface,
2084 const struct wined3d_gl_info *gl_info, void *mem, unsigned int pitch)
2086 struct wined3d_resource *texture_resource = &surface->container->resource;
2087 unsigned int width, height;
2088 BOOL create_dib = FALSE;
2089 DWORD valid_location = 0;
2090 HRESULT hr;
2092 if (surface->flags & SFLAG_DIBSECTION)
2094 DeleteDC(surface->hDC);
2095 DeleteObject(surface->dib.DIBsection);
2096 surface->dib.bitmap_data = NULL;
2097 surface->flags &= ~SFLAG_DIBSECTION;
2098 create_dib = TRUE;
2101 surface->locations = 0;
2102 wined3d_resource_free_sysmem(&surface->resource);
2104 width = texture_resource->width;
2105 height = texture_resource->height;
2106 surface->resource.width = width;
2107 surface->resource.height = height;
2108 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2109 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2111 surface->pow2Width = width;
2112 surface->pow2Height = height;
2114 else
2116 surface->pow2Width = surface->pow2Height = 1;
2117 while (surface->pow2Width < width)
2118 surface->pow2Width <<= 1;
2119 while (surface->pow2Height < height)
2120 surface->pow2Height <<= 1;
2123 if (surface->pow2Width != width || surface->pow2Height != height)
2124 surface->flags |= SFLAG_NONPOW2;
2125 else
2126 surface->flags &= ~SFLAG_NONPOW2;
2128 if ((surface->user_memory = mem))
2130 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
2131 valid_location = WINED3D_LOCATION_USER_MEMORY;
2133 surface->pitch = pitch;
2134 surface->resource.format = texture_resource->format;
2135 surface->resource.multisample_type = texture_resource->multisample_type;
2136 surface->resource.multisample_quality = texture_resource->multisample_quality;
2137 if (surface->pitch)
2139 surface->resource.size = height * surface->pitch;
2141 else
2143 /* User memory surfaces don't have the regular surface alignment. */
2144 surface->resource.size = wined3d_format_calculate_size(texture_resource->format,
2145 1, width, height, 1);
2146 surface->pitch = wined3d_format_calculate_pitch(texture_resource->format, width);
2149 /* The format might be changed to a format that needs conversion.
2150 * If the surface didn't use PBOs previously but could now, don't
2151 * change it - whatever made us not use PBOs might come back, e.g.
2152 * color keys. */
2153 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2154 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2156 if (create_dib)
2158 if (FAILED(hr = surface_create_dib_section(surface)))
2160 ERR("Failed to create dib section, hr %#x.\n", hr);
2161 return hr;
2163 if (!valid_location)
2164 valid_location = WINED3D_LOCATION_DIB;
2167 if (!valid_location)
2169 surface_prepare_system_memory(surface);
2170 valid_location = WINED3D_LOCATION_SYSMEM;
2173 surface_validate_location(surface, valid_location);
2175 return WINED3D_OK;
2178 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2179 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2181 unsigned short *dst_s;
2182 const float *src_f;
2183 unsigned int x, y;
2185 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2187 for (y = 0; y < h; ++y)
2189 src_f = (const float *)(src + y * pitch_in);
2190 dst_s = (unsigned short *) (dst + y * pitch_out);
2191 for (x = 0; x < w; ++x)
2193 dst_s[x] = float_32_to_16(src_f + x);
2198 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2199 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2201 static const unsigned char convert_5to8[] =
2203 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2204 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2205 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2206 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2208 static const unsigned char convert_6to8[] =
2210 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2211 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2212 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2213 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2214 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2215 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2216 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2217 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2219 unsigned int x, y;
2221 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2223 for (y = 0; y < h; ++y)
2225 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2226 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2227 for (x = 0; x < w; ++x)
2229 WORD pixel = src_line[x];
2230 dst_line[x] = 0xff000000
2231 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2232 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2233 | convert_5to8[(pixel & 0x001f)];
2238 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2239 * in both cases we're just setting the X / Alpha channel to 0xff. */
2240 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2241 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2243 unsigned int x, y;
2245 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2247 for (y = 0; y < h; ++y)
2249 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2250 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2252 for (x = 0; x < w; ++x)
2254 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2259 static inline BYTE cliptobyte(int x)
2261 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2264 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2265 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2267 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2268 unsigned int x, y;
2270 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2272 for (y = 0; y < h; ++y)
2274 const BYTE *src_line = src + y * pitch_in;
2275 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2276 for (x = 0; x < w; ++x)
2278 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2279 * C = Y - 16; D = U - 128; E = V - 128;
2280 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2281 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2282 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2283 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2284 * U and V are shared between the pixels. */
2285 if (!(x & 1)) /* For every even pixel, read new U and V. */
2287 d = (int) src_line[1] - 128;
2288 e = (int) src_line[3] - 128;
2289 r2 = 409 * e + 128;
2290 g2 = - 100 * d - 208 * e + 128;
2291 b2 = 516 * d + 128;
2293 c2 = 298 * ((int) src_line[0] - 16);
2294 dst_line[x] = 0xff000000
2295 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2296 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2297 | cliptobyte((c2 + b2) >> 8); /* blue */
2298 /* Scale RGB values to 0..255 range,
2299 * then clip them if still not in range (may be negative),
2300 * then shift them within DWORD if necessary. */
2301 src_line += 2;
2306 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2307 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2309 unsigned int x, y;
2310 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2312 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2314 for (y = 0; y < h; ++y)
2316 const BYTE *src_line = src + y * pitch_in;
2317 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2318 for (x = 0; x < w; ++x)
2320 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2321 * C = Y - 16; D = U - 128; E = V - 128;
2322 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2323 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2324 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2325 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2326 * U and V are shared between the pixels. */
2327 if (!(x & 1)) /* For every even pixel, read new U and V. */
2329 d = (int) src_line[1] - 128;
2330 e = (int) src_line[3] - 128;
2331 r2 = 409 * e + 128;
2332 g2 = - 100 * d - 208 * e + 128;
2333 b2 = 516 * d + 128;
2335 c2 = 298 * ((int) src_line[0] - 16);
2336 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2337 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2338 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2339 /* Scale RGB values to 0..255 range,
2340 * then clip them if still not in range (may be negative),
2341 * then shift them within DWORD if necessary. */
2342 src_line += 2;
2347 struct d3dfmt_converter_desc
2349 enum wined3d_format_id from, to;
2350 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2353 static const struct d3dfmt_converter_desc converters[] =
2355 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2356 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2357 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2358 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2359 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2360 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2363 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2364 enum wined3d_format_id to)
2366 unsigned int i;
2368 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2370 if (converters[i].from == from && converters[i].to == to)
2371 return &converters[i];
2374 return NULL;
2377 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2379 struct wined3d_map_desc src_map, dst_map;
2380 const struct d3dfmt_converter_desc *conv;
2381 struct wined3d_texture *ret = NULL;
2382 struct wined3d_resource_desc desc;
2383 struct wined3d_surface *dst;
2385 conv = find_converter(source->resource.format->id, to_fmt);
2386 if (!conv)
2388 FIXME("Cannot find a conversion function from format %s to %s.\n",
2389 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2390 return NULL;
2393 /* FIXME: Multisampled conversion? */
2394 wined3d_resource_get_desc(&source->resource, &desc);
2395 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2396 desc.format = to_fmt;
2397 desc.usage = 0;
2398 desc.pool = WINED3D_POOL_SCRATCH;
2399 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2400 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, NULL, &wined3d_null_parent_ops, &ret)))
2402 ERR("Failed to create a destination surface for conversion.\n");
2403 return NULL;
2405 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2407 memset(&src_map, 0, sizeof(src_map));
2408 memset(&dst_map, 0, sizeof(dst_map));
2410 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2412 ERR("Failed to lock the source surface.\n");
2413 wined3d_texture_decref(ret);
2414 return NULL;
2416 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2418 ERR("Failed to lock the destination surface.\n");
2419 wined3d_surface_unmap(source);
2420 wined3d_texture_decref(ret);
2421 return NULL;
2424 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2425 source->resource.width, source->resource.height);
2427 wined3d_surface_unmap(dst);
2428 wined3d_surface_unmap(source);
2430 return ret;
2433 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2434 unsigned int bpp, UINT pitch, DWORD color)
2436 BYTE *first;
2437 unsigned int x, y;
2439 /* Do first row */
2441 #define COLORFILL_ROW(type) \
2442 do { \
2443 type *d = (type *)buf; \
2444 for (x = 0; x < width; ++x) \
2445 d[x] = (type)color; \
2446 } while(0)
2448 switch (bpp)
2450 case 1:
2451 COLORFILL_ROW(BYTE);
2452 break;
2454 case 2:
2455 COLORFILL_ROW(WORD);
2456 break;
2458 case 3:
2460 BYTE *d = buf;
2461 for (x = 0; x < width; ++x, d += 3)
2463 d[0] = (color ) & 0xff;
2464 d[1] = (color >> 8) & 0xff;
2465 d[2] = (color >> 16) & 0xff;
2467 break;
2469 case 4:
2470 COLORFILL_ROW(DWORD);
2471 break;
2473 default:
2474 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2475 return WINED3DERR_NOTAVAILABLE;
2478 #undef COLORFILL_ROW
2480 /* Now copy first row. */
2481 first = buf;
2482 for (y = 1; y < height; ++y)
2484 buf += pitch;
2485 memcpy(buf, first, width * bpp);
2488 return WINED3D_OK;
2491 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2493 return surface_from_resource(resource);
2496 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2498 TRACE("surface %p.\n", surface);
2500 if (!surface->resource.map_count)
2502 WARN("Trying to unmap unmapped surface.\n");
2503 return WINEDDERR_NOTLOCKED;
2505 --surface->resource.map_count;
2507 surface->surface_ops->surface_unmap(surface);
2509 return WINED3D_OK;
2512 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2513 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
2515 const struct wined3d_format *format = surface->resource.format;
2516 unsigned int fmt_flags = surface->resource.format_flags;
2517 struct wined3d_device *device = surface->resource.device;
2518 struct wined3d_context *context;
2519 const struct wined3d_gl_info *gl_info;
2520 BYTE *base_memory;
2522 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
2523 surface, map_desc, wine_dbgstr_rect(rect), flags);
2525 if (surface->resource.map_count)
2527 WARN("Surface is already mapped.\n");
2528 return WINED3DERR_INVALIDCALL;
2531 if ((fmt_flags & WINED3DFMT_FLAG_BLOCKS) && rect
2532 && !surface_check_block_align(surface, rect))
2534 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
2535 wine_dbgstr_rect(rect), format->block_width, format->block_height);
2537 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2538 return WINED3DERR_INVALIDCALL;
2541 ++surface->resource.map_count;
2543 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2544 WARN("Trying to lock unlockable surface.\n");
2546 /* Performance optimization: Count how often a surface is mapped, if it is
2547 * mapped regularly do not throw away the system memory copy. This avoids
2548 * the need to download the surface from OpenGL all the time. The surface
2549 * is still downloaded if the OpenGL texture is changed. Note that this
2550 * only really makes sense for managed textures.*/
2551 if (!(surface->container->flags & WINED3D_TEXTURE_DYNAMIC_MAP)
2552 && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2554 if (++surface->lockCount > MAXLOCKCOUNT)
2556 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2557 surface->container->flags |= WINED3D_TEXTURE_DYNAMIC_MAP;
2561 surface_prepare_map_memory(surface);
2562 if (flags & WINED3D_MAP_DISCARD)
2564 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2565 wined3d_debug_location(surface->resource.map_binding));
2566 surface_validate_location(surface, surface->resource.map_binding);
2568 else
2570 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2571 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2573 surface_load_location(surface, surface->resource.map_binding);
2576 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2577 surface_invalidate_location(surface, ~surface->resource.map_binding);
2579 switch (surface->resource.map_binding)
2581 case WINED3D_LOCATION_SYSMEM:
2582 base_memory = surface->resource.heap_memory;
2583 break;
2585 case WINED3D_LOCATION_USER_MEMORY:
2586 base_memory = surface->user_memory;
2587 break;
2589 case WINED3D_LOCATION_DIB:
2590 base_memory = surface->dib.bitmap_data;
2591 break;
2593 case WINED3D_LOCATION_BUFFER:
2594 context = context_acquire(device, NULL);
2595 gl_info = context->gl_info;
2597 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
2598 base_memory = GL_EXTCALL(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE));
2599 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
2600 checkGLcall("map PBO");
2602 context_release(context);
2603 break;
2605 default:
2606 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2607 base_memory = NULL;
2610 if (fmt_flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2611 map_desc->row_pitch = surface->resource.width * format->byte_count;
2612 else
2613 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2614 map_desc->slice_pitch = 0;
2616 if (!rect)
2618 map_desc->data = base_memory;
2619 surface->lockedRect.left = 0;
2620 surface->lockedRect.top = 0;
2621 surface->lockedRect.right = surface->resource.width;
2622 surface->lockedRect.bottom = surface->resource.height;
2624 else
2626 if ((fmt_flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2628 /* Compressed textures are block based, so calculate the offset of
2629 * the block that contains the top-left pixel of the locked rectangle. */
2630 map_desc->data = base_memory
2631 + ((rect->top / format->block_height) * map_desc->row_pitch)
2632 + ((rect->left / format->block_width) * format->block_byte_count);
2634 else
2636 map_desc->data = base_memory
2637 + (map_desc->row_pitch * rect->top)
2638 + (rect->left * format->byte_count);
2640 surface->lockedRect.left = rect->left;
2641 surface->lockedRect.top = rect->top;
2642 surface->lockedRect.right = rect->right;
2643 surface->lockedRect.bottom = rect->bottom;
2646 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2647 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2649 return WINED3D_OK;
2652 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
2654 HRESULT hr;
2656 TRACE("surface %p, dc %p.\n", surface, dc);
2658 /* Give more detailed info for ddraw. */
2659 if (surface->flags & SFLAG_DCINUSE)
2660 return WINEDDERR_DCALREADYCREATED;
2662 /* Can't GetDC if the surface is locked. */
2663 if (surface->resource.map_count)
2664 return WINED3DERR_INVALIDCALL;
2666 /* Create a DIB section if there isn't a dc yet. */
2667 if (!surface->hDC)
2669 if (surface->flags & SFLAG_CLIENT)
2671 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
2672 surface_release_client_storage(surface);
2674 hr = surface_create_dib_section(surface);
2675 if (FAILED(hr))
2676 return WINED3DERR_INVALIDCALL;
2677 if (!(surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2678 || surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2679 || surface->pbo))
2680 surface->resource.map_binding = WINED3D_LOCATION_DIB;
2683 surface_load_location(surface, WINED3D_LOCATION_DIB);
2684 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
2686 surface->flags |= SFLAG_DCINUSE;
2687 surface->resource.map_count++;
2689 *dc = surface->hDC;
2690 TRACE("Returning dc %p.\n", *dc);
2692 return WINED3D_OK;
2695 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
2697 TRACE("surface %p, dc %p.\n", surface, dc);
2699 if (!(surface->flags & SFLAG_DCINUSE))
2700 return WINEDDERR_NODC;
2702 if (surface->hDC != dc)
2704 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
2705 dc, surface->hDC);
2706 return WINEDDERR_NODC;
2709 surface->resource.map_count--;
2710 surface->flags &= ~SFLAG_DCINUSE;
2712 if (surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2713 || (surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2714 && surface->resource.map_binding != WINED3D_LOCATION_DIB))
2716 /* The game Salammbo modifies the surface contents without mapping the surface between
2717 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
2718 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
2719 * Do not only copy the DIB to the map location, but also make sure the map location is
2720 * copied back to the DIB in the next getdc call.
2722 * The same consideration applies to user memory surfaces. */
2723 surface_load_location(surface, surface->resource.map_binding);
2724 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
2727 return WINED3D_OK;
2730 static void read_from_framebuffer(struct wined3d_surface *surface, DWORD dst_location)
2732 struct wined3d_device *device = surface->resource.device;
2733 const struct wined3d_gl_info *gl_info;
2734 struct wined3d_context *context;
2735 BYTE *mem;
2736 BYTE *row, *top, *bottom;
2737 int i;
2738 BOOL srcIsUpsideDown;
2739 struct wined3d_bo_address data;
2741 surface_get_memory(surface, &data, dst_location);
2743 context = context_acquire(device, surface);
2744 context_apply_blit_state(context, device);
2745 gl_info = context->gl_info;
2747 /* Select the correct read buffer, and give some debug output.
2748 * There is no need to keep track of the current read buffer or reset it, every part of the code
2749 * that reads sets the read buffer as desired.
2751 if (wined3d_resource_is_offscreen(&surface->container->resource))
2753 /* Mapping the primary render target which is not on a swapchain.
2754 * Read from the back buffer. */
2755 TRACE("Mapping offscreen render target.\n");
2756 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2757 srcIsUpsideDown = TRUE;
2759 else
2761 /* Onscreen surfaces are always part of a swapchain */
2762 GLenum buffer = surface_get_gl_buffer(surface);
2763 TRACE("Mapping %#x buffer.\n", buffer);
2764 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2765 checkGLcall("glReadBuffer");
2766 srcIsUpsideDown = FALSE;
2769 if (data.buffer_object)
2771 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
2772 checkGLcall("glBindBuffer");
2775 /* Setup pixel store pack state -- to glReadPixels into the correct place */
2776 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH,
2777 wined3d_surface_get_pitch(surface) / surface->resource.format->byte_count);
2778 checkGLcall("glPixelStorei");
2780 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
2781 surface->resource.width, surface->resource.height,
2782 surface->resource.format->glFormat,
2783 surface->resource.format->glType, data.addr);
2784 checkGLcall("glReadPixels");
2786 /* Reset previous pixel store pack state */
2787 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
2788 checkGLcall("glPixelStorei");
2790 if (!srcIsUpsideDown)
2792 /* glReadPixels returns the image upside down, and there is no way to prevent this.
2793 * Flip the lines in software. */
2794 UINT pitch = wined3d_surface_get_pitch(surface);
2796 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
2797 goto error;
2799 if (data.buffer_object)
2801 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
2802 checkGLcall("glMapBuffer");
2804 else
2805 mem = data.addr;
2807 top = mem;
2808 bottom = mem + pitch * (surface->resource.height - 1);
2809 for (i = 0; i < surface->resource.height / 2; i++)
2811 memcpy(row, top, pitch);
2812 memcpy(top, bottom, pitch);
2813 memcpy(bottom, row, pitch);
2814 top += pitch;
2815 bottom -= pitch;
2817 HeapFree(GetProcessHeap(), 0, row);
2819 if (data.buffer_object)
2820 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
2823 error:
2824 if (data.buffer_object)
2826 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2827 checkGLcall("glBindBuffer");
2830 context_release(context);
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. */
2836 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
2838 struct wined3d_device *device = surface->resource.device;
2839 const struct wined3d_gl_info *gl_info;
2840 struct wined3d_context *context;
2842 context = context_acquire(device, surface);
2843 gl_info = context->gl_info;
2844 device_invalidate_state(device, STATE_FRAMEBUFFER);
2846 wined3d_texture_prepare_texture(surface->container, context, srgb);
2847 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
2849 TRACE("Reading back offscreen render target %p.\n", surface);
2851 if (wined3d_resource_is_offscreen(&surface->container->resource))
2852 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2853 else
2854 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
2855 checkGLcall("glReadBuffer");
2857 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
2858 0, 0, 0, 0, surface->resource.width, surface->resource.height);
2859 checkGLcall("glCopyTexSubImage2D");
2861 context_release(context);
2864 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2866 if (multisample)
2868 if (surface->rb_multisample)
2869 return;
2871 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2872 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2873 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
2874 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
2875 checkGLcall("glRenderbufferStorageMultisample()");
2876 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2878 else
2880 if (surface->rb_resolved)
2881 return;
2883 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2884 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2885 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
2886 surface->pow2Width, surface->pow2Height);
2887 checkGLcall("glRenderbufferStorage()");
2888 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2892 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
2894 if (front->container->level_count != 1 || front->container->layer_count != 1
2895 || back->container->level_count != 1 || back->container->layer_count != 1)
2896 ERR("Flip between surfaces %p and %p not supported.\n", front, back);
2898 /* Flip the surface contents */
2899 /* Flip the DC */
2901 HDC tmp;
2902 tmp = front->hDC;
2903 front->hDC = back->hDC;
2904 back->hDC = tmp;
2907 /* Flip the DIBsection */
2909 HBITMAP tmp = front->dib.DIBsection;
2910 front->dib.DIBsection = back->dib.DIBsection;
2911 back->dib.DIBsection = tmp;
2914 /* Flip the surface data */
2916 void* tmp;
2918 tmp = front->dib.bitmap_data;
2919 front->dib.bitmap_data = back->dib.bitmap_data;
2920 back->dib.bitmap_data = tmp;
2922 tmp = front->resource.heap_memory;
2923 front->resource.heap_memory = back->resource.heap_memory;
2924 back->resource.heap_memory = tmp;
2927 /* Flip the PBO */
2929 GLuint tmp_pbo = front->pbo;
2930 front->pbo = back->pbo;
2931 back->pbo = tmp_pbo;
2934 /* Flip the opengl texture */
2936 GLuint tmp;
2938 tmp = back->container->texture_rgb.name;
2939 back->container->texture_rgb.name = front->container->texture_rgb.name;
2940 front->container->texture_rgb.name = tmp;
2942 tmp = back->container->texture_srgb.name;
2943 back->container->texture_srgb.name = front->container->texture_srgb.name;
2944 front->container->texture_srgb.name = tmp;
2946 tmp = back->rb_multisample;
2947 back->rb_multisample = front->rb_multisample;
2948 front->rb_multisample = tmp;
2950 tmp = back->rb_resolved;
2951 back->rb_resolved = front->rb_resolved;
2952 front->rb_resolved = tmp;
2954 resource_unload(&back->resource);
2955 resource_unload(&front->resource);
2959 DWORD tmp_flags = back->flags;
2960 back->flags = front->flags;
2961 front->flags = tmp_flags;
2963 tmp_flags = back->locations;
2964 back->locations = front->locations;
2965 front->locations = tmp_flags;
2969 /* Does a direct frame buffer -> texture copy. Stretching is done with single
2970 * pixel copy calls. */
2971 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2972 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2974 struct wined3d_device *device = dst_surface->resource.device;
2975 const struct wined3d_gl_info *gl_info;
2976 float xrel, yrel;
2977 struct wined3d_context *context;
2978 BOOL upsidedown = FALSE;
2979 RECT dst_rect = *dst_rect_in;
2981 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2982 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2984 if(dst_rect.top > dst_rect.bottom) {
2985 UINT tmp = dst_rect.bottom;
2986 dst_rect.bottom = dst_rect.top;
2987 dst_rect.top = tmp;
2988 upsidedown = TRUE;
2991 context = context_acquire(device, src_surface);
2992 gl_info = context->gl_info;
2993 context_apply_blit_state(context, device);
2994 wined3d_texture_load(dst_surface->container, context, FALSE);
2996 /* Bind the target texture */
2997 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
2998 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
3000 TRACE("Reading from an offscreen target\n");
3001 upsidedown = !upsidedown;
3002 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3004 else
3006 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3008 checkGLcall("glReadBuffer");
3010 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3011 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3013 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3015 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3017 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3018 ERR("Texture filtering not supported in direct blit.\n");
3020 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3021 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3023 ERR("Texture filtering not supported in direct blit\n");
3026 if (upsidedown
3027 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3028 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3030 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
3031 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3032 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3033 src_rect->left, src_surface->resource.height - src_rect->bottom,
3034 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3036 else
3038 LONG row;
3039 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
3040 /* I have to process this row by row to swap the image,
3041 * otherwise it would be upside down, so stretching in y direction
3042 * doesn't cost extra time
3044 * However, stretching in x direction can be avoided if not necessary
3046 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3047 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3049 /* Well, that stuff works, but it's very slow.
3050 * find a better way instead
3052 LONG col;
3054 for (col = dst_rect.left; col < dst_rect.right; ++col)
3056 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3057 dst_rect.left + col /* x offset */, row /* y offset */,
3058 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3061 else
3063 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3064 dst_rect.left /* x offset */, row /* y offset */,
3065 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3069 checkGLcall("glCopyTexSubImage2D");
3071 context_release(context);
3073 /* The texture is now most up to date - If the surface is a render target
3074 * and has a drawable, this path is never entered. */
3075 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3076 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3079 /* Uses the hardware to stretch and flip the image */
3080 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3081 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3083 struct wined3d_device *device = dst_surface->resource.device;
3084 GLuint src, backup = 0;
3085 float left, right, top, bottom; /* Texture coordinates */
3086 UINT fbwidth = src_surface->resource.width;
3087 UINT fbheight = src_surface->resource.height;
3088 const struct wined3d_gl_info *gl_info;
3089 struct wined3d_context *context;
3090 GLenum drawBuffer = GL_BACK;
3091 GLenum texture_target;
3092 BOOL noBackBufferBackup;
3093 BOOL src_offscreen;
3094 BOOL upsidedown = FALSE;
3095 RECT dst_rect = *dst_rect_in;
3097 TRACE("Using hwstretch blit\n");
3098 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3099 context = context_acquire(device, src_surface);
3100 gl_info = context->gl_info;
3101 context_apply_blit_state(context, device);
3102 wined3d_texture_load(dst_surface->container, context, FALSE);
3104 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
3105 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3106 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3108 /* Get it a description */
3109 wined3d_texture_load(src_surface->container, context, FALSE);
3112 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3113 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3115 if (context->aux_buffers >= 2)
3117 /* Got more than one aux buffer? Use the 2nd aux buffer */
3118 drawBuffer = GL_AUX1;
3120 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3122 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3123 drawBuffer = GL_AUX0;
3126 if (noBackBufferBackup)
3128 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3129 checkGLcall("glGenTextures");
3130 context_bind_texture(context, GL_TEXTURE_2D, backup);
3131 texture_target = GL_TEXTURE_2D;
3133 else
3135 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3136 * we are reading from the back buffer, the backup can be used as source texture
3138 texture_target = src_surface->texture_target;
3139 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3140 gl_info->gl_ops.gl.p_glEnable(texture_target);
3141 checkGLcall("glEnable(texture_target)");
3143 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3144 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3147 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3148 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3150 if(dst_rect.top > dst_rect.bottom) {
3151 UINT tmp = dst_rect.bottom;
3152 dst_rect.bottom = dst_rect.top;
3153 dst_rect.top = tmp;
3154 upsidedown = TRUE;
3157 if (src_offscreen)
3159 TRACE("Reading from an offscreen target\n");
3160 upsidedown = !upsidedown;
3161 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3163 else
3165 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3168 /* TODO: Only back up the part that will be overwritten */
3169 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3171 checkGLcall("glCopyTexSubImage2D");
3173 /* No issue with overriding these - the sampler is dirty due to blit usage */
3174 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
3175 checkGLcall("glTexParameteri");
3176 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3177 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
3178 checkGLcall("glTexParameteri");
3180 if (!src_surface->container->swapchain
3181 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
3183 src = backup ? backup : src_surface->container->texture_rgb.name;
3185 else
3187 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3188 checkGLcall("glReadBuffer(GL_FRONT)");
3190 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3191 checkGLcall("glGenTextures(1, &src)");
3192 context_bind_texture(context, GL_TEXTURE_2D, src);
3194 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3195 * out for power of 2 sizes
3197 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3198 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3199 checkGLcall("glTexImage2D");
3200 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3202 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3203 checkGLcall("glTexParameteri");
3204 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3205 checkGLcall("glTexParameteri");
3207 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3208 checkGLcall("glReadBuffer(GL_BACK)");
3210 if (texture_target != GL_TEXTURE_2D)
3212 gl_info->gl_ops.gl.p_glDisable(texture_target);
3213 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3214 texture_target = GL_TEXTURE_2D;
3217 checkGLcall("glEnd and previous");
3219 left = src_rect->left;
3220 right = src_rect->right;
3222 if (!upsidedown)
3224 top = src_surface->resource.height - src_rect->top;
3225 bottom = src_surface->resource.height - src_rect->bottom;
3227 else
3229 top = src_surface->resource.height - src_rect->bottom;
3230 bottom = src_surface->resource.height - src_rect->top;
3233 if (src_surface->container->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
3235 left /= src_surface->pow2Width;
3236 right /= src_surface->pow2Width;
3237 top /= src_surface->pow2Height;
3238 bottom /= src_surface->pow2Height;
3241 /* draw the source texture stretched and upside down. The correct surface is bound already */
3242 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3243 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3245 context_set_draw_buffer(context, drawBuffer);
3246 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3248 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3249 /* bottom left */
3250 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3251 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3253 /* top left */
3254 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3255 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3257 /* top right */
3258 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3259 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3261 /* bottom right */
3262 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3263 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3264 gl_info->gl_ops.gl.p_glEnd();
3265 checkGLcall("glEnd and previous");
3267 if (texture_target != dst_surface->texture_target)
3269 gl_info->gl_ops.gl.p_glDisable(texture_target);
3270 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3271 texture_target = dst_surface->texture_target;
3274 /* Now read the stretched and upside down image into the destination texture */
3275 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
3276 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
3278 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3279 0, 0, /* We blitted the image to the origin */
3280 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3281 checkGLcall("glCopyTexSubImage2D");
3283 if (drawBuffer == GL_BACK)
3285 /* Write the back buffer backup back. */
3286 if (backup)
3288 if (texture_target != GL_TEXTURE_2D)
3290 gl_info->gl_ops.gl.p_glDisable(texture_target);
3291 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3292 texture_target = GL_TEXTURE_2D;
3294 context_bind_texture(context, GL_TEXTURE_2D, backup);
3296 else
3298 if (texture_target != src_surface->texture_target)
3300 gl_info->gl_ops.gl.p_glDisable(texture_target);
3301 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3302 texture_target = src_surface->texture_target;
3304 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3307 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3308 /* top left */
3309 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3310 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3312 /* bottom left */
3313 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3314 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3316 /* bottom right */
3317 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3318 (float)fbheight / (float)src_surface->pow2Height);
3319 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3321 /* top right */
3322 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3323 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3324 gl_info->gl_ops.gl.p_glEnd();
3326 gl_info->gl_ops.gl.p_glDisable(texture_target);
3327 checkGLcall("glDisable(texture_target)");
3329 /* Cleanup */
3330 if (src != src_surface->container->texture_rgb.name && src != backup)
3332 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3333 checkGLcall("glDeleteTextures(1, &src)");
3335 if (backup)
3337 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3338 checkGLcall("glDeleteTextures(1, &backup)");
3341 if (wined3d_settings.strict_draw_ordering)
3342 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3344 context_release(context);
3346 /* The texture is now most up to date - If the surface is a render target
3347 * and has a drawable, this path is never entered. */
3348 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3349 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3352 /* Front buffer coordinates are always full screen coordinates, but our GL
3353 * drawable is limited to the window's client area. The sysmem and texture
3354 * copies do have the full screen size. Note that GL has a bottom-left
3355 * origin, while D3D has a top-left origin. */
3356 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3358 UINT drawable_height;
3360 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3362 POINT offset = {0, 0};
3363 RECT windowsize;
3365 ScreenToClient(window, &offset);
3366 OffsetRect(rect, offset.x, offset.y);
3368 GetClientRect(window, &windowsize);
3369 drawable_height = windowsize.bottom - windowsize.top;
3371 else
3373 drawable_height = surface->resource.height;
3376 rect->top = drawable_height - rect->top;
3377 rect->bottom = drawable_height - rect->bottom;
3380 static void surface_blt_to_drawable(const struct wined3d_device *device,
3381 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3382 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3383 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3385 const struct wined3d_gl_info *gl_info;
3386 struct wined3d_context *context;
3387 RECT src_rect, dst_rect;
3389 src_rect = *src_rect_in;
3390 dst_rect = *dst_rect_in;
3392 context = context_acquire(device, dst_surface);
3393 gl_info = context->gl_info;
3395 /* Make sure the surface is up-to-date. This should probably use
3396 * surface_load_location() and worry about the destination surface too,
3397 * unless we're overwriting it completely. */
3398 wined3d_texture_load(src_surface->container, context, FALSE);
3400 /* Activate the destination context, set it up for blitting */
3401 context_apply_blit_state(context, device);
3403 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3404 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3406 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
3408 if (alpha_test)
3410 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3411 checkGLcall("glEnable(GL_ALPHA_TEST)");
3413 /* For P8 surfaces, the alpha component contains the palette index.
3414 * Which means that the colorkey is one of the palette entries. In
3415 * other cases pixels that should be masked away have alpha set to 0. */
3416 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3417 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3418 (float)src_surface->container->async.src_blt_color_key.color_space_low_value / 255.0f);
3419 else
3420 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3421 checkGLcall("glAlphaFunc");
3423 else
3425 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3426 checkGLcall("glDisable(GL_ALPHA_TEST)");
3429 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3431 if (alpha_test)
3433 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3434 checkGLcall("glDisable(GL_ALPHA_TEST)");
3437 /* Leave the opengl state valid for blitting */
3438 device->blitter->unset_shader(context->gl_info);
3440 if (wined3d_settings.strict_draw_ordering
3441 || (dst_surface->container->swapchain
3442 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3443 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3445 context_release(context);
3448 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3450 struct wined3d_device *device = s->resource.device;
3451 const struct blit_shader *blitter;
3453 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, WINED3D_BLIT_OP_COLOR_FILL,
3454 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
3455 if (!blitter)
3457 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3458 return WINED3DERR_INVALIDCALL;
3461 return blitter->color_fill(device, s, rect, color);
3464 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3465 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3466 enum wined3d_texture_filter_type filter)
3468 struct wined3d_device *device = dst_surface->resource.device;
3469 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3470 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3472 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3473 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3474 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3476 /* Get the swapchain. One of the surfaces has to be a primary surface */
3477 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3479 WARN("Destination is in sysmem, rejecting gl blt\n");
3480 return WINED3DERR_INVALIDCALL;
3483 dst_swapchain = dst_surface->container->swapchain;
3485 if (src_surface)
3487 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3489 WARN("Src is in sysmem, rejecting gl blt\n");
3490 return WINED3DERR_INVALIDCALL;
3493 src_swapchain = src_surface->container->swapchain;
3495 else
3497 src_swapchain = NULL;
3500 /* Early sort out of cases where no render target is used */
3501 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
3503 TRACE("No surface is render target, not using hardware blit.\n");
3504 return WINED3DERR_INVALIDCALL;
3507 /* No destination color keying supported */
3508 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
3510 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3511 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3512 return WINED3DERR_INVALIDCALL;
3515 if (dst_swapchain && dst_swapchain == src_swapchain)
3517 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3518 return WINED3DERR_INVALIDCALL;
3521 if (dst_swapchain && src_swapchain)
3523 FIXME("Implement hardware blit between two different swapchains\n");
3524 return WINED3DERR_INVALIDCALL;
3527 if (dst_swapchain)
3529 /* Handled with regular texture -> swapchain blit */
3530 if (src_surface == rt)
3531 TRACE("Blit from active render target to a swapchain\n");
3533 else if (src_swapchain && dst_surface == rt)
3535 FIXME("Implement blit from a swapchain to the active render target\n");
3536 return WINED3DERR_INVALIDCALL;
3539 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
3541 /* Blit from render target to texture */
3542 BOOL stretchx;
3544 /* P8 read back is not implemented */
3545 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
3546 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
3548 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3549 return WINED3DERR_INVALIDCALL;
3552 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3554 TRACE("Color keying not supported by frame buffer to texture blit\n");
3555 return WINED3DERR_INVALIDCALL;
3556 /* Destination color key is checked above */
3559 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
3560 stretchx = TRUE;
3561 else
3562 stretchx = FALSE;
3564 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3565 * flip the image nor scale it.
3567 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3568 * -> If the app wants an image width an unscaled width, copy it line per line
3569 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
3570 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3571 * back buffer. This is slower than reading line per line, thus not used for flipping
3572 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3573 * pixel by pixel. */
3574 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
3575 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
3577 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
3578 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
3580 else
3582 TRACE("Using hardware stretching to flip / stretch the texture.\n");
3583 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
3586 surface_evict_sysmem(dst_surface);
3588 return WINED3D_OK;
3591 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3592 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3593 return WINED3DERR_INVALIDCALL;
3596 /* Context activation is done by the caller. */
3597 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
3598 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
3600 struct wined3d_device *device = surface->resource.device;
3601 const struct wined3d_gl_info *gl_info = context->gl_info;
3602 GLint compare_mode = GL_NONE;
3603 struct blt_info info;
3604 GLint old_binding = 0;
3605 RECT rect;
3607 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
3609 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
3610 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
3611 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3612 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
3613 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
3614 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
3615 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
3616 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
3617 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3618 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
3619 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
3621 SetRect(&rect, 0, h, w, 0);
3622 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
3623 context_active_texture(context, context->gl_info, 0);
3624 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
3625 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
3626 if (gl_info->supported[ARB_SHADOW])
3628 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
3629 if (compare_mode != GL_NONE)
3630 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
3633 device->shader_backend->shader_select_depth_blt(device->shader_priv,
3634 gl_info, info.tex_type, &surface->ds_current_size);
3636 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
3637 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
3638 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
3639 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
3640 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
3641 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
3642 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
3643 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
3644 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
3645 gl_info->gl_ops.gl.p_glEnd();
3647 if (compare_mode != GL_NONE)
3648 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
3649 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
3651 gl_info->gl_ops.gl.p_glPopAttrib();
3653 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
3656 void surface_modify_ds_location(struct wined3d_surface *surface,
3657 DWORD location, UINT w, UINT h)
3659 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
3661 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
3662 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
3663 wined3d_texture_set_dirty(surface->container);
3665 surface->ds_current_size.cx = w;
3666 surface->ds_current_size.cy = h;
3667 surface->locations = location;
3670 /* Context activation is done by the caller. */
3671 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3673 const struct wined3d_gl_info *gl_info = context->gl_info;
3674 struct wined3d_device *device = surface->resource.device;
3675 GLsizei w, h;
3677 TRACE("surface %p, new location %#x.\n", surface, location);
3679 /* TODO: Make this work for modes other than FBO */
3680 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3682 if (!(surface->locations & location))
3684 w = surface->ds_current_size.cx;
3685 h = surface->ds_current_size.cy;
3686 surface->ds_current_size.cx = 0;
3687 surface->ds_current_size.cy = 0;
3689 else
3691 w = surface->resource.width;
3692 h = surface->resource.height;
3695 if (surface->ds_current_size.cx == surface->resource.width
3696 && surface->ds_current_size.cy == surface->resource.height)
3698 TRACE("Location (%#x) is already up to date.\n", location);
3699 return;
3702 if (surface->current_renderbuffer)
3704 FIXME("Not supported with fixed up depth stencil.\n");
3705 return;
3708 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3710 TRACE("Surface was discarded, no need copy data.\n");
3711 switch (location)
3713 case WINED3D_LOCATION_TEXTURE_RGB:
3714 wined3d_texture_prepare_texture(surface->container, context, FALSE);
3715 break;
3716 case WINED3D_LOCATION_RB_MULTISAMPLE:
3717 surface_prepare_rb(surface, gl_info, TRUE);
3718 break;
3719 case WINED3D_LOCATION_DRAWABLE:
3720 /* Nothing to do */
3721 break;
3722 default:
3723 FIXME("Unhandled location %#x\n", location);
3725 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
3726 surface->locations |= location;
3727 surface->ds_current_size.cx = surface->resource.width;
3728 surface->ds_current_size.cy = surface->resource.height;
3729 return;
3732 if (!surface->locations)
3734 FIXME("No up to date depth stencil location.\n");
3735 surface->locations |= location;
3736 surface->ds_current_size.cx = surface->resource.width;
3737 surface->ds_current_size.cy = surface->resource.height;
3738 return;
3741 if (location == WINED3D_LOCATION_TEXTURE_RGB)
3743 GLint old_binding = 0;
3744 GLenum bind_target;
3746 /* The render target is allowed to be smaller than the depth/stencil
3747 * buffer, so the onscreen depth/stencil buffer is potentially smaller
3748 * than the offscreen surface. Don't overwrite the offscreen surface
3749 * with undefined data. */
3750 w = min(w, context->swapchain->desc.backbuffer_width);
3751 h = min(h, context->swapchain->desc.backbuffer_height);
3753 TRACE("Copying onscreen depth buffer to depth texture.\n");
3755 if (!device->depth_blt_texture)
3756 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
3758 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3759 * directly on the FBO texture. That's because we need to flip. */
3760 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3761 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3762 NULL, WINED3D_LOCATION_DRAWABLE);
3763 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
3765 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
3766 bind_target = GL_TEXTURE_RECTANGLE_ARB;
3768 else
3770 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3771 bind_target = GL_TEXTURE_2D;
3773 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
3774 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
3775 * internal format, because the internal format might include stencil
3776 * data. In principle we should copy stencil data as well, but unless
3777 * the driver supports stencil export it's hard to do, and doesn't
3778 * seem to be needed in practice. If the hardware doesn't support
3779 * writing stencil data, the glCopyTexImage2D() call might trigger
3780 * software fallbacks. */
3781 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
3782 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3783 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3784 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3785 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3786 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
3787 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
3788 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
3790 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3791 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
3792 context_set_draw_buffer(context, GL_NONE);
3794 /* Do the actual blit */
3795 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
3796 checkGLcall("depth_blt");
3798 context_invalidate_state(context, STATE_FRAMEBUFFER);
3800 if (wined3d_settings.strict_draw_ordering)
3801 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3803 else if (location == WINED3D_LOCATION_DRAWABLE)
3805 TRACE("Copying depth texture to onscreen depth buffer.\n");
3807 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3808 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3809 NULL, WINED3D_LOCATION_DRAWABLE);
3810 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
3811 0, surface->pow2Height - h, w, h, surface->texture_target);
3812 checkGLcall("depth_blt");
3814 context_invalidate_state(context, STATE_FRAMEBUFFER);
3816 if (wined3d_settings.strict_draw_ordering)
3817 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3819 else
3821 ERR("Invalid location (%#x) specified.\n", location);
3824 surface->locations |= location;
3825 surface->ds_current_size.cx = surface->resource.width;
3826 surface->ds_current_size.cy = surface->resource.height;
3829 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
3831 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3833 surface->locations |= location;
3836 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
3838 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3840 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3841 wined3d_texture_set_dirty(surface->container);
3842 surface->locations &= ~location;
3844 if (!surface->locations)
3845 ERR("Surface %p does not have any up to date location.\n", surface);
3848 static DWORD resource_access_from_location(DWORD location)
3850 switch (location)
3852 case WINED3D_LOCATION_SYSMEM:
3853 case WINED3D_LOCATION_USER_MEMORY:
3854 case WINED3D_LOCATION_DIB:
3855 case WINED3D_LOCATION_BUFFER:
3856 return WINED3D_RESOURCE_ACCESS_CPU;
3858 case WINED3D_LOCATION_DRAWABLE:
3859 case WINED3D_LOCATION_TEXTURE_SRGB:
3860 case WINED3D_LOCATION_TEXTURE_RGB:
3861 case WINED3D_LOCATION_RB_MULTISAMPLE:
3862 case WINED3D_LOCATION_RB_RESOLVED:
3863 return WINED3D_RESOURCE_ACCESS_GPU;
3865 default:
3866 FIXME("Unhandled location %#x.\n", location);
3867 return 0;
3871 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
3873 struct wined3d_device *device = surface->resource.device;
3874 struct wined3d_context *context;
3875 const struct wined3d_gl_info *gl_info;
3876 struct wined3d_bo_address dst, src;
3877 UINT size = surface->resource.size;
3879 surface_get_memory(surface, &dst, location);
3880 surface_get_memory(surface, &src, surface->locations);
3882 if (dst.buffer_object)
3884 context = context_acquire(device, NULL);
3885 gl_info = context->gl_info;
3886 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
3887 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr));
3888 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
3889 checkGLcall("Upload PBO");
3890 context_release(context);
3891 return;
3893 if (src.buffer_object)
3895 context = context_acquire(device, NULL);
3896 gl_info = context->gl_info;
3897 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
3898 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr));
3899 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
3900 checkGLcall("Download PBO");
3901 context_release(context);
3902 return;
3904 memcpy(dst.addr, src.addr, size);
3907 static void surface_load_sysmem(struct wined3d_surface *surface,
3908 const struct wined3d_gl_info *gl_info, DWORD dst_location)
3910 if (surface->locations & surface_simple_locations)
3912 surface_copy_simple_location(surface, dst_location);
3913 return;
3916 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
3917 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
3919 /* Download the surface to system memory. */
3920 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3922 struct wined3d_device *device = surface->resource.device;
3923 struct wined3d_context *context;
3925 /* TODO: Use already acquired context when possible. */
3926 context = context_acquire(device, NULL);
3928 wined3d_texture_bind_and_dirtify(surface->container, context,
3929 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
3930 surface_download_data(surface, gl_info, dst_location);
3932 context_release(context);
3934 return;
3937 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
3939 read_from_framebuffer(surface, dst_location);
3940 return;
3943 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
3944 surface, wined3d_debug_location(surface->locations));
3947 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
3948 const struct wined3d_gl_info *gl_info)
3950 RECT r;
3952 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3953 && wined3d_resource_is_offscreen(&surface->container->resource))
3955 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
3956 return WINED3DERR_INVALIDCALL;
3959 surface_get_rect(surface, NULL, &r);
3960 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
3961 surface_blt_to_drawable(surface->resource.device,
3962 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
3964 return WINED3D_OK;
3967 static HRESULT surface_load_texture(struct wined3d_surface *surface,
3968 const struct wined3d_gl_info *gl_info, BOOL srgb)
3970 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
3971 struct wined3d_device *device = surface->resource.device;
3972 const struct wined3d_color_key_conversion *conversion;
3973 struct wined3d_texture *texture = surface->container;
3974 struct wined3d_context *context;
3975 UINT width, src_pitch, dst_pitch;
3976 struct wined3d_bo_address data;
3977 struct wined3d_format format;
3978 POINT dst_point = {0, 0};
3979 BYTE *mem = NULL;
3981 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
3982 && wined3d_resource_is_offscreen(&texture->resource)
3983 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
3985 surface_load_fb_texture(surface, srgb);
3987 return WINED3D_OK;
3990 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
3991 && (surface->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
3992 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3993 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3994 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3996 if (srgb)
3997 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
3998 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
3999 else
4000 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
4001 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
4003 return WINED3D_OK;
4006 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
4007 && (!srgb || (surface->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
4008 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4009 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4010 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4012 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
4013 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
4014 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
4015 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4017 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
4018 &rect, surface, dst_location, &rect);
4020 return WINED3D_OK;
4023 /* Upload from system memory */
4025 if (srgb)
4027 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
4028 == WINED3D_LOCATION_TEXTURE_RGB)
4030 /* Performance warning... */
4031 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
4032 surface_prepare_map_memory(surface);
4033 surface_load_location(surface, surface->resource.map_binding);
4036 else
4038 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
4039 == WINED3D_LOCATION_TEXTURE_SRGB)
4041 /* Performance warning... */
4042 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
4043 surface_prepare_map_memory(surface);
4044 surface_load_location(surface, surface->resource.map_binding);
4048 if (!(surface->locations & surface_simple_locations))
4050 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
4051 /* Lets hope we get it from somewhere... */
4052 surface_prepare_system_memory(surface);
4053 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
4056 /* TODO: Use already acquired context when possible. */
4057 context = context_acquire(device, NULL);
4059 wined3d_texture_prepare_texture(texture, context, srgb);
4060 wined3d_texture_bind_and_dirtify(texture, context, srgb);
4062 width = surface->resource.width;
4063 src_pitch = wined3d_surface_get_pitch(surface);
4065 format = *texture->resource.format;
4066 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
4067 format = *wined3d_get_format(gl_info, conversion->dst_format);
4069 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
4070 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
4071 * getting called. */
4072 if ((format.convert || conversion) && surface->pbo)
4074 TRACE("Removing the pbo attached to surface %p.\n", surface);
4076 if (surface->flags & SFLAG_DIBSECTION)
4077 surface->resource.map_binding = WINED3D_LOCATION_DIB;
4078 else
4079 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
4081 surface_prepare_map_memory(surface);
4082 surface_load_location(surface, surface->resource.map_binding);
4083 surface_remove_pbo(surface, gl_info);
4086 surface_get_memory(surface, &data, surface->locations);
4087 if (format.convert)
4089 /* This code is entered for texture formats which need a fixup. */
4090 UINT height = surface->resource.height;
4092 format.byte_count = format.conv_byte_count;
4093 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4095 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4097 ERR("Out of memory (%u).\n", dst_pitch * height);
4098 context_release(context);
4099 return E_OUTOFMEMORY;
4101 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4102 dst_pitch, dst_pitch * height, width, height, 1);
4103 src_pitch = dst_pitch;
4104 data.addr = mem;
4106 else if (conversion)
4108 /* This code is only entered for color keying fixups */
4109 struct wined3d_palette *palette = NULL;
4110 UINT height = surface->resource.height;
4112 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4113 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4115 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4117 ERR("Out of memory (%u).\n", dst_pitch * height);
4118 context_release(context);
4119 return E_OUTOFMEMORY;
4121 if (texture->swapchain && texture->swapchain->palette)
4122 palette = texture->swapchain->palette;
4123 conversion->convert(data.addr, src_pitch, mem, dst_pitch,
4124 width, height, palette, &texture->async.gl_color_key);
4125 src_pitch = dst_pitch;
4126 data.addr = mem;
4129 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
4130 src_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
4132 context_release(context);
4134 HeapFree(GetProcessHeap(), 0, mem);
4136 return WINED3D_OK;
4139 static void surface_multisample_resolve(struct wined3d_surface *surface)
4141 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4143 if (!(surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE))
4144 ERR("Trying to resolve multisampled surface %p, but location WINED3D_LOCATION_RB_MULTISAMPLE not current.\n",
4145 surface);
4147 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
4148 surface, WINED3D_LOCATION_RB_MULTISAMPLE, &rect, surface, WINED3D_LOCATION_RB_RESOLVED, &rect);
4151 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
4153 struct wined3d_device *device = surface->resource.device;
4154 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4155 HRESULT hr;
4157 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4159 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4161 if (location == WINED3D_LOCATION_TEXTURE_RGB
4162 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4164 struct wined3d_context *context = context_acquire(device, NULL);
4165 surface_load_ds_location(surface, context, location);
4166 context_release(context);
4167 return WINED3D_OK;
4169 else if (location & surface->locations
4170 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
4172 /* Already up to date, nothing to do. */
4173 return WINED3D_OK;
4175 else
4177 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4178 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4179 return WINED3DERR_INVALIDCALL;
4183 if (surface->locations & location)
4185 TRACE("Location already up to date.\n");
4186 return WINED3D_OK;
4189 if (WARN_ON(d3d_surface))
4191 DWORD required_access = resource_access_from_location(location);
4192 if ((surface->resource.access_flags & required_access) != required_access)
4193 WARN("Operation requires %#x access, but surface only has %#x.\n",
4194 required_access, surface->resource.access_flags);
4197 if (!surface->locations)
4199 ERR("Surface %p does not have any up to date location.\n", surface);
4200 surface->flags |= SFLAG_LOST;
4201 return WINED3DERR_DEVICELOST;
4204 switch (location)
4206 case WINED3D_LOCATION_DIB:
4207 case WINED3D_LOCATION_USER_MEMORY:
4208 case WINED3D_LOCATION_SYSMEM:
4209 case WINED3D_LOCATION_BUFFER:
4210 surface_load_sysmem(surface, gl_info, location);
4211 break;
4213 case WINED3D_LOCATION_DRAWABLE:
4214 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
4215 return hr;
4216 break;
4218 case WINED3D_LOCATION_RB_RESOLVED:
4219 surface_multisample_resolve(surface);
4220 break;
4222 case WINED3D_LOCATION_TEXTURE_RGB:
4223 case WINED3D_LOCATION_TEXTURE_SRGB:
4224 if (FAILED(hr = surface_load_texture(surface, gl_info, location == WINED3D_LOCATION_TEXTURE_SRGB)))
4225 return hr;
4226 break;
4228 default:
4229 ERR("Don't know how to handle location %#x.\n", location);
4230 break;
4233 surface_validate_location(surface, location);
4235 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
4236 surface_evict_sysmem(surface);
4238 return WINED3D_OK;
4241 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
4242 /* Context activation is done by the caller. */
4243 static void ffp_blit_free(struct wined3d_device *device) { }
4245 /* Context activation is done by the caller. */
4246 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4247 const struct wined3d_color_key *color_key)
4249 const struct wined3d_gl_info *gl_info = context->gl_info;
4251 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
4252 checkGLcall("glEnable(target)");
4254 return WINED3D_OK;
4257 /* Context activation is done by the caller. */
4258 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
4260 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
4261 checkGLcall("glDisable(GL_TEXTURE_2D)");
4262 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4264 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4265 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4267 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4269 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
4270 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4274 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
4275 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4276 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4277 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4279 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4281 TRACE("Source or destination is in system memory.\n");
4282 return FALSE;
4285 switch (blit_op)
4287 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
4288 if (d3d_info->shader_color_key)
4290 TRACE("Color keying requires converted textures.\n");
4291 return FALSE;
4293 case WINED3D_BLIT_OP_COLOR_BLIT:
4294 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4296 TRACE("Checking support for fixup:\n");
4297 dump_color_fixup_desc(src_format->color_fixup);
4300 /* We only support identity conversions. */
4301 if (!is_identity_fixup(src_format->color_fixup)
4302 || !is_identity_fixup(dst_format->color_fixup))
4304 TRACE("Fixups are not supported.\n");
4305 return FALSE;
4308 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4310 TRACE("Can only blit to render targets.\n");
4311 return FALSE;
4313 return TRUE;
4315 case WINED3D_BLIT_OP_COLOR_FILL:
4316 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4318 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
4319 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4320 return FALSE;
4322 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4324 TRACE("Color fill not supported\n");
4325 return FALSE;
4328 /* FIXME: We should reject color fills on formats with fixups,
4329 * but this would break P8 color fills for example. */
4331 return TRUE;
4333 case WINED3D_BLIT_OP_DEPTH_FILL:
4334 return TRUE;
4336 default:
4337 TRACE("Unsupported blit_op=%d\n", blit_op);
4338 return FALSE;
4342 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4343 const RECT *dst_rect, const struct wined3d_color *color)
4345 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4346 struct wined3d_rendertarget_view *view;
4347 struct wined3d_fb_state fb = {&view, NULL};
4348 HRESULT hr;
4350 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4351 NULL, &wined3d_null_parent_ops, &view)))
4353 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4354 return hr;
4357 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4358 wined3d_rendertarget_view_decref(view);
4360 return WINED3D_OK;
4363 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4364 const RECT *dst_rect, float depth)
4366 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4367 struct wined3d_fb_state fb = {NULL, NULL};
4368 HRESULT hr;
4370 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4371 NULL, &wined3d_null_parent_ops, &fb.depth_stencil)))
4373 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4374 return hr;
4377 device_clear_render_targets(device, 0, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4378 wined3d_rendertarget_view_decref(fb.depth_stencil);
4380 return WINED3D_OK;
4383 static void ffp_blit_blit_surface(struct wined3d_device *device, DWORD filter,
4384 struct wined3d_surface *src_surface, const RECT *src_rect,
4385 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4386 const struct wined3d_color_key *color_key)
4388 /* Blit from offscreen surface to render target */
4389 struct wined3d_color_key old_blt_key = src_surface->container->async.src_blt_color_key;
4390 DWORD old_color_key_flags = src_surface->container->async.color_key_flags;
4392 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4394 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT, color_key);
4396 surface_blt_to_drawable(device, filter,
4397 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
4399 /* Restore the color key parameters */
4400 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT,
4401 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
4403 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
4404 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
4407 const struct blit_shader ffp_blit = {
4408 ffp_blit_alloc,
4409 ffp_blit_free,
4410 ffp_blit_set,
4411 ffp_blit_unset,
4412 ffp_blit_supported,
4413 ffp_blit_color_fill,
4414 ffp_blit_depth_fill,
4415 ffp_blit_blit_surface,
4418 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4420 return WINED3D_OK;
4423 /* Context activation is done by the caller. */
4424 static void cpu_blit_free(struct wined3d_device *device)
4428 /* Context activation is done by the caller. */
4429 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4430 const struct wined3d_color_key *color_key)
4432 return WINED3D_OK;
4435 /* Context activation is done by the caller. */
4436 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4440 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
4441 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4442 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4443 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4445 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4447 return TRUE;
4450 return FALSE;
4453 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4454 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4455 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4457 UINT row_block_count;
4458 const BYTE *src_row;
4459 BYTE *dst_row;
4460 UINT x, y;
4462 src_row = src_data;
4463 dst_row = dst_data;
4465 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4467 if (!flags)
4469 for (y = 0; y < update_h; y += format->block_height)
4471 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4472 src_row += src_pitch;
4473 dst_row += dst_pitch;
4476 return WINED3D_OK;
4479 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4481 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4483 switch (format->id)
4485 case WINED3DFMT_DXT1:
4486 for (y = 0; y < update_h; y += format->block_height)
4488 struct block
4490 WORD color[2];
4491 BYTE control_row[4];
4494 const struct block *s = (const struct block *)src_row;
4495 struct block *d = (struct block *)dst_row;
4497 for (x = 0; x < row_block_count; ++x)
4499 d[x].color[0] = s[x].color[0];
4500 d[x].color[1] = s[x].color[1];
4501 d[x].control_row[0] = s[x].control_row[3];
4502 d[x].control_row[1] = s[x].control_row[2];
4503 d[x].control_row[2] = s[x].control_row[1];
4504 d[x].control_row[3] = s[x].control_row[0];
4506 src_row -= src_pitch;
4507 dst_row += dst_pitch;
4509 return WINED3D_OK;
4511 case WINED3DFMT_DXT2:
4512 case WINED3DFMT_DXT3:
4513 for (y = 0; y < update_h; y += format->block_height)
4515 struct block
4517 WORD alpha_row[4];
4518 WORD color[2];
4519 BYTE control_row[4];
4522 const struct block *s = (const struct block *)src_row;
4523 struct block *d = (struct block *)dst_row;
4525 for (x = 0; x < row_block_count; ++x)
4527 d[x].alpha_row[0] = s[x].alpha_row[3];
4528 d[x].alpha_row[1] = s[x].alpha_row[2];
4529 d[x].alpha_row[2] = s[x].alpha_row[1];
4530 d[x].alpha_row[3] = s[x].alpha_row[0];
4531 d[x].color[0] = s[x].color[0];
4532 d[x].color[1] = s[x].color[1];
4533 d[x].control_row[0] = s[x].control_row[3];
4534 d[x].control_row[1] = s[x].control_row[2];
4535 d[x].control_row[2] = s[x].control_row[1];
4536 d[x].control_row[3] = s[x].control_row[0];
4538 src_row -= src_pitch;
4539 dst_row += dst_pitch;
4541 return WINED3D_OK;
4543 default:
4544 FIXME("Compressed flip not implemented for format %s.\n",
4545 debug_d3dformat(format->id));
4546 return E_NOTIMPL;
4550 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
4551 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
4553 return E_NOTIMPL;
4556 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4557 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4558 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4560 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
4561 const struct wined3d_format *src_format, *dst_format;
4562 unsigned int src_fmt_flags, dst_fmt_flags;
4563 struct wined3d_texture *src_texture = NULL;
4564 struct wined3d_map_desc dst_map, src_map;
4565 const BYTE *sbase = NULL;
4566 HRESULT hr = WINED3D_OK;
4567 const BYTE *sbuf;
4568 BYTE *dbuf;
4569 int x, y;
4571 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4572 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4573 flags, fx, debug_d3dtexturefiltertype(filter));
4575 if (src_surface == dst_surface)
4577 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
4578 src_map = dst_map;
4579 src_format = dst_surface->resource.format;
4580 dst_format = src_format;
4581 dst_fmt_flags = dst_surface->resource.format_flags;
4582 src_fmt_flags = dst_fmt_flags;
4584 else
4586 dst_format = dst_surface->resource.format;
4587 dst_fmt_flags = dst_surface->resource.format_flags;
4588 if (src_surface)
4590 if (dst_surface->resource.format->id != src_surface->resource.format->id)
4592 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
4594 /* The conv function writes a FIXME */
4595 WARN("Cannot convert source surface format to dest format.\n");
4596 goto release;
4598 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
4600 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
4601 src_format = src_surface->resource.format;
4602 src_fmt_flags = src_surface->resource.format_flags;
4604 else
4606 src_format = dst_format;
4607 src_fmt_flags = dst_fmt_flags;
4610 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
4613 bpp = dst_surface->resource.format->byte_count;
4614 srcheight = src_rect->bottom - src_rect->top;
4615 srcwidth = src_rect->right - src_rect->left;
4616 dstheight = dst_rect->bottom - dst_rect->top;
4617 dstwidth = dst_rect->right - dst_rect->left;
4618 width = (dst_rect->right - dst_rect->left) * bpp;
4620 if (src_surface)
4621 sbase = (BYTE *)src_map.data
4622 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
4623 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
4624 if (src_surface != dst_surface)
4625 dbuf = dst_map.data;
4626 else
4627 dbuf = (BYTE *)dst_map.data
4628 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
4629 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
4631 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
4633 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
4635 if (src_surface == dst_surface)
4637 FIXME("Only plain blits supported on compressed surfaces.\n");
4638 hr = E_NOTIMPL;
4639 goto release;
4642 if (srcheight != dstheight || srcwidth != dstwidth)
4644 WARN("Stretching not supported on compressed surfaces.\n");
4645 hr = WINED3DERR_INVALIDCALL;
4646 goto release;
4649 if (!surface_check_block_align(src_surface, src_rect))
4651 WARN("Source rectangle not block-aligned.\n");
4652 hr = WINED3DERR_INVALIDCALL;
4653 goto release;
4656 if (!surface_check_block_align(dst_surface, dst_rect))
4658 WARN("Destination rectangle not block-aligned.\n");
4659 hr = WINED3DERR_INVALIDCALL;
4660 goto release;
4663 hr = surface_cpu_blt_compressed(sbase, dbuf,
4664 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
4665 src_format, flags, fx);
4666 goto release;
4669 /* First, all the 'source-less' blits */
4670 if (flags & WINEDDBLT_COLORFILL)
4672 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
4673 flags &= ~WINEDDBLT_COLORFILL;
4676 if (flags & WINEDDBLT_DEPTHFILL)
4678 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
4680 if (flags & WINEDDBLT_DDROPS)
4682 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
4684 /* Now the 'with source' blits. */
4685 if (src_surface)
4687 int sx, xinc, sy, yinc;
4689 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
4690 goto release;
4692 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
4693 && (srcwidth != dstwidth || srcheight != dstheight))
4695 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
4696 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
4699 xinc = (srcwidth << 16) / dstwidth;
4700 yinc = (srcheight << 16) / dstheight;
4702 if (!flags)
4704 /* No effects, we can cheat here. */
4705 if (dstwidth == srcwidth)
4707 if (dstheight == srcheight)
4709 /* No stretching in either direction. This needs to be as
4710 * fast as possible. */
4711 sbuf = sbase;
4713 /* Check for overlapping surfaces. */
4714 if (src_surface != dst_surface || dst_rect->top < src_rect->top
4715 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
4717 /* No overlap, or dst above src, so copy from top downwards. */
4718 for (y = 0; y < dstheight; ++y)
4720 memcpy(dbuf, sbuf, width);
4721 sbuf += src_map.row_pitch;
4722 dbuf += dst_map.row_pitch;
4725 else if (dst_rect->top > src_rect->top)
4727 /* Copy from bottom upwards. */
4728 sbuf += src_map.row_pitch * dstheight;
4729 dbuf += dst_map.row_pitch * dstheight;
4730 for (y = 0; y < dstheight; ++y)
4732 sbuf -= src_map.row_pitch;
4733 dbuf -= dst_map.row_pitch;
4734 memcpy(dbuf, sbuf, width);
4737 else
4739 /* Src and dst overlapping on the same line, use memmove. */
4740 for (y = 0; y < dstheight; ++y)
4742 memmove(dbuf, sbuf, width);
4743 sbuf += src_map.row_pitch;
4744 dbuf += dst_map.row_pitch;
4748 else
4750 /* Stretching in y direction only. */
4751 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4753 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4754 memcpy(dbuf, sbuf, width);
4755 dbuf += dst_map.row_pitch;
4759 else
4761 /* Stretching in X direction. */
4762 int last_sy = -1;
4763 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4765 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4767 if ((sy >> 16) == (last_sy >> 16))
4769 /* This source row is the same as last source row -
4770 * Copy the already stretched row. */
4771 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
4773 else
4775 #define STRETCH_ROW(type) \
4776 do { \
4777 const type *s = (const type *)sbuf; \
4778 type *d = (type *)dbuf; \
4779 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4780 d[x] = s[sx >> 16]; \
4781 } while(0)
4783 switch(bpp)
4785 case 1:
4786 STRETCH_ROW(BYTE);
4787 break;
4788 case 2:
4789 STRETCH_ROW(WORD);
4790 break;
4791 case 4:
4792 STRETCH_ROW(DWORD);
4793 break;
4794 case 3:
4796 const BYTE *s;
4797 BYTE *d = dbuf;
4798 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
4800 DWORD pixel;
4802 s = sbuf + 3 * (sx >> 16);
4803 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4804 d[0] = (pixel ) & 0xff;
4805 d[1] = (pixel >> 8) & 0xff;
4806 d[2] = (pixel >> 16) & 0xff;
4807 d += 3;
4809 break;
4811 default:
4812 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
4813 hr = WINED3DERR_NOTAVAILABLE;
4814 goto error;
4816 #undef STRETCH_ROW
4818 dbuf += dst_map.row_pitch;
4819 last_sy = sy;
4823 else
4825 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
4826 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
4827 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
4828 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
4830 /* The color keying flags are checked for correctness in ddraw */
4831 if (flags & WINEDDBLT_KEYSRC)
4833 keylow = src_surface->container->async.src_blt_color_key.color_space_low_value;
4834 keyhigh = src_surface->container->async.src_blt_color_key.color_space_high_value;
4836 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4838 keylow = fx->ddckSrcColorkey.color_space_low_value;
4839 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
4842 if (flags & WINEDDBLT_KEYDEST)
4844 /* Destination color keys are taken from the source surface! */
4845 destkeylow = src_surface->container->async.dst_blt_color_key.color_space_low_value;
4846 destkeyhigh = src_surface->container->async.dst_blt_color_key.color_space_high_value;
4848 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
4850 destkeylow = fx->ddckDestColorkey.color_space_low_value;
4851 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
4854 if (bpp == 1)
4856 keymask = 0xff;
4858 else
4860 DWORD masks[3];
4861 get_color_masks(src_format, masks);
4862 keymask = masks[0]
4863 | masks[1]
4864 | masks[2];
4866 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
4869 if (flags & WINEDDBLT_DDFX)
4871 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
4872 LONG tmpxy;
4873 dTopLeft = dbuf;
4874 dTopRight = dbuf + ((dstwidth - 1) * bpp);
4875 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
4876 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
4878 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
4880 /* I don't think we need to do anything about this flag */
4881 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
4883 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
4885 tmp = dTopRight;
4886 dTopRight = dTopLeft;
4887 dTopLeft = tmp;
4888 tmp = dBottomRight;
4889 dBottomRight = dBottomLeft;
4890 dBottomLeft = tmp;
4891 dstxinc = dstxinc * -1;
4893 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
4895 tmp = dTopLeft;
4896 dTopLeft = dBottomLeft;
4897 dBottomLeft = tmp;
4898 tmp = dTopRight;
4899 dTopRight = dBottomRight;
4900 dBottomRight = tmp;
4901 dstyinc = dstyinc * -1;
4903 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
4905 /* I don't think we need to do anything about this flag */
4906 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
4908 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
4910 tmp = dBottomRight;
4911 dBottomRight = dTopLeft;
4912 dTopLeft = tmp;
4913 tmp = dBottomLeft;
4914 dBottomLeft = dTopRight;
4915 dTopRight = tmp;
4916 dstxinc = dstxinc * -1;
4917 dstyinc = dstyinc * -1;
4919 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
4921 tmp = dTopLeft;
4922 dTopLeft = dBottomLeft;
4923 dBottomLeft = dBottomRight;
4924 dBottomRight = dTopRight;
4925 dTopRight = tmp;
4926 tmpxy = dstxinc;
4927 dstxinc = dstyinc;
4928 dstyinc = tmpxy;
4929 dstxinc = dstxinc * -1;
4931 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
4933 tmp = dTopLeft;
4934 dTopLeft = dTopRight;
4935 dTopRight = dBottomRight;
4936 dBottomRight = dBottomLeft;
4937 dBottomLeft = tmp;
4938 tmpxy = dstxinc;
4939 dstxinc = dstyinc;
4940 dstyinc = tmpxy;
4941 dstyinc = dstyinc * -1;
4943 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
4945 /* I don't think we need to do anything about this flag */
4946 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
4948 dbuf = dTopLeft;
4949 flags &= ~(WINEDDBLT_DDFX);
4952 #define COPY_COLORKEY_FX(type) \
4953 do { \
4954 const type *s; \
4955 type *d = (type *)dbuf, *dx, tmp; \
4956 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
4958 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
4959 dx = d; \
4960 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4962 tmp = s[sx >> 16]; \
4963 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
4964 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
4966 dx[0] = tmp; \
4968 dx = (type *)(((BYTE *)dx) + dstxinc); \
4970 d = (type *)(((BYTE *)d) + dstyinc); \
4972 } while(0)
4974 switch (bpp)
4976 case 1:
4977 COPY_COLORKEY_FX(BYTE);
4978 break;
4979 case 2:
4980 COPY_COLORKEY_FX(WORD);
4981 break;
4982 case 4:
4983 COPY_COLORKEY_FX(DWORD);
4984 break;
4985 case 3:
4987 const BYTE *s;
4988 BYTE *d = dbuf, *dx;
4989 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4991 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4992 dx = d;
4993 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
4995 DWORD pixel, dpixel = 0;
4996 s = sbuf + 3 * (sx>>16);
4997 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4998 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
4999 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
5000 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
5002 dx[0] = (pixel ) & 0xff;
5003 dx[1] = (pixel >> 8) & 0xff;
5004 dx[2] = (pixel >> 16) & 0xff;
5006 dx += dstxinc;
5008 d += dstyinc;
5010 break;
5012 default:
5013 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
5014 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
5015 hr = WINED3DERR_NOTAVAILABLE;
5016 goto error;
5017 #undef COPY_COLORKEY_FX
5022 error:
5023 if (flags && FIXME_ON(d3d_surface))
5025 FIXME("\tUnsupported flags: %#x.\n", flags);
5028 release:
5029 wined3d_surface_unmap(dst_surface);
5030 if (src_surface && src_surface != dst_surface)
5031 wined3d_surface_unmap(src_surface);
5032 /* Release the converted surface, if any. */
5033 if (src_texture)
5034 wined3d_texture_decref(src_texture);
5036 return hr;
5039 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5040 const RECT *dst_rect, const struct wined3d_color *color)
5042 static const RECT src_rect;
5043 WINEDDBLTFX BltFx;
5045 memset(&BltFx, 0, sizeof(BltFx));
5046 BltFx.dwSize = sizeof(BltFx);
5047 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
5048 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
5049 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
5052 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
5053 struct wined3d_surface *surface, const RECT *rect, float depth)
5055 FIXME("Depth filling not implemented by cpu_blit.\n");
5056 return WINED3DERR_INVALIDCALL;
5059 static void cpu_blit_blit_surface(struct wined3d_device *device, DWORD filter,
5060 struct wined3d_surface *src_surface, const RECT *src_rect,
5061 struct wined3d_surface *dst_surface, const RECT *dst_rect,
5062 const struct wined3d_color_key *color_key)
5064 /* FIXME: Remove error returns from surface_blt_cpu. */
5065 ERR("Blit method not implemented by cpu_blit.\n");
5068 const struct blit_shader cpu_blit = {
5069 cpu_blit_alloc,
5070 cpu_blit_free,
5071 cpu_blit_set,
5072 cpu_blit_unset,
5073 cpu_blit_supported,
5074 cpu_blit_color_fill,
5075 cpu_blit_depth_fill,
5076 cpu_blit_blit_surface,
5079 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5080 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5081 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5083 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5084 struct wined3d_device *device = dst_surface->resource.device;
5085 DWORD src_ds_flags, dst_ds_flags;
5086 RECT src_rect, dst_rect;
5087 BOOL scale, convert;
5089 static const DWORD simple_blit = WINEDDBLT_ASYNC
5090 | WINEDDBLT_COLORFILL
5091 | WINEDDBLT_KEYSRC
5092 | WINEDDBLT_KEYSRCOVERRIDE
5093 | WINEDDBLT_WAIT
5094 | WINEDDBLT_DEPTHFILL
5095 | WINEDDBLT_DONOTWAIT;
5097 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5098 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
5099 flags, fx, debug_d3dtexturefiltertype(filter));
5100 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5102 if (fx)
5104 TRACE("dwSize %#x.\n", fx->dwSize);
5105 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5106 TRACE("dwROP %#x.\n", fx->dwROP);
5107 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5108 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5109 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5110 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5111 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5112 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5113 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5114 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5115 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5116 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5117 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5118 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5119 TRACE("dwReserved %#x.\n", fx->dwReserved);
5120 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5121 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5122 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5123 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5124 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5125 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5126 fx->ddckDestColorkey.color_space_low_value,
5127 fx->ddckDestColorkey.color_space_high_value);
5128 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5129 fx->ddckSrcColorkey.color_space_low_value,
5130 fx->ddckSrcColorkey.color_space_high_value);
5133 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5135 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5136 return WINEDDERR_SURFACEBUSY;
5139 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5141 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5142 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5143 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5144 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5145 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5147 WARN("The application gave us a bad destination rectangle.\n");
5148 return WINEDDERR_INVALIDRECT;
5151 if (src_surface)
5153 surface_get_rect(src_surface, src_rect_in, &src_rect);
5155 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5156 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5157 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5158 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5159 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5161 WARN("Application gave us bad source rectangle for Blt.\n");
5162 return WINEDDERR_INVALIDRECT;
5165 else
5167 memset(&src_rect, 0, sizeof(src_rect));
5170 if (!fx || !(fx->dwDDFX))
5171 flags &= ~WINEDDBLT_DDFX;
5173 if (flags & WINEDDBLT_WAIT)
5174 flags &= ~WINEDDBLT_WAIT;
5176 if (flags & WINEDDBLT_ASYNC)
5178 static unsigned int once;
5180 if (!once++)
5181 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5182 flags &= ~WINEDDBLT_ASYNC;
5185 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5186 if (flags & WINEDDBLT_DONOTWAIT)
5188 static unsigned int once;
5190 if (!once++)
5191 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5192 flags &= ~WINEDDBLT_DONOTWAIT;
5195 if (!device->d3d_initialized)
5197 WARN("D3D not initialized, using fallback.\n");
5198 goto cpu;
5201 /* We want to avoid invalidating the sysmem location for converted
5202 * surfaces, since otherwise we'd have to convert the data back when
5203 * locking them. */
5204 if (dst_surface->container->flags & WINED3D_TEXTURE_CONVERTED
5205 || dst_surface->container->resource.format->convert
5206 || wined3d_format_get_color_key_conversion(dst_surface->container, TRUE))
5208 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5209 goto cpu;
5212 if (flags & ~simple_blit)
5214 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5215 goto fallback;
5218 if (src_surface)
5219 src_swapchain = src_surface->container->swapchain;
5220 else
5221 src_swapchain = NULL;
5223 dst_swapchain = dst_surface->container->swapchain;
5225 /* This isn't strictly needed. FBO blits for example could deal with
5226 * cross-swapchain blits by first downloading the source to a texture
5227 * before switching to the destination context. We just have this here to
5228 * not have to deal with the issue, since cross-swapchain blits should be
5229 * rare. */
5230 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5232 FIXME("Using fallback for cross-swapchain blit.\n");
5233 goto fallback;
5236 scale = src_surface
5237 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5238 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5239 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5241 dst_ds_flags = dst_surface->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5242 if (src_surface)
5243 src_ds_flags = src_surface->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5244 else
5245 src_ds_flags = 0;
5247 if (src_ds_flags || dst_ds_flags)
5249 if (flags & WINEDDBLT_DEPTHFILL)
5251 float depth;
5253 TRACE("Depth fill.\n");
5255 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5256 return WINED3DERR_INVALIDCALL;
5258 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
5259 return WINED3D_OK;
5261 else
5263 if (src_ds_flags != dst_ds_flags)
5265 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
5266 return WINED3DERR_INVALIDCALL;
5269 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
5270 &src_rect, dst_surface, dst_surface->container->resource.draw_binding, &dst_rect)))
5271 return WINED3D_OK;
5274 else
5276 const struct blit_shader *blitter;
5278 /* In principle this would apply to depth blits as well, but we don't
5279 * implement those in the CPU blitter at the moment. */
5280 if ((dst_surface->locations & dst_surface->resource.map_binding)
5281 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
5283 if (scale)
5284 TRACE("Not doing sysmem blit because of scaling.\n");
5285 else if (convert)
5286 TRACE("Not doing sysmem blit because of format conversion.\n");
5287 else
5288 goto cpu;
5291 if (flags & WINEDDBLT_COLORFILL)
5293 struct wined3d_color color;
5294 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
5296 TRACE("Color fill.\n");
5298 if (!wined3d_format_convert_color_to_float(dst_surface->resource.format,
5299 palette, fx->u5.dwFillColor, &color))
5300 goto fallback;
5302 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
5303 return WINED3D_OK;
5305 else
5307 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
5308 const struct wined3d_color_key *color_key = NULL;
5310 TRACE("Color blit.\n");
5311 if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5313 color_key = &fx->ddckSrcColorkey;
5314 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5316 else if (flags & WINEDDBLT_KEYSRC)
5318 color_key = &src_surface->container->async.src_blt_color_key;
5319 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5321 else if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5322 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5324 /* Upload */
5325 if (scale)
5326 TRACE("Not doing upload because of scaling.\n");
5327 else if (convert)
5328 TRACE("Not doing upload because of format conversion.\n");
5329 else
5331 POINT dst_point = {dst_rect.left, dst_rect.top};
5333 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
5335 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
5336 surface_load_location(dst_surface, dst_surface->container->resource.draw_binding);
5337 return WINED3D_OK;
5341 else if (dst_swapchain && dst_swapchain->back_buffers
5342 && dst_surface->container == dst_swapchain->front_buffer
5343 && src_surface->container == dst_swapchain->back_buffers[0])
5345 /* Use present for back -> front blits. The idea behind this is
5346 * that present is potentially faster than a blit, in particular
5347 * when FBO blits aren't available. Some ddraw applications like
5348 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5349 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5350 * applications can't blit directly to the frontbuffer. */
5351 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5353 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5355 /* Set the swap effect to COPY, we don't want the backbuffer
5356 * to become undefined. */
5357 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5358 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5359 dst_swapchain->desc.swap_effect = swap_effect;
5361 return WINED3D_OK;
5364 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
5365 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5366 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5368 TRACE("Using FBO blit.\n");
5370 surface_blt_fbo(device, filter,
5371 src_surface, src_surface->container->resource.draw_binding, &src_rect,
5372 dst_surface, dst_surface->container->resource.draw_binding, &dst_rect);
5373 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5374 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5376 return WINED3D_OK;
5379 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
5380 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5381 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format);
5382 if (blitter)
5384 blitter->blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect, color_key);
5385 return WINED3D_OK;
5390 fallback:
5391 /* Special cases for render targets. */
5392 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
5393 return WINED3D_OK;
5395 cpu:
5397 /* For the rest call the X11 surface implementation. For render targets
5398 * this should be implemented OpenGL accelerated in surface_blt_special(),
5399 * other blits are rather rare. */
5400 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
5403 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5404 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5406 struct wined3d_device *device = container->resource.device;
5407 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5408 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5409 UINT multisample_quality = desc->multisample_quality;
5410 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
5411 unsigned int resource_size;
5412 HRESULT hr;
5414 if (multisample_quality > 0)
5416 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
5417 multisample_quality = 0;
5420 /* Quick lockable sanity check.
5421 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5422 * this function is too deep to need to care about things like this.
5423 * Levels need to be checked too, since they all affect what can be done. */
5424 switch (desc->pool)
5426 case WINED3D_POOL_MANAGED:
5427 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5428 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5429 break;
5431 case WINED3D_POOL_DEFAULT:
5432 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5433 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5434 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5435 break;
5437 case WINED3D_POOL_SCRATCH:
5438 case WINED3D_POOL_SYSTEM_MEM:
5439 break;
5441 default:
5442 FIXME("Unknown pool %#x.\n", desc->pool);
5443 break;
5446 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5447 FIXME("Trying to create a render target that isn't in the default pool.\n");
5449 /* FIXME: Check that the format is supported by the device. */
5451 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5452 if (!resource_size)
5453 return WINED3DERR_INVALIDCALL;
5455 if (device->wined3d->flags & WINED3D_NO3D)
5456 surface->surface_ops = &gdi_surface_ops;
5457 else
5458 surface->surface_ops = &surface_ops;
5460 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, container->resource.gl_type,
5461 format, desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height,
5462 1, resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5464 WARN("Failed to initialize resource, returning %#x.\n", hr);
5465 return hr;
5468 surface->container = container;
5469 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5470 list_init(&surface->renderbuffers);
5471 list_init(&surface->overlays);
5473 /* Flags */
5474 if (flags & WINED3D_SURFACE_DISCARD)
5475 surface->flags |= SFLAG_DISCARD;
5476 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5477 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5479 surface->texture_target = target;
5480 surface->texture_level = level;
5481 surface->texture_layer = layer;
5483 /* Call the private setup routine */
5484 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5486 ERR("Private setup failed, hr %#x.\n", hr);
5487 surface_cleanup(surface);
5488 return hr;
5491 /* Similar to lockable rendertargets above, creating the DIB section
5492 * during surface initialization prevents the sysmem pointer from changing
5493 * after a wined3d_surface_getdc() call. */
5494 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5495 && SUCCEEDED(surface_create_dib_section(surface)))
5496 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5498 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5500 wined3d_resource_free_sysmem(&surface->resource);
5501 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5502 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5505 return hr;
5508 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5509 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
5511 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
5512 const struct wined3d_parent_ops *parent_ops;
5513 struct wined3d_surface *object;
5514 void *parent;
5515 HRESULT hr;
5517 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
5518 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
5519 container, desc->width, desc->height, debug_d3dformat(desc->format),
5520 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
5521 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
5523 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
5524 return E_OUTOFMEMORY;
5526 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
5528 WARN("Failed to initialize surface, returning %#x.\n", hr);
5529 HeapFree(GetProcessHeap(), 0, object);
5530 return hr;
5533 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
5534 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
5536 WARN("Failed to create surface parent, hr %#x.\n", hr);
5537 wined3d_surface_destroy(object);
5538 return hr;
5541 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
5543 object->resource.parent = parent;
5544 object->resource.parent_ops = parent_ops;
5545 *surface = object;
5547 return hr;