wined3d: Handle slice pitch and alignment as well in wined3d_format_calculate_pitch().
[wine.git] / dlls / wined3d / surface.c
blob1860258f9db693296e9fddde73954f15b1be7ce1
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2011, 2013-2014 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d);
37 #define MAXLOCKCOUNT 50 /* After this amount of locks do not free the sysmem copy. */
39 static const DWORD surface_simple_locations =
40 WINED3D_LOCATION_SYSMEM | WINED3D_LOCATION_USER_MEMORY
41 | WINED3D_LOCATION_DIB | WINED3D_LOCATION_BUFFER;
43 static void surface_cleanup(struct wined3d_surface *surface)
45 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->pbo || surface->rb_multisample
50 || surface->rb_resolved || !list_empty(&surface->renderbuffers))
52 struct wined3d_renderbuffer_entry *entry, *entry2;
53 const struct wined3d_gl_info *gl_info;
54 struct wined3d_context *context;
56 context = context_acquire(surface->resource.device, NULL);
57 gl_info = context->gl_info;
59 if (surface->pbo)
61 TRACE("Deleting PBO %u.\n", surface->pbo);
62 GL_EXTCALL(glDeleteBuffers(1, &surface->pbo));
65 if (surface->rb_multisample)
67 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
68 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
71 if (surface->rb_resolved)
73 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
74 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
77 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
79 TRACE("Deleting renderbuffer %u.\n", entry->id);
80 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
81 HeapFree(GetProcessHeap(), 0, entry);
84 context_release(context);
87 if (surface->flags & SFLAG_DIBSECTION)
89 DeleteDC(surface->hDC);
90 DeleteObject(surface->dib.DIBsection);
91 surface->dib.bitmap_data = NULL;
94 if (surface->overlay_dest)
95 list_remove(&surface->overlay_entry);
97 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
99 list_remove(&overlay->overlay_entry);
100 overlay->overlay_dest = NULL;
103 resource_cleanup(&surface->resource);
106 void wined3d_surface_destroy(struct wined3d_surface *surface)
108 TRACE("surface %p.\n", surface);
110 surface_cleanup(surface);
111 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
112 HeapFree(GetProcessHeap(), 0, surface);
115 void surface_get_drawable_size(const struct wined3d_surface *surface, const struct wined3d_context *context,
116 unsigned int *width, unsigned int *height)
118 if (surface->container->swapchain)
120 /* The drawable size of an onscreen drawable is the surface size.
121 * (Actually: The window size, but the surface is created in window
122 * size.) */
123 *width = context->current_rt->resource.width;
124 *height = context->current_rt->resource.height;
126 else if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER)
128 const struct wined3d_swapchain *swapchain = context->swapchain;
130 /* The drawable size of a backbuffer / aux buffer offscreen target is
131 * the size of the current context's drawable, which is the size of
132 * the back buffer of the swapchain the active context belongs to. */
133 *width = swapchain->desc.backbuffer_width;
134 *height = swapchain->desc.backbuffer_height;
136 else
138 /* The drawable size of an FBO target is the OpenGL texture size,
139 * which is the power of two size. */
140 *width = context->current_rt->pow2Width;
141 *height = context->current_rt->pow2Height;
145 struct blt_info
147 GLenum binding;
148 GLenum bind_target;
149 enum wined3d_gl_resource_type tex_type;
150 GLfloat coords[4][3];
153 struct float_rect
155 float l;
156 float t;
157 float r;
158 float b;
161 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
163 f->l = ((r->left * 2.0f) / w) - 1.0f;
164 f->t = ((r->top * 2.0f) / h) - 1.0f;
165 f->r = ((r->right * 2.0f) / w) - 1.0f;
166 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
169 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
171 GLfloat (*coords)[3] = info->coords;
172 struct float_rect f;
174 switch (target)
176 default:
177 FIXME("Unsupported texture target %#x\n", target);
178 /* Fall back to GL_TEXTURE_2D */
179 case GL_TEXTURE_2D:
180 info->binding = GL_TEXTURE_BINDING_2D;
181 info->bind_target = GL_TEXTURE_2D;
182 info->tex_type = WINED3D_GL_RES_TYPE_TEX_2D;
183 coords[0][0] = (float)rect->left / w;
184 coords[0][1] = (float)rect->top / h;
185 coords[0][2] = 0.0f;
187 coords[1][0] = (float)rect->right / w;
188 coords[1][1] = (float)rect->top / h;
189 coords[1][2] = 0.0f;
191 coords[2][0] = (float)rect->left / w;
192 coords[2][1] = (float)rect->bottom / h;
193 coords[2][2] = 0.0f;
195 coords[3][0] = (float)rect->right / w;
196 coords[3][1] = (float)rect->bottom / h;
197 coords[3][2] = 0.0f;
198 break;
200 case GL_TEXTURE_RECTANGLE_ARB:
201 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
202 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
203 info->tex_type = WINED3D_GL_RES_TYPE_TEX_RECT;
204 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
205 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
206 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
207 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
208 break;
210 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
211 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
212 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
213 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
214 cube_coords_float(rect, w, h, &f);
216 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
217 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
218 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
219 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
220 break;
222 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
223 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
224 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
225 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
226 cube_coords_float(rect, w, h, &f);
228 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
229 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
230 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
231 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
232 break;
234 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
235 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
236 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
237 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
238 cube_coords_float(rect, w, h, &f);
240 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
241 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
242 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
243 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
244 break;
246 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
247 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
248 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
249 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
250 cube_coords_float(rect, w, h, &f);
252 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
253 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
254 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
255 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
256 break;
258 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
259 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
260 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
261 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
262 cube_coords_float(rect, w, h, &f);
264 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
265 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
266 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
267 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
268 break;
270 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
271 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
272 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
273 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
274 cube_coords_float(rect, w, h, &f);
276 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
277 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
278 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
279 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
280 break;
284 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
286 if (rect_in)
287 *rect_out = *rect_in;
288 else
290 rect_out->left = 0;
291 rect_out->top = 0;
292 rect_out->right = surface->resource.width;
293 rect_out->bottom = surface->resource.height;
297 /* Context activation is done by the caller. */
298 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
299 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
301 const struct wined3d_gl_info *gl_info = context->gl_info;
302 struct wined3d_texture *texture = src_surface->container;
303 struct blt_info info;
305 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
307 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
308 checkGLcall("glEnable(bind_target)");
310 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
312 /* Filtering for StretchRect */
313 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
314 checkGLcall("glTexParameteri");
315 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
316 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
317 checkGLcall("glTexParameteri");
318 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
319 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
320 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
321 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
322 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
323 checkGLcall("glTexEnvi");
325 /* Draw a quad */
326 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
327 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
328 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
330 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
331 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
333 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
334 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
336 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
337 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
338 gl_info->gl_ops.gl.p_glEnd();
340 /* Unbind the texture */
341 context_bind_texture(context, info.bind_target, 0);
343 /* We changed the filtering settings on the texture. Inform the
344 * container about this to get the filters reset properly next draw. */
345 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
346 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
347 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
348 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
351 /* Works correctly only for <= 4 bpp formats. */
352 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
354 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
355 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
356 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
359 HRESULT surface_create_dib_section(struct wined3d_surface *surface)
361 const struct wined3d_format *format = surface->resource.format;
362 unsigned int format_flags = surface->container->resource.format_flags;
363 SYSTEM_INFO sysInfo;
364 BITMAPINFO *b_info;
365 int extraline = 0;
366 DWORD *masks;
368 TRACE("surface %p.\n", surface);
370 if (!(format_flags & WINED3DFMT_FLAG_GETDC))
372 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
373 return WINED3DERR_INVALIDCALL;
376 switch (format->byte_count)
378 case 2:
379 case 4:
380 /* Allocate extra space to store the RGB bit masks. */
381 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
382 break;
384 case 3:
385 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
386 break;
388 default:
389 /* Allocate extra space for a palette. */
390 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
391 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1u << (format->byte_count * 8)));
392 break;
395 if (!b_info)
396 return E_OUTOFMEMORY;
398 /* Some applications access the surface in via DWORDs, and do not take
399 * the necessary care at the end of the surface. So we need at least
400 * 4 extra bytes at the end of the surface. Check against the page size,
401 * if the last page used for the surface has at least 4 spare bytes we're
402 * safe, otherwise add an extra line to the DIB section. */
403 GetSystemInfo(&sysInfo);
404 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
406 extraline = 1;
407 TRACE("Adding an extra line to the DIB section.\n");
410 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
411 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
412 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
413 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
414 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
415 * wined3d_surface_get_pitch(surface);
416 b_info->bmiHeader.biPlanes = 1;
417 b_info->bmiHeader.biBitCount = format->byte_count * 8;
419 b_info->bmiHeader.biXPelsPerMeter = 0;
420 b_info->bmiHeader.biYPelsPerMeter = 0;
421 b_info->bmiHeader.biClrUsed = 0;
422 b_info->bmiHeader.biClrImportant = 0;
424 /* Get the bit masks */
425 masks = (DWORD *)b_info->bmiColors;
426 switch (surface->resource.format->id)
428 case WINED3DFMT_B8G8R8_UNORM:
429 b_info->bmiHeader.biCompression = BI_RGB;
430 break;
432 case WINED3DFMT_B5G5R5X1_UNORM:
433 case WINED3DFMT_B5G5R5A1_UNORM:
434 case WINED3DFMT_B4G4R4A4_UNORM:
435 case WINED3DFMT_B4G4R4X4_UNORM:
436 case WINED3DFMT_B2G3R3_UNORM:
437 case WINED3DFMT_B2G3R3A8_UNORM:
438 case WINED3DFMT_R10G10B10A2_UNORM:
439 case WINED3DFMT_R8G8B8A8_UNORM:
440 case WINED3DFMT_R8G8B8X8_UNORM:
441 case WINED3DFMT_B10G10R10A2_UNORM:
442 case WINED3DFMT_B5G6R5_UNORM:
443 case WINED3DFMT_R16G16B16A16_UNORM:
444 b_info->bmiHeader.biCompression = BI_BITFIELDS;
445 get_color_masks(format, masks);
446 break;
448 default:
449 /* Don't know palette */
450 b_info->bmiHeader.biCompression = BI_RGB;
451 break;
454 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
455 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
456 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
457 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
459 if (!surface->dib.DIBsection)
461 ERR("Failed to create DIB section.\n");
462 HeapFree(GetProcessHeap(), 0, b_info);
463 return HRESULT_FROM_WIN32(GetLastError());
466 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
467 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
469 HeapFree(GetProcessHeap(), 0, b_info);
471 /* Now allocate a DC. */
472 surface->hDC = CreateCompatibleDC(0);
473 SelectObject(surface->hDC, surface->dib.DIBsection);
475 surface->flags |= SFLAG_DIBSECTION;
477 return WINED3D_OK;
480 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
481 DWORD location)
483 if (location & WINED3D_LOCATION_BUFFER)
485 data->addr = NULL;
486 data->buffer_object = surface->pbo;
487 return;
489 if (location & WINED3D_LOCATION_USER_MEMORY)
491 data->addr = surface->container->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->container->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 if (surface->resource.map_count || surface->container->flags & (WINED3D_TEXTURE_CONVERTED
594 | WINED3D_TEXTURE_PIN_SYSMEM | WINED3D_TEXTURE_DYNAMIC_MAP))
595 return;
597 wined3d_resource_free_sysmem(&surface->resource);
598 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
601 static BOOL surface_use_pbo(const struct wined3d_surface *surface)
603 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
604 struct wined3d_texture *texture = surface->container;
606 return texture->resource.pool == WINED3D_POOL_DEFAULT
607 && surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU
608 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
609 && !texture->resource.format->convert
610 && !(texture->flags & WINED3D_TEXTURE_PIN_SYSMEM)
611 && !(surface->flags & SFLAG_NONPOW2);
614 static HRESULT surface_private_setup(struct wined3d_surface *surface)
616 /* TODO: Check against the maximum texture sizes supported by the video card. */
617 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
618 unsigned int pow2Width, pow2Height;
620 TRACE("surface %p.\n", surface);
622 /* Non-power2 support */
623 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
624 || gl_info->supported[ARB_TEXTURE_RECTANGLE])
626 pow2Width = surface->resource.width;
627 pow2Height = surface->resource.height;
629 else
631 /* Find the nearest pow2 match */
632 pow2Width = pow2Height = 1;
633 while (pow2Width < surface->resource.width)
634 pow2Width <<= 1;
635 while (pow2Height < surface->resource.height)
636 pow2Height <<= 1;
638 surface->pow2Width = pow2Width;
639 surface->pow2Height = pow2Height;
641 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
643 /* TODO: Add support for non power two compressed textures. */
644 if (surface->container->resource.format_flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE))
646 FIXME("(%p) Compressed or height scaled non-power-two textures are not supported w(%d) h(%d)\n",
647 surface, surface->resource.width, surface->resource.height);
648 return WINED3DERR_NOTAVAILABLE;
652 if (pow2Width != surface->resource.width
653 || pow2Height != surface->resource.height)
655 surface->flags |= SFLAG_NONPOW2;
658 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
659 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
661 /* One of three options:
662 * 1: Do the same as we do with NPOT and scale the texture, (any
663 * texture ops would require the texture to be scaled which is
664 * potentially slow)
665 * 2: Set the texture to the maximum size (bad idea).
666 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
667 * 4: Create the surface, but allow it to be used only for DirectDraw
668 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
669 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
670 * the render target. */
671 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
673 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
674 return WINED3DERR_NOTAVAILABLE;
677 /* We should never use this surface in combination with OpenGL! */
678 TRACE("Creating an oversized surface: %ux%u.\n",
679 surface->pow2Width, surface->pow2Height);
682 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
683 surface->locations = WINED3D_LOCATION_DISCARDED;
685 if (surface_use_pbo(surface))
686 surface->resource.map_binding = WINED3D_LOCATION_BUFFER;
688 return WINED3D_OK;
691 static void surface_unmap(struct wined3d_surface *surface)
693 struct wined3d_device *device = surface->resource.device;
694 const struct wined3d_gl_info *gl_info;
695 struct wined3d_context *context;
697 TRACE("surface %p.\n", surface);
699 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
701 switch (surface->resource.map_binding)
703 case WINED3D_LOCATION_SYSMEM:
704 case WINED3D_LOCATION_USER_MEMORY:
705 case WINED3D_LOCATION_DIB:
706 break;
708 case WINED3D_LOCATION_BUFFER:
709 context = context_acquire(device, NULL);
710 gl_info = context->gl_info;
712 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
713 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
714 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
715 checkGLcall("glUnmapBuffer");
716 context_release(context);
717 break;
719 default:
720 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
723 if (surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_TEXTURE_RGB))
725 TRACE("Not dirtified, nothing to do.\n");
726 return;
729 if (surface->container->swapchain && surface->container->swapchain->front_buffer == surface->container)
731 context = context_acquire(device, surface);
732 surface_load_location(surface, context, surface->container->resource.draw_binding);
733 context_release(context);
735 else if (surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
736 FIXME("Depth / stencil buffer locking is not implemented.\n");
739 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
741 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
742 return FALSE;
743 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
744 return FALSE;
745 return TRUE;
748 static void surface_depth_blt_fbo(const struct wined3d_device *device,
749 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
750 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
752 const struct wined3d_gl_info *gl_info;
753 struct wined3d_context *context;
754 DWORD src_mask, dst_mask;
755 GLbitfield gl_mask;
757 TRACE("device %p\n", device);
758 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
759 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
760 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
761 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
763 src_mask = src_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
764 dst_mask = dst_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
766 if (src_mask != dst_mask)
768 ERR("Incompatible formats %s and %s.\n",
769 debug_d3dformat(src_surface->resource.format->id),
770 debug_d3dformat(dst_surface->resource.format->id));
771 return;
774 if (!src_mask)
776 ERR("Not a depth / stencil format: %s.\n",
777 debug_d3dformat(src_surface->resource.format->id));
778 return;
781 gl_mask = 0;
782 if (src_mask & WINED3DFMT_FLAG_DEPTH)
783 gl_mask |= GL_DEPTH_BUFFER_BIT;
784 if (src_mask & WINED3DFMT_FLAG_STENCIL)
785 gl_mask |= GL_STENCIL_BUFFER_BIT;
787 context = context_acquire(device, NULL);
788 if (!context->valid)
790 context_release(context);
791 WARN("Invalid context, skipping blit.\n");
792 return;
795 /* Make sure the locations are up-to-date. Loading the destination
796 * surface isn't required if the entire surface is overwritten. */
797 surface_load_location(src_surface, context, src_location);
798 if (!surface_is_full_rect(dst_surface, dst_rect))
799 surface_load_location(dst_surface, context, dst_location);
800 else
801 wined3d_surface_prepare(dst_surface, context, dst_location);
803 gl_info = context->gl_info;
805 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
806 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
808 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
809 context_set_draw_buffer(context, GL_NONE);
810 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
811 context_invalidate_state(context, STATE_FRAMEBUFFER);
813 if (gl_mask & GL_DEPTH_BUFFER_BIT)
815 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
816 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
818 if (gl_mask & GL_STENCIL_BUFFER_BIT)
820 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
822 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
823 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
825 gl_info->gl_ops.gl.p_glStencilMask(~0U);
826 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
829 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
830 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
832 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
833 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
834 checkGLcall("glBlitFramebuffer()");
836 if (wined3d_settings.strict_draw_ordering)
837 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
839 context_release(context);
842 /* Blit between surface locations. Onscreen on different swapchains is not supported.
843 * Depth / stencil is not supported. Context activation is done by the caller. */
844 static void surface_blt_fbo(const struct wined3d_device *device,
845 struct wined3d_context *old_ctx, enum wined3d_texture_filter_type filter,
846 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
847 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
849 const struct wined3d_gl_info *gl_info;
850 struct wined3d_context *context = old_ctx;
851 struct wined3d_surface *required_rt, *restore_rt = NULL;
852 RECT src_rect, dst_rect;
853 GLenum gl_filter;
854 GLenum buffer;
856 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
857 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
858 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
859 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
860 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
862 src_rect = *src_rect_in;
863 dst_rect = *dst_rect_in;
865 switch (filter)
867 case WINED3D_TEXF_LINEAR:
868 gl_filter = GL_LINEAR;
869 break;
871 default:
872 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
873 case WINED3D_TEXF_NONE:
874 case WINED3D_TEXF_POINT:
875 gl_filter = GL_NEAREST;
876 break;
879 /* Resolve the source surface first if needed. */
880 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
881 && (src_surface->resource.format->id != dst_surface->resource.format->id
882 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
883 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
884 src_location = WINED3D_LOCATION_RB_RESOLVED;
886 /* Make sure the locations are up-to-date. Loading the destination
887 * surface isn't required if the entire surface is overwritten. (And is
888 * in fact harmful if we're being called by surface_load_location() with
889 * the purpose of loading the destination surface.) */
890 surface_load_location(src_surface, old_ctx, src_location);
891 if (!surface_is_full_rect(dst_surface, &dst_rect))
892 surface_load_location(dst_surface, old_ctx, dst_location);
893 else
894 wined3d_surface_prepare(dst_surface, old_ctx, dst_location);
897 if (src_location == WINED3D_LOCATION_DRAWABLE) required_rt = src_surface;
898 else if (dst_location == WINED3D_LOCATION_DRAWABLE) required_rt = dst_surface;
899 else required_rt = NULL;
901 if (required_rt && required_rt != old_ctx->current_rt)
903 restore_rt = old_ctx->current_rt;
904 context = context_acquire(device, required_rt);
907 if (!context->valid)
909 context_release(context);
910 WARN("Invalid context, skipping blit.\n");
911 return;
914 gl_info = context->gl_info;
916 if (src_location == WINED3D_LOCATION_DRAWABLE)
918 TRACE("Source surface %p is onscreen.\n", src_surface);
919 buffer = surface_get_gl_buffer(src_surface);
920 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
922 else
924 TRACE("Source surface %p is offscreen.\n", src_surface);
925 buffer = GL_COLOR_ATTACHMENT0;
928 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
929 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
930 checkGLcall("glReadBuffer()");
931 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
933 if (dst_location == WINED3D_LOCATION_DRAWABLE)
935 TRACE("Destination surface %p is onscreen.\n", dst_surface);
936 buffer = surface_get_gl_buffer(dst_surface);
937 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
939 else
941 TRACE("Destination surface %p is offscreen.\n", dst_surface);
942 buffer = GL_COLOR_ATTACHMENT0;
945 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
946 context_set_draw_buffer(context, buffer);
947 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
948 context_invalidate_state(context, STATE_FRAMEBUFFER);
950 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
951 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
952 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
953 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
954 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
956 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
957 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
959 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
960 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
961 checkGLcall("glBlitFramebuffer()");
963 if (wined3d_settings.strict_draw_ordering
964 || (dst_location == WINED3D_LOCATION_DRAWABLE
965 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
966 gl_info->gl_ops.gl.p_glFlush();
968 if (restore_rt)
969 context_restore(context, restore_rt);
972 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
973 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
974 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
976 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
977 return FALSE;
979 /* Source and/or destination need to be on the GL side */
980 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
981 return FALSE;
983 switch (blit_op)
985 case WINED3D_BLIT_OP_COLOR_BLIT:
986 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
987 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
988 return FALSE;
989 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
990 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
991 return FALSE;
992 if (!(src_format->id == dst_format->id
993 || (is_identity_fixup(src_format->color_fixup)
994 && is_identity_fixup(dst_format->color_fixup))))
995 return FALSE;
996 break;
998 case WINED3D_BLIT_OP_DEPTH_BLIT:
999 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1000 return FALSE;
1001 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1002 return FALSE;
1003 /* Accept pure swizzle fixups for depth formats. In general we
1004 * ignore the stencil component (if present) at the moment and the
1005 * swizzle is not relevant with just the depth component. */
1006 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
1007 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
1008 return FALSE;
1009 break;
1011 default:
1012 return FALSE;
1015 return TRUE;
1018 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1020 const struct wined3d_format *format = surface->resource.format;
1022 switch (format->id)
1024 case WINED3DFMT_S1_UINT_D15_UNORM:
1025 *float_depth = depth / (float)0x00007fff;
1026 break;
1028 case WINED3DFMT_D16_UNORM:
1029 *float_depth = depth / (float)0x0000ffff;
1030 break;
1032 case WINED3DFMT_D24_UNORM_S8_UINT:
1033 case WINED3DFMT_X8D24_UNORM:
1034 *float_depth = depth / (float)0x00ffffff;
1035 break;
1037 case WINED3DFMT_D32_UNORM:
1038 *float_depth = depth / (float)0xffffffff;
1039 break;
1041 default:
1042 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1043 return FALSE;
1046 return TRUE;
1049 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1051 struct wined3d_resource *resource = &surface->container->resource;
1052 struct wined3d_device *device = resource->device;
1053 struct wined3d_rendertarget_view_desc view_desc;
1054 struct wined3d_rendertarget_view *view;
1055 const struct blit_shader *blitter;
1056 HRESULT hr;
1058 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
1059 WINED3D_BLIT_OP_DEPTH_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
1061 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1062 return WINED3DERR_INVALIDCALL;
1065 view_desc.format_id = resource->format->id;
1066 view_desc.u.texture.level_idx = surface->texture_level;
1067 view_desc.u.texture.layer_idx = surface->texture_layer;
1068 view_desc.u.texture.layer_count = 1;
1069 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
1070 resource, NULL, &wined3d_null_parent_ops, &view)))
1072 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
1073 return hr;
1076 hr = blitter->depth_fill(device, view, rect, depth);
1077 wined3d_rendertarget_view_decref(view);
1079 return hr;
1082 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1083 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1085 struct wined3d_device *device = src_surface->resource.device;
1087 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1088 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1089 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1090 return WINED3DERR_INVALIDCALL;
1092 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1094 surface_modify_ds_location(dst_surface, dst_location,
1095 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1097 return WINED3D_OK;
1100 /* Context activation is done by the caller. */
1101 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1103 GL_EXTCALL(glDeleteBuffers(1, &surface->pbo));
1104 checkGLcall("glDeleteBuffers(1, &surface->pbo)");
1106 surface->pbo = 0;
1107 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1110 static ULONG surface_resource_incref(struct wined3d_resource *resource)
1112 struct wined3d_surface *surface = surface_from_resource(resource);
1114 TRACE("surface %p, container %p.\n", surface, surface->container);
1116 return wined3d_texture_incref(surface->container);
1119 static ULONG surface_resource_decref(struct wined3d_resource *resource)
1121 struct wined3d_surface *surface = surface_from_resource(resource);
1123 TRACE("surface %p, container %p.\n", surface, surface->container);
1125 return wined3d_texture_decref(surface->container);
1128 static void surface_unload(struct wined3d_resource *resource)
1130 struct wined3d_surface *surface = surface_from_resource(resource);
1131 struct wined3d_renderbuffer_entry *entry, *entry2;
1132 struct wined3d_device *device = resource->device;
1133 const struct wined3d_gl_info *gl_info;
1134 struct wined3d_context *context;
1136 TRACE("surface %p.\n", surface);
1138 context = context_acquire(device, NULL);
1139 gl_info = context->gl_info;
1141 if (resource->pool == WINED3D_POOL_DEFAULT)
1143 /* Default pool resources are supposed to be destroyed before Reset is called.
1144 * Implicit resources stay however. So this means we have an implicit render target
1145 * or depth stencil. The content may be destroyed, but we still have to tear down
1146 * opengl resources, so we cannot leave early.
1148 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1149 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1150 * or the depth stencil into an FBO the texture or render buffer will be removed
1151 * and all flags get lost */
1152 if (resource->usage & WINED3DUSAGE_DEPTHSTENCIL)
1154 surface_validate_location(surface, WINED3D_LOCATION_DISCARDED);
1155 surface_invalidate_location(surface, ~WINED3D_LOCATION_DISCARDED);
1157 else
1159 surface_prepare_system_memory(surface);
1160 memset(surface->resource.heap_memory, 0, surface->resource.size);
1161 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1162 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1165 else
1167 surface_prepare_map_memory(surface);
1168 surface_load_location(surface, context, surface->resource.map_binding);
1169 surface_invalidate_location(surface, ~surface->resource.map_binding);
1172 /* Destroy PBOs, but load them into real sysmem before */
1173 if (surface->pbo)
1174 surface_remove_pbo(surface, gl_info);
1176 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1177 * all application-created targets the application has to release the surface
1178 * before calling _Reset
1180 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1182 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1183 list_remove(&entry->entry);
1184 HeapFree(GetProcessHeap(), 0, entry);
1186 list_init(&surface->renderbuffers);
1187 surface->current_renderbuffer = NULL;
1189 if (surface->rb_multisample)
1191 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1192 surface->rb_multisample = 0;
1194 if (surface->rb_resolved)
1196 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1197 surface->rb_resolved = 0;
1200 context_release(context);
1202 resource_unload(resource);
1205 static HRESULT surface_resource_sub_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx,
1206 struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
1208 ERR("Not supported on sub-resources.\n");
1209 return WINED3DERR_INVALIDCALL;
1212 static HRESULT surface_resource_sub_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx)
1214 ERR("Not supported on sub-resources.\n");
1215 return WINED3DERR_INVALIDCALL;
1218 static const struct wined3d_resource_ops surface_resource_ops =
1220 surface_resource_incref,
1221 surface_resource_decref,
1222 surface_unload,
1223 surface_resource_sub_resource_map,
1224 surface_resource_sub_resource_unmap,
1227 static const struct wined3d_surface_ops surface_ops =
1229 surface_private_setup,
1230 surface_unmap,
1233 /*****************************************************************************
1234 * Initializes the GDI surface, aka creates the DIB section we render to
1235 * The DIB section creation is done by calling GetDC, which will create the
1236 * section and releasing the dc to allow the app to use it. The dib section
1237 * will stay until the surface is released
1239 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1240 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1241 * avoid confusion in the shared surface code.
1243 * Returns:
1244 * WINED3D_OK on success
1245 * The return values of called methods on failure
1247 *****************************************************************************/
1248 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1250 HRESULT hr;
1252 TRACE("surface %p.\n", surface);
1254 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1256 ERR("Overlays not yet supported by GDI surfaces.\n");
1257 return WINED3DERR_INVALIDCALL;
1260 /* Sysmem textures have memory already allocated - release it,
1261 * this avoids an unnecessary memcpy. */
1262 hr = surface_create_dib_section(surface);
1263 if (FAILED(hr))
1264 return hr;
1265 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1267 /* We don't mind the nonpow2 stuff in GDI. */
1268 surface->pow2Width = surface->resource.width;
1269 surface->pow2Height = surface->resource.height;
1271 return WINED3D_OK;
1274 static void gdi_surface_unmap(struct wined3d_surface *surface)
1276 TRACE("surface %p.\n", surface);
1278 /* Tell the swapchain to update the screen. */
1279 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
1280 x11_copy_to_screen(surface->container->swapchain, &surface->lockedRect);
1282 memset(&surface->lockedRect, 0, sizeof(RECT));
1285 static const struct wined3d_surface_ops gdi_surface_ops =
1287 gdi_surface_private_setup,
1288 gdi_surface_unmap,
1291 /* This call just downloads data, the caller is responsible for binding the
1292 * correct texture. */
1293 /* Context activation is done by the caller. */
1294 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1295 DWORD dst_location)
1297 const struct wined3d_format *format = surface->resource.format;
1298 struct wined3d_bo_address data;
1300 /* Only support read back of converted P8 surfaces. */
1301 if (surface->container->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1303 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1304 return;
1307 surface_get_memory(surface, &data, dst_location);
1309 if (surface->container->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
1311 TRACE("(%p) : Calling glGetCompressedTexImage level %d, format %#x, type %#x, data %p.\n",
1312 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1314 if (data.buffer_object)
1316 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1317 checkGLcall("glBindBuffer");
1318 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, NULL));
1319 checkGLcall("glGetCompressedTexImage");
1320 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1321 checkGLcall("glBindBuffer");
1323 else
1325 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target,
1326 surface->texture_level, data.addr));
1327 checkGLcall("glGetCompressedTexImage");
1330 else
1332 void *mem;
1333 GLenum gl_format = format->glFormat;
1334 GLenum gl_type = format->glType;
1335 int src_pitch = 0;
1336 int dst_pitch = 0;
1338 if (surface->flags & SFLAG_NONPOW2)
1340 unsigned char alignment = surface->resource.device->surface_alignment;
1341 src_pitch = format->byte_count * surface->pow2Width;
1342 dst_pitch = wined3d_surface_get_pitch(surface);
1343 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1344 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1346 else
1348 mem = data.addr;
1351 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1352 surface, surface->texture_level, gl_format, gl_type, mem);
1354 if (data.buffer_object)
1356 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1357 checkGLcall("glBindBuffer");
1359 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1360 gl_format, gl_type, NULL);
1361 checkGLcall("glGetTexImage");
1363 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1364 checkGLcall("glBindBuffer");
1366 else
1368 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1369 gl_format, gl_type, mem);
1370 checkGLcall("glGetTexImage");
1373 if (surface->flags & SFLAG_NONPOW2)
1375 const BYTE *src_data;
1376 BYTE *dst_data;
1377 UINT y;
1379 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1380 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1381 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1383 * We're doing this...
1385 * instead of boxing the texture :
1386 * |<-texture width ->| -->pow2width| /\
1387 * |111111111111111111| | |
1388 * |222 Texture 222222| boxed empty | texture height
1389 * |3333 Data 33333333| | |
1390 * |444444444444444444| | \/
1391 * ----------------------------------- |
1392 * | boxed empty | boxed empty | pow2height
1393 * | | | \/
1394 * -----------------------------------
1397 * we're repacking the data to the expected texture width
1399 * |<-texture width ->| -->pow2width| /\
1400 * |111111111111111111222222222222222| |
1401 * |222333333333333333333444444444444| texture height
1402 * |444444 | |
1403 * | | \/
1404 * | | |
1405 * | empty | pow2height
1406 * | | \/
1407 * -----------------------------------
1409 * == is the same as
1411 * |<-texture width ->| /\
1412 * |111111111111111111|
1413 * |222222222222222222|texture height
1414 * |333333333333333333|
1415 * |444444444444444444| \/
1416 * --------------------
1418 * This also means that any references to surface memory should work with the data as if it were a
1419 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1421 * internally the texture is still stored in a boxed format so any references to textureName will
1422 * get a boxed texture with width pow2width and not a texture of width resource.width.
1424 * Performance should not be an issue, because applications normally do not lock the surfaces when
1425 * rendering. If an app does, the WINED3D_TEXTURE_DYNAMIC_MAP flag will kick in and the memory copy
1426 * won't be released, and doesn't have to be re-read. */
1427 src_data = mem;
1428 dst_data = data.addr;
1429 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1430 for (y = 0; y < surface->resource.height; ++y)
1432 memcpy(dst_data, src_data, dst_pitch);
1433 src_data += src_pitch;
1434 dst_data += dst_pitch;
1437 HeapFree(GetProcessHeap(), 0, mem);
1442 /* This call just uploads data, the caller is responsible for binding the
1443 * correct texture. */
1444 /* Context activation is done by the caller. */
1445 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1446 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1447 BOOL srgb, const struct wined3d_const_bo_address *data)
1449 UINT update_w = src_rect->right - src_rect->left;
1450 UINT update_h = src_rect->bottom - src_rect->top;
1452 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1453 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1454 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1456 if (surface->resource.map_count)
1458 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
1459 surface->container->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
1462 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
1464 update_h *= format->height_scale.numerator;
1465 update_h /= format->height_scale.denominator;
1468 if (data->buffer_object)
1470 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
1471 checkGLcall("glBindBuffer");
1474 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
1476 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1477 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1478 const BYTE *addr = data->addr;
1479 GLenum internal;
1481 addr += (src_rect->top / format->block_height) * src_pitch;
1482 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1484 if (srgb)
1485 internal = format->glGammaInternal;
1486 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1487 && wined3d_resource_is_offscreen(&surface->container->resource))
1488 internal = format->rtInternal;
1489 else
1490 internal = format->glInternal;
1492 TRACE("glCompressedTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, "
1493 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1494 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1496 if (row_length == src_pitch)
1498 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1499 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1501 else
1503 UINT row, y;
1505 /* glCompressedTexSubImage2D() ignores pixel store state, so we
1506 * can't use the unpack row length like for glTexSubImage2D. */
1507 for (row = 0, y = dst_point->y; row < row_count; ++row)
1509 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1510 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1511 y += format->block_height;
1512 addr += src_pitch;
1515 checkGLcall("glCompressedTexSubImage2D");
1517 else
1519 const BYTE *addr = data->addr;
1521 addr += src_rect->top * src_pitch;
1522 addr += src_rect->left * format->byte_count;
1524 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1525 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1526 update_w, update_h, format->glFormat, format->glType, addr);
1528 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1529 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1530 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1531 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1532 checkGLcall("glTexSubImage2D");
1535 if (data->buffer_object)
1537 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1538 checkGLcall("glBindBuffer");
1541 if (wined3d_settings.strict_draw_ordering)
1542 gl_info->gl_ops.gl.p_glFlush();
1544 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1546 struct wined3d_device *device = surface->resource.device;
1547 unsigned int i;
1549 for (i = 0; i < device->context_count; ++i)
1551 context_surface_update(device->contexts[i], surface);
1556 static BOOL surface_check_block_align(struct wined3d_surface *surface, const struct wined3d_box *box)
1558 UINT width_mask, height_mask;
1560 if (!box->left && !box->top
1561 && box->right == surface->resource.width
1562 && box->bottom == surface->resource.height)
1563 return TRUE;
1565 if ((box->left >= box->right)
1566 || (box->top >= box->bottom)
1567 || (box->right > surface->resource.width)
1568 || (box->bottom > surface->resource.height))
1569 return FALSE;
1571 /* This assumes power of two block sizes, but NPOT block sizes would be
1572 * silly anyway. */
1573 width_mask = surface->resource.format->block_width - 1;
1574 height_mask = surface->resource.format->block_height - 1;
1576 if (!(box->left & width_mask) && !(box->top & height_mask)
1577 && !(box->right & width_mask) && !(box->bottom & height_mask))
1578 return TRUE;
1580 return FALSE;
1583 static BOOL surface_check_block_align_rect(struct wined3d_surface *surface, const RECT *rect)
1585 struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
1587 return surface_check_block_align(surface, &box);
1590 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1591 struct wined3d_surface *src_surface, const RECT *src_rect)
1593 const struct wined3d_format *src_format;
1594 const struct wined3d_format *dst_format;
1595 unsigned int src_fmt_flags, dst_fmt_flags;
1596 const struct wined3d_gl_info *gl_info;
1597 struct wined3d_context *context;
1598 struct wined3d_bo_address data;
1599 UINT update_w, update_h;
1600 UINT dst_w, dst_h;
1601 RECT r, dst_rect;
1602 UINT src_pitch;
1603 POINT p;
1605 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1606 dst_surface, wine_dbgstr_point(dst_point),
1607 src_surface, wine_dbgstr_rect(src_rect));
1609 src_format = src_surface->resource.format;
1610 dst_format = dst_surface->resource.format;
1611 src_fmt_flags = src_surface->container->resource.format_flags;
1612 dst_fmt_flags = dst_surface->container->resource.format_flags;
1614 if (src_format->id != dst_format->id)
1616 WARN("Source and destination surfaces should have the same format.\n");
1617 return WINED3DERR_INVALIDCALL;
1620 if (!dst_point)
1622 p.x = 0;
1623 p.y = 0;
1624 dst_point = &p;
1626 else if (dst_point->x < 0 || dst_point->y < 0)
1628 WARN("Invalid destination point.\n");
1629 return WINED3DERR_INVALIDCALL;
1632 if (!src_rect)
1634 r.left = 0;
1635 r.top = 0;
1636 r.right = src_surface->resource.width;
1637 r.bottom = src_surface->resource.height;
1638 src_rect = &r;
1640 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1641 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1643 WARN("Invalid source rectangle.\n");
1644 return WINED3DERR_INVALIDCALL;
1647 dst_w = dst_surface->resource.width;
1648 dst_h = dst_surface->resource.height;
1650 update_w = src_rect->right - src_rect->left;
1651 update_h = src_rect->bottom - src_rect->top;
1653 if (update_w > dst_w || dst_point->x > dst_w - update_w
1654 || update_h > dst_h || dst_point->y > dst_h - update_h)
1656 WARN("Destination out of bounds.\n");
1657 return WINED3DERR_INVALIDCALL;
1660 if ((src_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(src_surface, src_rect))
1662 WARN("Source rectangle not block-aligned.\n");
1663 return WINED3DERR_INVALIDCALL;
1666 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1667 if ((dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(dst_surface, &dst_rect))
1669 WARN("Destination rectangle not block-aligned.\n");
1670 return WINED3DERR_INVALIDCALL;
1673 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1674 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_surface->container, FALSE))
1675 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1677 context = context_acquire(dst_surface->resource.device, NULL);
1678 gl_info = context->gl_info;
1680 /* Only load the surface for partial updates. For newly allocated texture
1681 * the texture wouldn't be the current location, and we'd upload zeroes
1682 * just to overwrite them again. */
1683 if (update_w == dst_w && update_h == dst_h)
1684 wined3d_texture_prepare_texture(dst_surface->container, context, FALSE);
1685 else
1686 surface_load_location(dst_surface, context, WINED3D_LOCATION_TEXTURE_RGB);
1687 wined3d_texture_bind_and_dirtify(dst_surface->container, context, FALSE);
1689 surface_get_memory(src_surface, &data, src_surface->locations);
1690 src_pitch = wined3d_surface_get_pitch(src_surface);
1692 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1693 src_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1695 context_release(context);
1697 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1698 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1700 return WINED3D_OK;
1703 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1704 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1705 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1706 /* Context activation is done by the caller. */
1707 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1709 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1710 struct wined3d_renderbuffer_entry *entry;
1711 GLuint renderbuffer = 0;
1712 unsigned int src_width, src_height;
1713 unsigned int width, height;
1715 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1717 width = rt->pow2Width;
1718 height = rt->pow2Height;
1720 else
1722 width = surface->pow2Width;
1723 height = surface->pow2Height;
1726 src_width = surface->pow2Width;
1727 src_height = surface->pow2Height;
1729 /* A depth stencil smaller than the render target is not valid */
1730 if (width > src_width || height > src_height) return;
1732 /* Remove any renderbuffer set if the sizes match */
1733 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1734 || (width == src_width && height == src_height))
1736 surface->current_renderbuffer = NULL;
1737 return;
1740 /* Look if we've already got a renderbuffer of the correct dimensions */
1741 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1743 if (entry->width == width && entry->height == height)
1745 renderbuffer = entry->id;
1746 surface->current_renderbuffer = entry;
1747 break;
1751 if (!renderbuffer)
1753 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1754 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1755 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1756 surface->resource.format->glInternal, width, height);
1758 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1759 entry->width = width;
1760 entry->height = height;
1761 entry->id = renderbuffer;
1762 list_add_head(&surface->renderbuffers, &entry->entry);
1764 surface->current_renderbuffer = entry;
1767 checkGLcall("set_compatible_renderbuffer");
1770 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1772 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1774 TRACE("surface %p.\n", surface);
1776 if (!swapchain)
1778 ERR("Surface %p is not on a swapchain.\n", surface);
1779 return GL_NONE;
1782 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
1784 if (swapchain->render_to_fbo)
1786 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1787 return GL_COLOR_ATTACHMENT0;
1789 TRACE("Returning GL_BACK\n");
1790 return GL_BACK;
1792 else if (surface->container == swapchain->front_buffer)
1794 TRACE("Returning GL_FRONT\n");
1795 return GL_FRONT;
1798 FIXME("Higher back buffer, returning GL_BACK\n");
1799 return GL_BACK;
1802 /* Context activation is done by the caller. */
1803 void surface_load(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
1805 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1807 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1809 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
1810 ERR("Not supported on scratch surfaces.\n");
1812 if (surface->locations & location)
1814 TRACE("surface is already in texture\n");
1815 return;
1817 TRACE("Reloading because surface is dirty.\n");
1819 surface_load_location(surface, context, location);
1820 surface_evict_sysmem(surface);
1823 /* See also float_16_to_32() in wined3d_private.h */
1824 static inline unsigned short float_32_to_16(const float *in)
1826 int exp = 0;
1827 float tmp = fabsf(*in);
1828 unsigned int mantissa;
1829 unsigned short ret;
1831 /* Deal with special numbers */
1832 if (*in == 0.0f)
1833 return 0x0000;
1834 if (isnan(*in))
1835 return 0x7c01;
1836 if (isinf(*in))
1837 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1839 if (tmp < (float)(1u << 10))
1843 tmp = tmp * 2.0f;
1844 exp--;
1845 } while (tmp < (float)(1u << 10));
1847 else if (tmp >= (float)(1u << 11))
1851 tmp /= 2.0f;
1852 exp++;
1853 } while (tmp >= (float)(1u << 11));
1856 mantissa = (unsigned int)tmp;
1857 if (tmp - mantissa >= 0.5f)
1858 ++mantissa; /* Round to nearest, away from zero. */
1860 exp += 10; /* Normalize the mantissa. */
1861 exp += 15; /* Exponent is encoded with excess 15. */
1863 if (exp > 30) /* too big */
1865 ret = 0x7c00; /* INF */
1867 else if (exp <= 0)
1869 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1870 while (exp <= 0)
1872 mantissa = mantissa >> 1;
1873 ++exp;
1875 ret = mantissa & 0x3ff;
1877 else
1879 ret = (exp << 10) | (mantissa & 0x3ff);
1882 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1883 return ret;
1886 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
1888 unsigned int row_pitch, slice_pitch;
1890 TRACE("surface %p.\n", surface);
1892 if (surface->container->row_pitch)
1893 return surface->container->row_pitch;
1895 wined3d_format_calculate_pitch(surface->resource.format, surface->resource.device->surface_alignment,
1896 surface->resource.width, surface->resource.height, &row_pitch, &slice_pitch);
1898 TRACE("Returning %u.\n", row_pitch);
1900 return row_pitch;
1903 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1905 struct wined3d_resource *texture_resource = &surface->container->resource;
1906 unsigned int width, height;
1907 BOOL create_dib = FALSE;
1908 DWORD valid_location = 0;
1909 HRESULT hr;
1911 if (surface->flags & SFLAG_DIBSECTION)
1913 DeleteDC(surface->hDC);
1914 DeleteObject(surface->dib.DIBsection);
1915 surface->dib.bitmap_data = NULL;
1916 surface->flags &= ~SFLAG_DIBSECTION;
1917 create_dib = TRUE;
1920 surface->locations = 0;
1921 wined3d_resource_free_sysmem(&surface->resource);
1923 width = texture_resource->width;
1924 height = texture_resource->height;
1925 surface->resource.width = width;
1926 surface->resource.height = height;
1927 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
1928 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
1930 surface->pow2Width = width;
1931 surface->pow2Height = height;
1933 else
1935 surface->pow2Width = surface->pow2Height = 1;
1936 while (surface->pow2Width < width)
1937 surface->pow2Width <<= 1;
1938 while (surface->pow2Height < height)
1939 surface->pow2Height <<= 1;
1942 if (surface->pow2Width != width || surface->pow2Height != height)
1943 surface->flags |= SFLAG_NONPOW2;
1944 else
1945 surface->flags &= ~SFLAG_NONPOW2;
1947 if (surface->container->user_memory)
1949 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
1950 valid_location = WINED3D_LOCATION_USER_MEMORY;
1952 surface->resource.format = texture_resource->format;
1953 surface->resource.multisample_type = texture_resource->multisample_type;
1954 surface->resource.multisample_quality = texture_resource->multisample_quality;
1955 if (surface->container->row_pitch)
1957 surface->resource.size = height * surface->container->row_pitch;
1959 else
1961 /* User memory surfaces don't have the regular surface alignment. */
1962 wined3d_format_calculate_pitch(texture_resource->format, 1, width, height,
1963 &surface->container->row_pitch, &surface->resource.size);
1966 /* The format might be changed to a format that needs conversion.
1967 * If the surface didn't use PBOs previously but could now, don't
1968 * change it - whatever made us not use PBOs might come back, e.g.
1969 * color keys. */
1970 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
1971 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
1973 if (create_dib)
1975 if (FAILED(hr = surface_create_dib_section(surface)))
1977 ERR("Failed to create dib section, hr %#x.\n", hr);
1978 return hr;
1980 if (!valid_location)
1981 valid_location = WINED3D_LOCATION_DIB;
1984 if (!valid_location)
1986 surface_prepare_system_memory(surface);
1987 valid_location = WINED3D_LOCATION_SYSMEM;
1990 surface_validate_location(surface, valid_location);
1992 return WINED3D_OK;
1995 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
1996 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1998 unsigned short *dst_s;
1999 const float *src_f;
2000 unsigned int x, y;
2002 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2004 for (y = 0; y < h; ++y)
2006 src_f = (const float *)(src + y * pitch_in);
2007 dst_s = (unsigned short *) (dst + y * pitch_out);
2008 for (x = 0; x < w; ++x)
2010 dst_s[x] = float_32_to_16(src_f + x);
2015 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2016 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2018 static const unsigned char convert_5to8[] =
2020 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2021 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2022 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2023 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2025 static const unsigned char convert_6to8[] =
2027 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2028 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2029 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2030 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2031 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2032 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2033 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2034 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2036 unsigned int x, y;
2038 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2040 for (y = 0; y < h; ++y)
2042 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2043 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2044 for (x = 0; x < w; ++x)
2046 WORD pixel = src_line[x];
2047 dst_line[x] = 0xff000000u
2048 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
2049 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
2050 | convert_5to8[(pixel & 0x001fu)];
2055 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2056 * in both cases we're just setting the X / Alpha channel to 0xff. */
2057 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2058 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2060 unsigned int x, y;
2062 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2064 for (y = 0; y < h; ++y)
2066 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2067 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2069 for (x = 0; x < w; ++x)
2071 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2076 static inline BYTE cliptobyte(int x)
2078 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2081 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2082 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2084 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2085 unsigned int x, y;
2087 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2089 for (y = 0; y < h; ++y)
2091 const BYTE *src_line = src + y * pitch_in;
2092 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2093 for (x = 0; x < w; ++x)
2095 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2096 * C = Y - 16; D = U - 128; E = V - 128;
2097 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2098 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2099 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2100 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2101 * U and V are shared between the pixels. */
2102 if (!(x & 1)) /* For every even pixel, read new U and V. */
2104 d = (int) src_line[1] - 128;
2105 e = (int) src_line[3] - 128;
2106 r2 = 409 * e + 128;
2107 g2 = - 100 * d - 208 * e + 128;
2108 b2 = 516 * d + 128;
2110 c2 = 298 * ((int) src_line[0] - 16);
2111 dst_line[x] = 0xff000000
2112 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2113 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2114 | cliptobyte((c2 + b2) >> 8); /* blue */
2115 /* Scale RGB values to 0..255 range,
2116 * then clip them if still not in range (may be negative),
2117 * then shift them within DWORD if necessary. */
2118 src_line += 2;
2123 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2124 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2126 unsigned int x, y;
2127 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2129 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2131 for (y = 0; y < h; ++y)
2133 const BYTE *src_line = src + y * pitch_in;
2134 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2135 for (x = 0; x < w; ++x)
2137 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2138 * C = Y - 16; D = U - 128; E = V - 128;
2139 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2140 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2141 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2142 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2143 * U and V are shared between the pixels. */
2144 if (!(x & 1)) /* For every even pixel, read new U and V. */
2146 d = (int) src_line[1] - 128;
2147 e = (int) src_line[3] - 128;
2148 r2 = 409 * e + 128;
2149 g2 = - 100 * d - 208 * e + 128;
2150 b2 = 516 * d + 128;
2152 c2 = 298 * ((int) src_line[0] - 16);
2153 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2154 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2155 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2156 /* Scale RGB values to 0..255 range,
2157 * then clip them if still not in range (may be negative),
2158 * then shift them within DWORD if necessary. */
2159 src_line += 2;
2164 struct d3dfmt_converter_desc
2166 enum wined3d_format_id from, to;
2167 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2170 static const struct d3dfmt_converter_desc converters[] =
2172 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2173 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2174 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2175 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2176 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2177 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2180 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2181 enum wined3d_format_id to)
2183 unsigned int i;
2185 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2187 if (converters[i].from == from && converters[i].to == to)
2188 return &converters[i];
2191 return NULL;
2194 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2196 struct wined3d_map_desc src_map, dst_map;
2197 const struct d3dfmt_converter_desc *conv;
2198 struct wined3d_texture *ret = NULL;
2199 struct wined3d_resource_desc desc;
2200 struct wined3d_surface *dst;
2202 conv = find_converter(source->resource.format->id, to_fmt);
2203 if (!conv)
2205 FIXME("Cannot find a conversion function from format %s to %s.\n",
2206 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2207 return NULL;
2210 /* FIXME: Multisampled conversion? */
2211 wined3d_resource_get_desc(&source->resource, &desc);
2212 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
2213 desc.format = to_fmt;
2214 desc.usage = 0;
2215 desc.pool = WINED3D_POOL_SCRATCH;
2216 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2217 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
2218 NULL, NULL, &wined3d_null_parent_ops, &ret)))
2220 ERR("Failed to create a destination surface for conversion.\n");
2221 return NULL;
2223 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2225 memset(&src_map, 0, sizeof(src_map));
2226 memset(&dst_map, 0, sizeof(dst_map));
2228 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2230 ERR("Failed to lock the source surface.\n");
2231 wined3d_texture_decref(ret);
2232 return NULL;
2234 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2236 ERR("Failed to lock the destination surface.\n");
2237 wined3d_surface_unmap(source);
2238 wined3d_texture_decref(ret);
2239 return NULL;
2242 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2243 source->resource.width, source->resource.height);
2245 wined3d_surface_unmap(dst);
2246 wined3d_surface_unmap(source);
2248 return ret;
2251 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2252 unsigned int bpp, UINT pitch, DWORD color)
2254 BYTE *first;
2255 unsigned int x, y;
2257 /* Do first row */
2259 #define COLORFILL_ROW(type) \
2260 do { \
2261 type *d = (type *)buf; \
2262 for (x = 0; x < width; ++x) \
2263 d[x] = (type)color; \
2264 } while(0)
2266 switch (bpp)
2268 case 1:
2269 COLORFILL_ROW(BYTE);
2270 break;
2272 case 2:
2273 COLORFILL_ROW(WORD);
2274 break;
2276 case 3:
2278 BYTE *d = buf;
2279 for (x = 0; x < width; ++x, d += 3)
2281 d[0] = (color ) & 0xff;
2282 d[1] = (color >> 8) & 0xff;
2283 d[2] = (color >> 16) & 0xff;
2285 break;
2287 case 4:
2288 COLORFILL_ROW(DWORD);
2289 break;
2291 default:
2292 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2293 return WINED3DERR_NOTAVAILABLE;
2296 #undef COLORFILL_ROW
2298 /* Now copy first row. */
2299 first = buf;
2300 for (y = 1; y < height; ++y)
2302 buf += pitch;
2303 memcpy(buf, first, width * bpp);
2306 return WINED3D_OK;
2309 HRESULT wined3d_surface_unmap(struct wined3d_surface *surface)
2311 TRACE("surface %p.\n", surface);
2313 if (!surface->resource.map_count)
2315 WARN("Trying to unmap unmapped surface.\n");
2316 return WINEDDERR_NOTLOCKED;
2318 --surface->resource.map_count;
2320 surface->surface_ops->surface_unmap(surface);
2322 return WINED3D_OK;
2325 HRESULT wined3d_surface_map(struct wined3d_surface *surface, struct wined3d_map_desc *map_desc,
2326 const struct wined3d_box *box, DWORD flags)
2328 const struct wined3d_format *format = surface->resource.format;
2329 unsigned int fmt_flags = surface->container->resource.format_flags;
2330 struct wined3d_device *device = surface->resource.device;
2331 struct wined3d_context *context;
2332 const struct wined3d_gl_info *gl_info;
2333 BYTE *base_memory;
2335 TRACE("surface %p, map_desc %p, box %s, flags %#x.\n",
2336 surface, map_desc, debug_box(box), flags);
2338 if (surface->resource.map_count)
2340 WARN("Surface is already mapped.\n");
2341 return WINED3DERR_INVALIDCALL;
2344 if ((fmt_flags & WINED3DFMT_FLAG_BLOCKS) && box
2345 && !surface_check_block_align(surface, box))
2347 WARN("Map box %s is misaligned for %ux%u blocks.\n",
2348 debug_box(box), format->block_width, format->block_height);
2350 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2351 return WINED3DERR_INVALIDCALL;
2354 ++surface->resource.map_count;
2356 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2357 WARN("Trying to lock unlockable surface.\n");
2359 /* Performance optimization: Count how often a surface is mapped, if it is
2360 * mapped regularly do not throw away the system memory copy. This avoids
2361 * the need to download the surface from OpenGL all the time. The surface
2362 * is still downloaded if the OpenGL texture is changed. Note that this
2363 * only really makes sense for managed textures.*/
2364 if (!(surface->container->flags & WINED3D_TEXTURE_DYNAMIC_MAP)
2365 && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2367 if (++surface->lockCount > MAXLOCKCOUNT)
2369 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2370 surface->container->flags |= WINED3D_TEXTURE_DYNAMIC_MAP;
2374 surface_prepare_map_memory(surface);
2375 if (flags & WINED3D_MAP_DISCARD)
2377 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2378 wined3d_debug_location(surface->resource.map_binding));
2379 surface_validate_location(surface, surface->resource.map_binding);
2381 else
2383 struct wined3d_context *context = NULL;
2385 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2386 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2388 if (surface->resource.device->d3d_initialized)
2389 context = context_acquire(surface->resource.device, NULL);
2390 surface_load_location(surface, context, surface->resource.map_binding);
2391 if (context)
2392 context_release(context);
2395 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2396 surface_invalidate_location(surface, ~surface->resource.map_binding);
2398 switch (surface->resource.map_binding)
2400 case WINED3D_LOCATION_SYSMEM:
2401 base_memory = surface->resource.heap_memory;
2402 break;
2404 case WINED3D_LOCATION_USER_MEMORY:
2405 base_memory = surface->container->user_memory;
2406 break;
2408 case WINED3D_LOCATION_DIB:
2409 base_memory = surface->dib.bitmap_data;
2410 break;
2412 case WINED3D_LOCATION_BUFFER:
2413 context = context_acquire(device, NULL);
2414 gl_info = context->gl_info;
2416 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
2417 base_memory = GL_EXTCALL(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE));
2418 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
2419 checkGLcall("map PBO");
2421 context_release(context);
2422 break;
2424 default:
2425 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2426 base_memory = NULL;
2429 if (fmt_flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2430 map_desc->row_pitch = surface->resource.width * format->byte_count;
2431 else
2432 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2433 map_desc->slice_pitch = surface->resource.height * map_desc->row_pitch;
2435 if (!box)
2437 map_desc->data = base_memory;
2438 surface->lockedRect.left = 0;
2439 surface->lockedRect.top = 0;
2440 surface->lockedRect.right = surface->resource.width;
2441 surface->lockedRect.bottom = surface->resource.height;
2443 else
2445 if ((fmt_flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2447 /* Compressed textures are block based, so calculate the offset of
2448 * the block that contains the top-left pixel of the locked rectangle. */
2449 map_desc->data = base_memory
2450 + ((box->top / format->block_height) * map_desc->row_pitch)
2451 + ((box->left / format->block_width) * format->block_byte_count);
2453 else
2455 map_desc->data = base_memory
2456 + (map_desc->row_pitch * box->top)
2457 + (box->left * format->byte_count);
2459 surface->lockedRect.left = box->left;
2460 surface->lockedRect.top = box->top;
2461 surface->lockedRect.right = box->right;
2462 surface->lockedRect.bottom = box->bottom;
2465 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2466 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2468 return WINED3D_OK;
2471 static void read_from_framebuffer(struct wined3d_surface *surface,
2472 struct wined3d_context *old_ctx, DWORD dst_location)
2474 struct wined3d_device *device = surface->resource.device;
2475 const struct wined3d_gl_info *gl_info;
2476 struct wined3d_context *context = old_ctx;
2477 struct wined3d_surface *restore_rt = NULL;
2478 BYTE *mem;
2479 BYTE *row, *top, *bottom;
2480 int i;
2481 BOOL srcIsUpsideDown;
2482 struct wined3d_bo_address data;
2484 surface_get_memory(surface, &data, dst_location);
2486 if (surface != old_ctx->current_rt)
2488 restore_rt = old_ctx->current_rt;
2489 context = context_acquire(device, surface);
2492 context_apply_blit_state(context, device);
2493 gl_info = context->gl_info;
2495 /* Select the correct read buffer, and give some debug output.
2496 * There is no need to keep track of the current read buffer or reset it, every part of the code
2497 * that reads sets the read buffer as desired.
2499 if (wined3d_resource_is_offscreen(&surface->container->resource))
2501 /* Mapping the primary render target which is not on a swapchain.
2502 * Read from the back buffer. */
2503 TRACE("Mapping offscreen render target.\n");
2504 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2505 srcIsUpsideDown = TRUE;
2507 else
2509 /* Onscreen surfaces are always part of a swapchain */
2510 GLenum buffer = surface_get_gl_buffer(surface);
2511 TRACE("Mapping %#x buffer.\n", buffer);
2512 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2513 checkGLcall("glReadBuffer");
2514 srcIsUpsideDown = FALSE;
2517 if (data.buffer_object)
2519 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
2520 checkGLcall("glBindBuffer");
2523 /* Setup pixel store pack state -- to glReadPixels into the correct place */
2524 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH,
2525 wined3d_surface_get_pitch(surface) / surface->resource.format->byte_count);
2526 checkGLcall("glPixelStorei");
2528 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
2529 surface->resource.width, surface->resource.height,
2530 surface->resource.format->glFormat,
2531 surface->resource.format->glType, data.addr);
2532 checkGLcall("glReadPixels");
2534 /* Reset previous pixel store pack state */
2535 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
2536 checkGLcall("glPixelStorei");
2538 if (!srcIsUpsideDown)
2540 /* glReadPixels returns the image upside down, and there is no way to prevent this.
2541 * Flip the lines in software. */
2542 UINT pitch = wined3d_surface_get_pitch(surface);
2544 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
2545 goto error;
2547 if (data.buffer_object)
2549 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
2550 checkGLcall("glMapBuffer");
2552 else
2553 mem = data.addr;
2555 top = mem;
2556 bottom = mem + pitch * (surface->resource.height - 1);
2557 for (i = 0; i < surface->resource.height / 2; i++)
2559 memcpy(row, top, pitch);
2560 memcpy(top, bottom, pitch);
2561 memcpy(bottom, row, pitch);
2562 top += pitch;
2563 bottom -= pitch;
2565 HeapFree(GetProcessHeap(), 0, row);
2567 if (data.buffer_object)
2568 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
2571 error:
2572 if (data.buffer_object)
2574 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2575 checkGLcall("glBindBuffer");
2578 if (restore_rt)
2579 context_restore(context, restore_rt);
2582 /* Read the framebuffer contents into a texture. Note that this function
2583 * doesn't do any kind of flipping. Using this on an onscreen surface will
2584 * result in a flipped D3D texture.
2586 * Context activation is done by the caller. This function may temporarily
2587 * switch to a different context and restore the original one before return. */
2588 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
2590 struct wined3d_device *device = surface->resource.device;
2591 const struct wined3d_gl_info *gl_info;
2592 struct wined3d_context *context = old_ctx;
2593 struct wined3d_surface *restore_rt = NULL;
2595 if (old_ctx->current_rt != surface)
2597 restore_rt = old_ctx->current_rt;
2598 context = context_acquire(device, surface);
2601 gl_info = context->gl_info;
2602 device_invalidate_state(device, STATE_FRAMEBUFFER);
2604 wined3d_texture_prepare_texture(surface->container, context, srgb);
2605 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
2607 TRACE("Reading back offscreen render target %p.\n", surface);
2609 if (wined3d_resource_is_offscreen(&surface->container->resource))
2610 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2611 else
2612 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
2613 checkGLcall("glReadBuffer");
2615 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
2616 0, 0, 0, 0, surface->resource.width, surface->resource.height);
2617 checkGLcall("glCopyTexSubImage2D");
2619 if (restore_rt)
2620 context_restore(context, restore_rt);
2623 static void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2625 if (multisample)
2627 DWORD samples;
2629 if (surface->rb_multisample)
2630 return;
2632 /* TODO: Nvidia exposes their Coverage Sample Anti-Aliasing (CSAA) feature
2633 * through type == MULTISAMPLE_XX and quality != 0. This could be mapped
2634 * to GL_NV_framebuffer_multisample_coverage.
2636 * AMD has a similar feature called Enhanced Quality Anti-Aliasing (EQAA),
2637 * but it does not have an equivalent OpenGL extension. */
2639 /* We advertise as many WINED3D_MULTISAMPLE_NON_MASKABLE quality levels
2640 * as the count of advertised multisample types for the surface format. */
2641 if (surface->resource.multisample_type == WINED3D_MULTISAMPLE_NON_MASKABLE)
2643 const struct wined3d_format *format = surface->resource.format;
2644 unsigned int i, count = 0;
2646 for (i = 0; i < sizeof(format->multisample_types) * 8; ++i)
2648 if (format->multisample_types & 1u << i)
2650 if (surface->resource.multisample_quality == count++)
2651 break;
2654 samples = i + 1;
2656 else
2658 samples = surface->resource.multisample_type;
2661 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2662 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2663 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
2664 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
2665 checkGLcall("glRenderbufferStorageMultisample()");
2666 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2668 else
2670 if (surface->rb_resolved)
2671 return;
2673 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2674 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2675 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
2676 surface->pow2Width, surface->pow2Height);
2677 checkGLcall("glRenderbufferStorage()");
2678 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2682 /* Does a direct frame buffer -> texture copy. Stretching is done with single
2683 * pixel copy calls. */
2684 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2685 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2687 struct wined3d_device *device = dst_surface->resource.device;
2688 const struct wined3d_gl_info *gl_info;
2689 float xrel, yrel;
2690 struct wined3d_context *context;
2691 BOOL upsidedown = FALSE;
2692 RECT dst_rect = *dst_rect_in;
2694 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2695 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2697 if(dst_rect.top > dst_rect.bottom) {
2698 UINT tmp = dst_rect.bottom;
2699 dst_rect.bottom = dst_rect.top;
2700 dst_rect.top = tmp;
2701 upsidedown = TRUE;
2704 context = context_acquire(device, src_surface);
2705 gl_info = context->gl_info;
2706 context_apply_blit_state(context, device);
2707 wined3d_texture_load(dst_surface->container, context, FALSE);
2709 /* Bind the target texture */
2710 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
2711 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
2713 TRACE("Reading from an offscreen target\n");
2714 upsidedown = !upsidedown;
2715 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2717 else
2719 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
2721 checkGLcall("glReadBuffer");
2723 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
2724 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
2726 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2728 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2730 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2731 ERR("Texture filtering not supported in direct blit.\n");
2733 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2734 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2736 ERR("Texture filtering not supported in direct blit\n");
2739 if (upsidedown
2740 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2741 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2743 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
2744 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2745 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
2746 src_rect->left, src_surface->resource.height - src_rect->bottom,
2747 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2749 else
2751 LONG row;
2752 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
2753 /* I have to process this row by row to swap the image,
2754 * otherwise it would be upside down, so stretching in y direction
2755 * doesn't cost extra time
2757 * However, stretching in x direction can be avoided if not necessary
2759 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
2760 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2762 /* Well, that stuff works, but it's very slow.
2763 * find a better way instead
2765 LONG col;
2767 for (col = dst_rect.left; col < dst_rect.right; ++col)
2769 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2770 dst_rect.left + col /* x offset */, row /* y offset */,
2771 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
2774 else
2776 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2777 dst_rect.left /* x offset */, row /* y offset */,
2778 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
2782 checkGLcall("glCopyTexSubImage2D");
2784 context_release(context);
2786 /* The texture is now most up to date - If the surface is a render target
2787 * and has a drawable, this path is never entered. */
2788 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
2789 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
2792 /* Uses the hardware to stretch and flip the image */
2793 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2794 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2796 struct wined3d_device *device = dst_surface->resource.device;
2797 GLuint src, backup = 0;
2798 float left, right, top, bottom; /* Texture coordinates */
2799 UINT fbwidth = src_surface->resource.width;
2800 UINT fbheight = src_surface->resource.height;
2801 const struct wined3d_gl_info *gl_info;
2802 struct wined3d_context *context;
2803 GLenum drawBuffer = GL_BACK;
2804 GLenum texture_target;
2805 BOOL noBackBufferBackup;
2806 BOOL src_offscreen;
2807 BOOL upsidedown = FALSE;
2808 RECT dst_rect = *dst_rect_in;
2810 TRACE("Using hwstretch blit\n");
2811 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2812 context = context_acquire(device, src_surface);
2813 gl_info = context->gl_info;
2814 context_apply_blit_state(context, device);
2815 wined3d_texture_load(dst_surface->container, context, FALSE);
2817 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
2818 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2819 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
2821 /* Get it a description */
2822 wined3d_texture_load(src_surface->container, context, FALSE);
2825 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2826 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2828 if (context->aux_buffers >= 2)
2830 /* Got more than one aux buffer? Use the 2nd aux buffer */
2831 drawBuffer = GL_AUX1;
2833 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
2835 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2836 drawBuffer = GL_AUX0;
2839 if (noBackBufferBackup)
2841 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
2842 checkGLcall("glGenTextures");
2843 context_bind_texture(context, GL_TEXTURE_2D, backup);
2844 texture_target = GL_TEXTURE_2D;
2846 else
2848 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2849 * we are reading from the back buffer, the backup can be used as source texture
2851 texture_target = src_surface->texture_target;
2852 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
2853 gl_info->gl_ops.gl.p_glEnable(texture_target);
2854 checkGLcall("glEnable(texture_target)");
2856 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2857 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
2860 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2861 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2863 if(dst_rect.top > dst_rect.bottom) {
2864 UINT tmp = dst_rect.bottom;
2865 dst_rect.bottom = dst_rect.top;
2866 dst_rect.top = tmp;
2867 upsidedown = TRUE;
2870 if (src_offscreen)
2872 TRACE("Reading from an offscreen target\n");
2873 upsidedown = !upsidedown;
2874 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2876 else
2878 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
2881 /* TODO: Only back up the part that will be overwritten */
2882 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
2884 checkGLcall("glCopyTexSubImage2D");
2886 /* No issue with overriding these - the sampler is dirty due to blit usage */
2887 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
2888 checkGLcall("glTexParameteri");
2889 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2890 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
2891 checkGLcall("glTexParameteri");
2893 if (!src_surface->container->swapchain
2894 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
2896 src = backup ? backup : src_surface->container->texture_rgb.name;
2898 else
2900 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
2901 checkGLcall("glReadBuffer(GL_FRONT)");
2903 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
2904 checkGLcall("glGenTextures(1, &src)");
2905 context_bind_texture(context, GL_TEXTURE_2D, src);
2907 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
2908 * out for power of 2 sizes
2910 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
2911 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2912 checkGLcall("glTexImage2D");
2913 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
2915 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2916 checkGLcall("glTexParameteri");
2917 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2918 checkGLcall("glTexParameteri");
2920 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
2921 checkGLcall("glReadBuffer(GL_BACK)");
2923 if (texture_target != GL_TEXTURE_2D)
2925 gl_info->gl_ops.gl.p_glDisable(texture_target);
2926 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2927 texture_target = GL_TEXTURE_2D;
2930 checkGLcall("glEnd and previous");
2932 left = src_rect->left;
2933 right = src_rect->right;
2935 if (!upsidedown)
2937 top = src_surface->resource.height - src_rect->top;
2938 bottom = src_surface->resource.height - src_rect->bottom;
2940 else
2942 top = src_surface->resource.height - src_rect->bottom;
2943 bottom = src_surface->resource.height - src_rect->top;
2946 if (src_surface->container->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
2948 left /= src_surface->pow2Width;
2949 right /= src_surface->pow2Width;
2950 top /= src_surface->pow2Height;
2951 bottom /= src_surface->pow2Height;
2954 /* draw the source texture stretched and upside down. The correct surface is bound already */
2955 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2956 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2958 context_set_draw_buffer(context, drawBuffer);
2959 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
2961 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2962 /* bottom left */
2963 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
2964 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2966 /* top left */
2967 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
2968 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
2970 /* top right */
2971 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
2972 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2974 /* bottom right */
2975 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
2976 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
2977 gl_info->gl_ops.gl.p_glEnd();
2978 checkGLcall("glEnd and previous");
2980 if (texture_target != dst_surface->texture_target)
2982 gl_info->gl_ops.gl.p_glDisable(texture_target);
2983 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
2984 texture_target = dst_surface->texture_target;
2987 /* Now read the stretched and upside down image into the destination texture */
2988 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
2989 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
2991 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
2992 0, 0, /* We blitted the image to the origin */
2993 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2994 checkGLcall("glCopyTexSubImage2D");
2996 if (drawBuffer == GL_BACK)
2998 /* Write the back buffer backup back. */
2999 if (backup)
3001 if (texture_target != GL_TEXTURE_2D)
3003 gl_info->gl_ops.gl.p_glDisable(texture_target);
3004 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3005 texture_target = GL_TEXTURE_2D;
3007 context_bind_texture(context, GL_TEXTURE_2D, backup);
3009 else
3011 if (texture_target != src_surface->texture_target)
3013 gl_info->gl_ops.gl.p_glDisable(texture_target);
3014 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3015 texture_target = src_surface->texture_target;
3017 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3020 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3021 /* top left */
3022 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3023 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3025 /* bottom left */
3026 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3027 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3029 /* bottom right */
3030 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3031 (float)fbheight / (float)src_surface->pow2Height);
3032 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3034 /* top right */
3035 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3036 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3037 gl_info->gl_ops.gl.p_glEnd();
3039 gl_info->gl_ops.gl.p_glDisable(texture_target);
3040 checkGLcall("glDisable(texture_target)");
3042 /* Cleanup */
3043 if (src != src_surface->container->texture_rgb.name && src != backup)
3045 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3046 checkGLcall("glDeleteTextures(1, &src)");
3048 if (backup)
3050 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3051 checkGLcall("glDeleteTextures(1, &backup)");
3054 if (wined3d_settings.strict_draw_ordering)
3055 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3057 context_release(context);
3059 /* The texture is now most up to date - If the surface is a render target
3060 * and has a drawable, this path is never entered. */
3061 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3062 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3065 /* Front buffer coordinates are always full screen coordinates, but our GL
3066 * drawable is limited to the window's client area. The sysmem and texture
3067 * copies do have the full screen size. Note that GL has a bottom-left
3068 * origin, while D3D has a top-left origin. */
3069 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3071 UINT drawable_height;
3073 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3075 POINT offset = {0, 0};
3076 RECT windowsize;
3078 ScreenToClient(window, &offset);
3079 OffsetRect(rect, offset.x, offset.y);
3081 GetClientRect(window, &windowsize);
3082 drawable_height = windowsize.bottom - windowsize.top;
3084 else
3086 drawable_height = surface->resource.height;
3089 rect->top = drawable_height - rect->top;
3090 rect->bottom = drawable_height - rect->bottom;
3093 /* Context activation is done by the caller. */
3094 static void surface_blt_to_drawable(const struct wined3d_device *device,
3095 struct wined3d_context *old_ctx,
3096 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3097 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3098 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3100 const struct wined3d_gl_info *gl_info;
3101 struct wined3d_context *context = old_ctx;
3102 struct wined3d_surface *restore_rt = NULL;
3103 RECT src_rect, dst_rect;
3105 src_rect = *src_rect_in;
3106 dst_rect = *dst_rect_in;
3109 if (old_ctx->current_rt != dst_surface)
3111 restore_rt = old_ctx->current_rt;
3112 context = context_acquire(device, dst_surface);
3115 gl_info = context->gl_info;
3117 /* Make sure the surface is up-to-date. This should probably use
3118 * surface_load_location() and worry about the destination surface too,
3119 * unless we're overwriting it completely. */
3120 wined3d_texture_load(src_surface->container, context, FALSE);
3122 /* Activate the destination context, set it up for blitting */
3123 context_apply_blit_state(context, device);
3125 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3126 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3128 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
3130 if (alpha_test)
3132 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3133 checkGLcall("glEnable(GL_ALPHA_TEST)");
3135 /* For P8 surfaces, the alpha component contains the palette index.
3136 * Which means that the colorkey is one of the palette entries. In
3137 * other cases pixels that should be masked away have alpha set to 0. */
3138 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3139 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3140 (float)src_surface->container->async.src_blt_color_key.color_space_low_value / 255.0f);
3141 else
3142 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3143 checkGLcall("glAlphaFunc");
3145 else
3147 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3148 checkGLcall("glDisable(GL_ALPHA_TEST)");
3151 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3153 if (alpha_test)
3155 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3156 checkGLcall("glDisable(GL_ALPHA_TEST)");
3159 /* Leave the opengl state valid for blitting */
3160 device->blitter->unset_shader(context->gl_info);
3162 if (wined3d_settings.strict_draw_ordering
3163 || (dst_surface->container->swapchain
3164 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3165 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3167 if (restore_rt)
3168 context_restore(context, restore_rt);
3171 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3173 struct wined3d_resource *resource = &s->container->resource;
3174 struct wined3d_device *device = resource->device;
3175 struct wined3d_rendertarget_view_desc view_desc;
3176 struct wined3d_rendertarget_view *view;
3177 const struct blit_shader *blitter;
3178 HRESULT hr;
3180 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
3181 WINED3D_BLIT_OP_COLOR_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
3183 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3184 return WINED3DERR_INVALIDCALL;
3187 view_desc.format_id = resource->format->id;
3188 view_desc.u.texture.level_idx = s->texture_level;
3189 view_desc.u.texture.layer_idx = s->texture_layer;
3190 view_desc.u.texture.layer_count = 1;
3191 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
3192 resource, NULL, &wined3d_null_parent_ops, &view)))
3194 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
3195 return hr;
3198 hr = blitter->color_fill(device, view, rect, color);
3199 wined3d_rendertarget_view_decref(view);
3201 return hr;
3204 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3205 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3206 enum wined3d_texture_filter_type filter)
3208 struct wined3d_device *device = dst_surface->resource.device;
3209 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3210 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3212 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3213 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3214 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3216 /* Get the swapchain. One of the surfaces has to be a primary surface */
3217 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3219 WARN("Destination is in sysmem, rejecting gl blt\n");
3220 return WINED3DERR_INVALIDCALL;
3223 dst_swapchain = dst_surface->container->swapchain;
3225 if (src_surface)
3227 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3229 WARN("Src is in sysmem, rejecting gl blt\n");
3230 return WINED3DERR_INVALIDCALL;
3233 src_swapchain = src_surface->container->swapchain;
3235 else
3237 src_swapchain = NULL;
3240 /* Early sort out of cases where no render target is used */
3241 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
3243 TRACE("No surface is render target, not using hardware blit.\n");
3244 return WINED3DERR_INVALIDCALL;
3247 /* No destination color keying supported */
3248 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
3250 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3251 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3252 return WINED3DERR_INVALIDCALL;
3255 if (dst_swapchain && dst_swapchain == src_swapchain)
3257 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3258 return WINED3DERR_INVALIDCALL;
3261 if (dst_swapchain && src_swapchain)
3263 FIXME("Implement hardware blit between two different swapchains\n");
3264 return WINED3DERR_INVALIDCALL;
3267 if (dst_swapchain)
3269 /* Handled with regular texture -> swapchain blit */
3270 if (src_surface == rt)
3271 TRACE("Blit from active render target to a swapchain\n");
3273 else if (src_swapchain && dst_surface == rt)
3275 FIXME("Implement blit from a swapchain to the active render target\n");
3276 return WINED3DERR_INVALIDCALL;
3279 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
3281 /* Blit from render target to texture */
3282 BOOL stretchx;
3284 /* P8 read back is not implemented */
3285 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
3286 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
3288 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3289 return WINED3DERR_INVALIDCALL;
3292 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3294 TRACE("Color keying not supported by frame buffer to texture blit\n");
3295 return WINED3DERR_INVALIDCALL;
3296 /* Destination color key is checked above */
3299 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
3300 stretchx = TRUE;
3301 else
3302 stretchx = FALSE;
3304 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3305 * flip the image nor scale it.
3307 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3308 * -> If the app wants an image width an unscaled width, copy it line per line
3309 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
3310 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3311 * back buffer. This is slower than reading line per line, thus not used for flipping
3312 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3313 * pixel by pixel. */
3314 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
3315 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
3317 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
3318 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
3320 else
3322 TRACE("Using hardware stretching to flip / stretch the texture.\n");
3323 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
3326 surface_evict_sysmem(dst_surface);
3328 return WINED3D_OK;
3331 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3332 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3333 return WINED3DERR_INVALIDCALL;
3336 /* Context activation is done by the caller. */
3337 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
3338 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
3340 struct wined3d_device *device = surface->resource.device;
3341 const struct wined3d_gl_info *gl_info = context->gl_info;
3342 GLint compare_mode = GL_NONE;
3343 struct blt_info info;
3344 GLint old_binding = 0;
3345 RECT rect;
3347 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
3349 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
3350 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
3351 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3352 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
3353 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
3354 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
3355 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
3356 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
3357 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3358 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
3359 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
3361 SetRect(&rect, 0, h, w, 0);
3362 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
3363 context_active_texture(context, context->gl_info, 0);
3364 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
3365 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
3366 if (gl_info->supported[ARB_SHADOW])
3368 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
3369 if (compare_mode != GL_NONE)
3370 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
3373 device->shader_backend->shader_select_depth_blt(device->shader_priv,
3374 gl_info, info.tex_type, &surface->ds_current_size);
3376 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
3377 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
3378 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
3379 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
3380 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
3381 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
3382 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
3383 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
3384 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
3385 gl_info->gl_ops.gl.p_glEnd();
3387 if (compare_mode != GL_NONE)
3388 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
3389 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
3391 gl_info->gl_ops.gl.p_glPopAttrib();
3393 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
3396 void surface_modify_ds_location(struct wined3d_surface *surface,
3397 DWORD location, UINT w, UINT h)
3399 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
3401 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
3402 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
3403 wined3d_texture_set_dirty(surface->container);
3405 surface->ds_current_size.cx = w;
3406 surface->ds_current_size.cy = h;
3407 surface->locations = location;
3410 /* Context activation is done by the caller. */
3411 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3413 const struct wined3d_gl_info *gl_info = context->gl_info;
3414 struct wined3d_device *device = surface->resource.device;
3415 GLsizei w, h;
3417 TRACE("surface %p, context %p, new location %#x.\n", surface, context, location);
3419 /* TODO: Make this work for modes other than FBO */
3420 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3422 if (!(surface->locations & location))
3424 w = surface->ds_current_size.cx;
3425 h = surface->ds_current_size.cy;
3426 surface->ds_current_size.cx = 0;
3427 surface->ds_current_size.cy = 0;
3429 else
3431 w = surface->resource.width;
3432 h = surface->resource.height;
3435 if (surface->ds_current_size.cx == surface->resource.width
3436 && surface->ds_current_size.cy == surface->resource.height)
3438 TRACE("Location (%#x) is already up to date.\n", location);
3439 return;
3442 if (surface->current_renderbuffer)
3444 FIXME("Not supported with fixed up depth stencil.\n");
3445 return;
3448 wined3d_surface_prepare(surface, context, location);
3449 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3451 TRACE("Surface was discarded, no need copy data.\n");
3452 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
3453 surface->locations |= location;
3454 surface->ds_current_size.cx = surface->resource.width;
3455 surface->ds_current_size.cy = surface->resource.height;
3456 return;
3459 if (!surface->locations)
3461 FIXME("No up to date depth stencil location.\n");
3462 surface->locations |= location;
3463 surface->ds_current_size.cx = surface->resource.width;
3464 surface->ds_current_size.cy = surface->resource.height;
3465 return;
3468 if (location == WINED3D_LOCATION_TEXTURE_RGB)
3470 GLint old_binding = 0;
3471 GLenum bind_target;
3473 /* The render target is allowed to be smaller than the depth/stencil
3474 * buffer, so the onscreen depth/stencil buffer is potentially smaller
3475 * than the offscreen surface. Don't overwrite the offscreen surface
3476 * with undefined data. */
3477 w = min(w, context->swapchain->desc.backbuffer_width);
3478 h = min(h, context->swapchain->desc.backbuffer_height);
3480 TRACE("Copying onscreen depth buffer to depth texture.\n");
3482 if (!device->depth_blt_texture)
3483 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
3485 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3486 * directly on the FBO texture. That's because we need to flip. */
3487 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3488 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3489 NULL, WINED3D_LOCATION_DRAWABLE);
3490 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
3492 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
3493 bind_target = GL_TEXTURE_RECTANGLE_ARB;
3495 else
3497 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3498 bind_target = GL_TEXTURE_2D;
3500 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
3501 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
3502 * internal format, because the internal format might include stencil
3503 * data. In principle we should copy stencil data as well, but unless
3504 * the driver supports stencil export it's hard to do, and doesn't
3505 * seem to be needed in practice. If the hardware doesn't support
3506 * writing stencil data, the glCopyTexImage2D() call might trigger
3507 * software fallbacks. */
3508 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
3509 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3510 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3511 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3512 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3513 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
3514 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
3516 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3517 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
3518 context_set_draw_buffer(context, GL_NONE);
3520 /* Do the actual blit */
3521 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
3522 checkGLcall("depth_blt");
3524 context_invalidate_state(context, STATE_FRAMEBUFFER);
3526 if (wined3d_settings.strict_draw_ordering)
3527 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3529 else if (location == WINED3D_LOCATION_DRAWABLE)
3531 TRACE("Copying depth texture to onscreen depth buffer.\n");
3533 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3534 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3535 NULL, WINED3D_LOCATION_DRAWABLE);
3536 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
3537 0, surface->pow2Height - h, w, h, surface->texture_target);
3538 checkGLcall("depth_blt");
3540 context_invalidate_state(context, STATE_FRAMEBUFFER);
3542 if (wined3d_settings.strict_draw_ordering)
3543 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3545 else
3547 ERR("Invalid location (%#x) specified.\n", location);
3550 surface->locations |= location;
3551 surface->ds_current_size.cx = surface->resource.width;
3552 surface->ds_current_size.cy = surface->resource.height;
3555 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
3557 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3559 surface->locations |= location;
3562 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
3564 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3566 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3567 wined3d_texture_set_dirty(surface->container);
3568 surface->locations &= ~location;
3570 if (!surface->locations)
3571 ERR("Surface %p does not have any up to date location.\n", surface);
3574 static DWORD resource_access_from_location(DWORD location)
3576 switch (location)
3578 case WINED3D_LOCATION_SYSMEM:
3579 case WINED3D_LOCATION_USER_MEMORY:
3580 case WINED3D_LOCATION_DIB:
3581 case WINED3D_LOCATION_BUFFER:
3582 return WINED3D_RESOURCE_ACCESS_CPU;
3584 case WINED3D_LOCATION_DRAWABLE:
3585 case WINED3D_LOCATION_TEXTURE_SRGB:
3586 case WINED3D_LOCATION_TEXTURE_RGB:
3587 case WINED3D_LOCATION_RB_MULTISAMPLE:
3588 case WINED3D_LOCATION_RB_RESOLVED:
3589 return WINED3D_RESOURCE_ACCESS_GPU;
3591 default:
3592 FIXME("Unhandled location %#x.\n", location);
3593 return 0;
3597 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
3599 struct wined3d_device *device = surface->resource.device;
3600 struct wined3d_context *context;
3601 const struct wined3d_gl_info *gl_info;
3602 struct wined3d_bo_address dst, src;
3603 UINT size = surface->resource.size;
3605 surface_get_memory(surface, &dst, location);
3606 surface_get_memory(surface, &src, surface->locations);
3608 if (dst.buffer_object)
3610 context = context_acquire(device, NULL);
3611 gl_info = context->gl_info;
3612 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
3613 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr));
3614 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
3615 checkGLcall("Upload PBO");
3616 context_release(context);
3617 return;
3619 if (src.buffer_object)
3621 context = context_acquire(device, NULL);
3622 gl_info = context->gl_info;
3623 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
3624 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr));
3625 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
3626 checkGLcall("Download PBO");
3627 context_release(context);
3628 return;
3630 memcpy(dst.addr, src.addr, size);
3633 /* Context activation is done by the caller. */
3634 static void surface_load_sysmem(struct wined3d_surface *surface,
3635 struct wined3d_context *context, DWORD dst_location)
3637 const struct wined3d_gl_info *gl_info = context->gl_info;
3639 if (surface->locations & surface_simple_locations)
3641 surface_copy_simple_location(surface, dst_location);
3642 return;
3645 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
3646 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3648 /* Download the surface to system memory. */
3649 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3651 wined3d_texture_bind_and_dirtify(surface->container, context,
3652 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
3653 surface_download_data(surface, gl_info, dst_location);
3655 return;
3658 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
3660 read_from_framebuffer(surface, context, dst_location);
3661 return;
3664 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
3665 surface, wined3d_debug_location(surface->locations));
3668 /* Context activation is done by the caller. */
3669 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
3670 struct wined3d_context *context)
3672 RECT r;
3674 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3675 && wined3d_resource_is_offscreen(&surface->container->resource))
3677 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
3678 return WINED3DERR_INVALIDCALL;
3681 surface_get_rect(surface, NULL, &r);
3682 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3683 surface_blt_to_drawable(surface->resource.device, context,
3684 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
3686 return WINED3D_OK;
3689 static HRESULT surface_load_texture(struct wined3d_surface *surface,
3690 struct wined3d_context *context, BOOL srgb)
3692 const struct wined3d_gl_info *gl_info = context->gl_info;
3693 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
3694 unsigned int width, src_pitch, dst_row_pitch, dst_slice_pitch;
3695 struct wined3d_device *device = surface->resource.device;
3696 const struct wined3d_color_key_conversion *conversion;
3697 struct wined3d_texture *texture = surface->container;
3698 struct wined3d_bo_address data;
3699 struct wined3d_format format;
3700 POINT dst_point = {0, 0};
3701 BYTE *mem = NULL;
3703 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
3704 && wined3d_resource_is_offscreen(&texture->resource)
3705 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
3707 surface_load_fb_texture(surface, srgb, context);
3709 return WINED3D_OK;
3712 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
3713 && (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
3714 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3715 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3716 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3718 if (srgb)
3719 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
3720 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
3721 else
3722 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
3723 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
3725 return WINED3D_OK;
3728 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
3729 && (!srgb || (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
3730 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3731 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3732 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3734 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
3735 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
3736 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
3737 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3739 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
3740 &rect, surface, dst_location, &rect);
3742 return WINED3D_OK;
3745 /* Upload from system memory */
3747 if (srgb)
3749 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
3750 == WINED3D_LOCATION_TEXTURE_RGB)
3752 /* Performance warning... */
3753 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
3754 surface_prepare_map_memory(surface);
3755 surface_load_location(surface, context, surface->resource.map_binding);
3758 else
3760 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
3761 == WINED3D_LOCATION_TEXTURE_SRGB)
3763 /* Performance warning... */
3764 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
3765 surface_prepare_map_memory(surface);
3766 surface_load_location(surface, context, surface->resource.map_binding);
3770 if (!(surface->locations & surface_simple_locations))
3772 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
3773 /* Lets hope we get it from somewhere... */
3774 surface_prepare_system_memory(surface);
3775 surface_load_location(surface, context, WINED3D_LOCATION_SYSMEM);
3778 wined3d_texture_prepare_texture(texture, context, srgb);
3779 wined3d_texture_bind_and_dirtify(texture, context, srgb);
3781 width = surface->resource.width;
3782 src_pitch = wined3d_surface_get_pitch(surface);
3784 format = *texture->resource.format;
3785 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
3786 format = *wined3d_get_format(gl_info, conversion->dst_format);
3788 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
3789 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
3790 * getting called. */
3791 if ((format.convert || conversion) && surface->pbo)
3793 TRACE("Removing the pbo attached to surface %p.\n", surface);
3795 if (surface->flags & SFLAG_DIBSECTION)
3796 surface->resource.map_binding = WINED3D_LOCATION_DIB;
3797 else
3798 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
3800 surface_prepare_map_memory(surface);
3801 surface_load_location(surface, context, surface->resource.map_binding);
3802 surface_remove_pbo(surface, gl_info);
3805 surface_get_memory(surface, &data, surface->locations);
3806 if (format.convert)
3808 /* This code is entered for texture formats which need a fixup. */
3809 UINT height = surface->resource.height;
3811 format.byte_count = format.conv_byte_count;
3812 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
3814 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
3816 ERR("Out of memory (%u).\n", dst_slice_pitch);
3817 context_release(context);
3818 return E_OUTOFMEMORY;
3820 format.convert(data.addr, mem, src_pitch, src_pitch * height,
3821 dst_row_pitch, dst_slice_pitch, width, height, 1);
3822 src_pitch = dst_row_pitch;
3823 data.addr = mem;
3825 else if (conversion)
3827 /* This code is only entered for color keying fixups */
3828 struct wined3d_palette *palette = NULL;
3829 UINT height = surface->resource.height;
3831 wined3d_format_calculate_pitch(&format, device->surface_alignment,
3832 width, height, &dst_row_pitch, &dst_slice_pitch);
3834 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
3836 ERR("Out of memory (%u).\n", dst_slice_pitch);
3837 context_release(context);
3838 return E_OUTOFMEMORY;
3840 if (texture->swapchain && texture->swapchain->palette)
3841 palette = texture->swapchain->palette;
3842 conversion->convert(data.addr, src_pitch, mem, dst_row_pitch,
3843 width, height, palette, &texture->async.gl_color_key);
3844 src_pitch = dst_row_pitch;
3845 data.addr = mem;
3848 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
3849 src_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
3851 HeapFree(GetProcessHeap(), 0, mem);
3853 return WINED3D_OK;
3856 /* Context activation is done by the caller. */
3857 static void surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
3858 DWORD dst_location)
3860 const RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3861 DWORD src_location;
3863 if (surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE)
3864 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
3865 else if (surface->locations & WINED3D_LOCATION_RB_RESOLVED)
3866 src_location = WINED3D_LOCATION_RB_RESOLVED;
3867 else if (surface->locations & WINED3D_LOCATION_TEXTURE_SRGB)
3868 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
3869 else /* surface_blt_fbo will load the source location if necessary. */
3870 src_location = WINED3D_LOCATION_TEXTURE_RGB;
3872 surface_blt_fbo(surface->resource.device, context, WINED3D_TEXF_POINT,
3873 surface, src_location, &rect, surface, dst_location, &rect);
3876 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
3877 HRESULT surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3879 HRESULT hr;
3881 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3883 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3885 if (location == WINED3D_LOCATION_TEXTURE_RGB
3886 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
3888 surface_load_ds_location(surface, context, location);
3889 return WINED3D_OK;
3891 else if (location & surface->locations
3892 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
3894 /* Already up to date, nothing to do. */
3895 return WINED3D_OK;
3897 else
3899 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
3900 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
3901 return WINED3DERR_INVALIDCALL;
3905 if (surface->locations & location)
3907 TRACE("Location already up to date.\n");
3908 return WINED3D_OK;
3911 if (WARN_ON(d3d_surface))
3913 DWORD required_access = resource_access_from_location(location);
3914 if ((surface->resource.access_flags & required_access) != required_access)
3915 WARN("Operation requires %#x access, but surface only has %#x.\n",
3916 required_access, surface->resource.access_flags);
3919 if (!surface->locations)
3921 ERR("Surface %p does not have any up to date location.\n", surface);
3922 return WINED3DERR_INVALIDCALL;
3925 switch (location)
3927 case WINED3D_LOCATION_DIB:
3928 case WINED3D_LOCATION_USER_MEMORY:
3929 case WINED3D_LOCATION_SYSMEM:
3930 case WINED3D_LOCATION_BUFFER:
3931 surface_load_sysmem(surface, context, location);
3932 break;
3934 case WINED3D_LOCATION_DRAWABLE:
3935 if (FAILED(hr = surface_load_drawable(surface, context)))
3936 return hr;
3937 break;
3939 case WINED3D_LOCATION_RB_RESOLVED:
3940 case WINED3D_LOCATION_RB_MULTISAMPLE:
3941 surface_load_renderbuffer(surface, context, location);
3942 break;
3944 case WINED3D_LOCATION_TEXTURE_RGB:
3945 case WINED3D_LOCATION_TEXTURE_SRGB:
3946 if (FAILED(hr = surface_load_texture(surface, context,
3947 location == WINED3D_LOCATION_TEXTURE_SRGB)))
3948 return hr;
3949 break;
3951 default:
3952 ERR("Don't know how to handle location %#x.\n", location);
3953 break;
3956 surface_validate_location(surface, location);
3958 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
3959 surface_evict_sysmem(surface);
3961 return WINED3D_OK;
3964 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
3965 /* Context activation is done by the caller. */
3966 static void ffp_blit_free(struct wined3d_device *device) { }
3968 /* Context activation is done by the caller. */
3969 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
3970 const struct wined3d_color_key *color_key)
3972 const struct wined3d_gl_info *gl_info = context->gl_info;
3974 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
3975 checkGLcall("glEnable(target)");
3977 return WINED3D_OK;
3980 /* Context activation is done by the caller. */
3981 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
3983 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
3984 checkGLcall("glDisable(GL_TEXTURE_2D)");
3985 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
3987 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3988 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3990 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
3992 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
3993 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
3997 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
3998 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
3999 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4000 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4002 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4004 TRACE("Source or destination is in system memory.\n");
4005 return FALSE;
4008 switch (blit_op)
4010 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
4011 if (d3d_info->shader_color_key)
4013 TRACE("Color keying requires converted textures.\n");
4014 return FALSE;
4016 case WINED3D_BLIT_OP_COLOR_BLIT:
4017 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
4018 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4020 TRACE("Checking support for fixup:\n");
4021 dump_color_fixup_desc(src_format->color_fixup);
4024 /* We only support identity conversions. */
4025 if (!is_identity_fixup(src_format->color_fixup)
4026 || !is_identity_fixup(dst_format->color_fixup))
4028 TRACE("Fixups are not supported.\n");
4029 return FALSE;
4032 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4034 TRACE("Can only blit to render targets.\n");
4035 return FALSE;
4037 return TRUE;
4039 case WINED3D_BLIT_OP_COLOR_FILL:
4040 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4042 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
4043 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4044 return FALSE;
4046 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4048 TRACE("Color fill not supported\n");
4049 return FALSE;
4052 /* FIXME: We should reject color fills on formats with fixups,
4053 * but this would break P8 color fills for example. */
4055 return TRUE;
4057 case WINED3D_BLIT_OP_DEPTH_FILL:
4058 return TRUE;
4060 default:
4061 TRACE("Unsupported blit_op=%d\n", blit_op);
4062 return FALSE;
4066 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
4067 const RECT *rect, const struct wined3d_color *color)
4069 const RECT draw_rect = {0, 0, view->width, view->height};
4070 struct wined3d_fb_state fb = {&view, NULL};
4072 device_clear_render_targets(device, 1, &fb, 1, rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4074 return WINED3D_OK;
4077 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
4078 struct wined3d_rendertarget_view *view, const RECT *rect, float depth)
4080 const RECT draw_rect = {0, 0, view->width, view->height};
4081 struct wined3d_fb_state fb = {NULL, view};
4083 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4085 return WINED3D_OK;
4088 static void ffp_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
4089 struct wined3d_surface *src_surface, const RECT *src_rect,
4090 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4091 const struct wined3d_color_key *color_key)
4093 struct wined3d_context *context;
4095 /* Blit from offscreen surface to render target */
4096 struct wined3d_color_key old_blt_key = src_surface->container->async.src_blt_color_key;
4097 DWORD old_color_key_flags = src_surface->container->async.color_key_flags;
4099 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4101 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT, color_key);
4103 context = context_acquire(device, dst_surface);
4105 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
4106 glEnable(GL_ALPHA_TEST);
4108 surface_blt_to_drawable(device, context, filter,
4109 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
4111 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
4112 glDisable(GL_ALPHA_TEST);
4114 context_release(context);
4116 /* Restore the color key parameters */
4117 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT,
4118 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
4120 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
4121 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
4124 const struct blit_shader ffp_blit = {
4125 ffp_blit_alloc,
4126 ffp_blit_free,
4127 ffp_blit_set,
4128 ffp_blit_unset,
4129 ffp_blit_supported,
4130 ffp_blit_color_fill,
4131 ffp_blit_depth_fill,
4132 ffp_blit_blit_surface,
4135 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4137 return WINED3D_OK;
4140 /* Context activation is done by the caller. */
4141 static void cpu_blit_free(struct wined3d_device *device)
4145 /* Context activation is done by the caller. */
4146 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4147 const struct wined3d_color_key *color_key)
4149 return WINED3D_OK;
4152 /* Context activation is done by the caller. */
4153 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4157 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
4158 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4159 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4160 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4162 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4164 return TRUE;
4167 return FALSE;
4170 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4171 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4172 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4174 UINT row_block_count;
4175 const BYTE *src_row;
4176 BYTE *dst_row;
4177 UINT x, y;
4179 src_row = src_data;
4180 dst_row = dst_data;
4182 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4184 if (!flags)
4186 for (y = 0; y < update_h; y += format->block_height)
4188 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4189 src_row += src_pitch;
4190 dst_row += dst_pitch;
4193 return WINED3D_OK;
4196 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4198 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4200 switch (format->id)
4202 case WINED3DFMT_DXT1:
4203 for (y = 0; y < update_h; y += format->block_height)
4205 struct block
4207 WORD color[2];
4208 BYTE control_row[4];
4211 const struct block *s = (const struct block *)src_row;
4212 struct block *d = (struct block *)dst_row;
4214 for (x = 0; x < row_block_count; ++x)
4216 d[x].color[0] = s[x].color[0];
4217 d[x].color[1] = s[x].color[1];
4218 d[x].control_row[0] = s[x].control_row[3];
4219 d[x].control_row[1] = s[x].control_row[2];
4220 d[x].control_row[2] = s[x].control_row[1];
4221 d[x].control_row[3] = s[x].control_row[0];
4223 src_row -= src_pitch;
4224 dst_row += dst_pitch;
4226 return WINED3D_OK;
4228 case WINED3DFMT_DXT2:
4229 case WINED3DFMT_DXT3:
4230 for (y = 0; y < update_h; y += format->block_height)
4232 struct block
4234 WORD alpha_row[4];
4235 WORD color[2];
4236 BYTE control_row[4];
4239 const struct block *s = (const struct block *)src_row;
4240 struct block *d = (struct block *)dst_row;
4242 for (x = 0; x < row_block_count; ++x)
4244 d[x].alpha_row[0] = s[x].alpha_row[3];
4245 d[x].alpha_row[1] = s[x].alpha_row[2];
4246 d[x].alpha_row[2] = s[x].alpha_row[1];
4247 d[x].alpha_row[3] = s[x].alpha_row[0];
4248 d[x].color[0] = s[x].color[0];
4249 d[x].color[1] = s[x].color[1];
4250 d[x].control_row[0] = s[x].control_row[3];
4251 d[x].control_row[1] = s[x].control_row[2];
4252 d[x].control_row[2] = s[x].control_row[1];
4253 d[x].control_row[3] = s[x].control_row[0];
4255 src_row -= src_pitch;
4256 dst_row += dst_pitch;
4258 return WINED3D_OK;
4260 default:
4261 FIXME("Compressed flip not implemented for format %s.\n",
4262 debug_d3dformat(format->id));
4263 return E_NOTIMPL;
4267 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
4268 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
4270 return E_NOTIMPL;
4273 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4274 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4275 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4277 const struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
4278 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
4279 const struct wined3d_format *src_format, *dst_format;
4280 unsigned int src_fmt_flags, dst_fmt_flags;
4281 struct wined3d_texture *src_texture = NULL;
4282 struct wined3d_map_desc dst_map, src_map;
4283 const BYTE *sbase = NULL;
4284 HRESULT hr = WINED3D_OK;
4285 const BYTE *sbuf;
4286 BYTE *dbuf;
4287 int x, y;
4289 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4290 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4291 flags, fx, debug_d3dtexturefiltertype(filter));
4293 if (src_surface == dst_surface)
4295 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
4296 src_map = dst_map;
4297 src_format = dst_surface->resource.format;
4298 dst_format = src_format;
4299 dst_fmt_flags = dst_surface->container->resource.format_flags;
4300 src_fmt_flags = dst_fmt_flags;
4302 else
4304 dst_format = dst_surface->resource.format;
4305 dst_fmt_flags = dst_surface->container->resource.format_flags;
4306 if (src_surface)
4308 if (dst_surface->resource.format->id != src_surface->resource.format->id)
4310 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
4312 /* The conv function writes a FIXME */
4313 WARN("Cannot convert source surface format to dest format.\n");
4314 goto release;
4316 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
4318 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
4319 src_format = src_surface->resource.format;
4320 src_fmt_flags = src_surface->container->resource.format_flags;
4322 else
4324 src_format = dst_format;
4325 src_fmt_flags = dst_fmt_flags;
4328 wined3d_surface_map(dst_surface, &dst_map, &dst_box, 0);
4331 bpp = dst_surface->resource.format->byte_count;
4332 srcheight = src_rect->bottom - src_rect->top;
4333 srcwidth = src_rect->right - src_rect->left;
4334 dstheight = dst_rect->bottom - dst_rect->top;
4335 dstwidth = dst_rect->right - dst_rect->left;
4336 width = (dst_rect->right - dst_rect->left) * bpp;
4338 if (src_surface)
4339 sbase = (BYTE *)src_map.data
4340 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
4341 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
4342 if (src_surface != dst_surface)
4343 dbuf = dst_map.data;
4344 else
4345 dbuf = (BYTE *)dst_map.data
4346 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
4347 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
4349 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
4351 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
4353 if (src_surface == dst_surface)
4355 FIXME("Only plain blits supported on compressed surfaces.\n");
4356 hr = E_NOTIMPL;
4357 goto release;
4360 if (srcheight != dstheight || srcwidth != dstwidth)
4362 WARN("Stretching not supported on compressed surfaces.\n");
4363 hr = WINED3DERR_INVALIDCALL;
4364 goto release;
4367 if (!surface_check_block_align_rect(src_surface, src_rect))
4369 WARN("Source rectangle not block-aligned.\n");
4370 hr = WINED3DERR_INVALIDCALL;
4371 goto release;
4374 if (!surface_check_block_align_rect(dst_surface, dst_rect))
4376 WARN("Destination rectangle not block-aligned.\n");
4377 hr = WINED3DERR_INVALIDCALL;
4378 goto release;
4381 hr = surface_cpu_blt_compressed(sbase, dbuf,
4382 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
4383 src_format, flags, fx);
4384 goto release;
4387 /* First, all the 'source-less' blits */
4388 if (flags & WINEDDBLT_COLORFILL)
4390 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
4391 flags &= ~WINEDDBLT_COLORFILL;
4394 if (flags & WINEDDBLT_DEPTHFILL)
4396 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
4398 if (flags & WINEDDBLT_DDROPS)
4400 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
4402 /* Now the 'with source' blits. */
4403 if (src_surface)
4405 int sx, xinc, sy, yinc;
4407 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
4408 goto release;
4410 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
4411 && (srcwidth != dstwidth || srcheight != dstheight))
4413 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
4414 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
4417 xinc = (srcwidth << 16) / dstwidth;
4418 yinc = (srcheight << 16) / dstheight;
4420 if (!flags)
4422 /* No effects, we can cheat here. */
4423 if (dstwidth == srcwidth)
4425 if (dstheight == srcheight)
4427 /* No stretching in either direction. This needs to be as
4428 * fast as possible. */
4429 sbuf = sbase;
4431 /* Check for overlapping surfaces. */
4432 if (src_surface != dst_surface || dst_rect->top < src_rect->top
4433 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
4435 /* No overlap, or dst above src, so copy from top downwards. */
4436 for (y = 0; y < dstheight; ++y)
4438 memcpy(dbuf, sbuf, width);
4439 sbuf += src_map.row_pitch;
4440 dbuf += dst_map.row_pitch;
4443 else if (dst_rect->top > src_rect->top)
4445 /* Copy from bottom upwards. */
4446 sbuf += src_map.row_pitch * dstheight;
4447 dbuf += dst_map.row_pitch * dstheight;
4448 for (y = 0; y < dstheight; ++y)
4450 sbuf -= src_map.row_pitch;
4451 dbuf -= dst_map.row_pitch;
4452 memcpy(dbuf, sbuf, width);
4455 else
4457 /* Src and dst overlapping on the same line, use memmove. */
4458 for (y = 0; y < dstheight; ++y)
4460 memmove(dbuf, sbuf, width);
4461 sbuf += src_map.row_pitch;
4462 dbuf += dst_map.row_pitch;
4466 else
4468 /* Stretching in y direction only. */
4469 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4471 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4472 memcpy(dbuf, sbuf, width);
4473 dbuf += dst_map.row_pitch;
4477 else
4479 /* Stretching in X direction. */
4480 int last_sy = -1;
4481 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4483 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4485 if ((sy >> 16) == (last_sy >> 16))
4487 /* This source row is the same as last source row -
4488 * Copy the already stretched row. */
4489 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
4491 else
4493 #define STRETCH_ROW(type) \
4494 do { \
4495 const type *s = (const type *)sbuf; \
4496 type *d = (type *)dbuf; \
4497 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4498 d[x] = s[sx >> 16]; \
4499 } while(0)
4501 switch(bpp)
4503 case 1:
4504 STRETCH_ROW(BYTE);
4505 break;
4506 case 2:
4507 STRETCH_ROW(WORD);
4508 break;
4509 case 4:
4510 STRETCH_ROW(DWORD);
4511 break;
4512 case 3:
4514 const BYTE *s;
4515 BYTE *d = dbuf;
4516 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
4518 DWORD pixel;
4520 s = sbuf + 3 * (sx >> 16);
4521 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4522 d[0] = (pixel ) & 0xff;
4523 d[1] = (pixel >> 8) & 0xff;
4524 d[2] = (pixel >> 16) & 0xff;
4525 d += 3;
4527 break;
4529 default:
4530 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
4531 hr = WINED3DERR_NOTAVAILABLE;
4532 goto error;
4534 #undef STRETCH_ROW
4536 dbuf += dst_map.row_pitch;
4537 last_sy = sy;
4541 else
4543 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
4544 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
4545 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
4546 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
4548 /* The color keying flags are checked for correctness in ddraw */
4549 if (flags & WINEDDBLT_KEYSRC)
4551 keylow = src_surface->container->async.src_blt_color_key.color_space_low_value;
4552 keyhigh = src_surface->container->async.src_blt_color_key.color_space_high_value;
4554 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4556 keylow = fx->ddckSrcColorkey.color_space_low_value;
4557 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
4560 if (flags & WINEDDBLT_KEYDEST)
4562 /* Destination color keys are taken from the source surface! */
4563 destkeylow = src_surface->container->async.dst_blt_color_key.color_space_low_value;
4564 destkeyhigh = src_surface->container->async.dst_blt_color_key.color_space_high_value;
4566 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
4568 destkeylow = fx->ddckDestColorkey.color_space_low_value;
4569 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
4572 if (bpp == 1)
4574 keymask = 0xff;
4576 else
4578 DWORD masks[3];
4579 get_color_masks(src_format, masks);
4580 keymask = masks[0]
4581 | masks[1]
4582 | masks[2];
4584 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
4587 if (flags & WINEDDBLT_DDFX)
4589 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
4590 LONG tmpxy;
4591 dTopLeft = dbuf;
4592 dTopRight = dbuf + ((dstwidth - 1) * bpp);
4593 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
4594 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
4596 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
4598 /* I don't think we need to do anything about this flag */
4599 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
4601 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
4603 tmp = dTopRight;
4604 dTopRight = dTopLeft;
4605 dTopLeft = tmp;
4606 tmp = dBottomRight;
4607 dBottomRight = dBottomLeft;
4608 dBottomLeft = tmp;
4609 dstxinc = dstxinc * -1;
4611 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
4613 tmp = dTopLeft;
4614 dTopLeft = dBottomLeft;
4615 dBottomLeft = tmp;
4616 tmp = dTopRight;
4617 dTopRight = dBottomRight;
4618 dBottomRight = tmp;
4619 dstyinc = dstyinc * -1;
4621 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
4623 /* I don't think we need to do anything about this flag */
4624 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
4626 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
4628 tmp = dBottomRight;
4629 dBottomRight = dTopLeft;
4630 dTopLeft = tmp;
4631 tmp = dBottomLeft;
4632 dBottomLeft = dTopRight;
4633 dTopRight = tmp;
4634 dstxinc = dstxinc * -1;
4635 dstyinc = dstyinc * -1;
4637 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
4639 tmp = dTopLeft;
4640 dTopLeft = dBottomLeft;
4641 dBottomLeft = dBottomRight;
4642 dBottomRight = dTopRight;
4643 dTopRight = tmp;
4644 tmpxy = dstxinc;
4645 dstxinc = dstyinc;
4646 dstyinc = tmpxy;
4647 dstxinc = dstxinc * -1;
4649 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
4651 tmp = dTopLeft;
4652 dTopLeft = dTopRight;
4653 dTopRight = dBottomRight;
4654 dBottomRight = dBottomLeft;
4655 dBottomLeft = tmp;
4656 tmpxy = dstxinc;
4657 dstxinc = dstyinc;
4658 dstyinc = tmpxy;
4659 dstyinc = dstyinc * -1;
4661 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
4663 /* I don't think we need to do anything about this flag */
4664 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
4666 dbuf = dTopLeft;
4667 flags &= ~(WINEDDBLT_DDFX);
4670 #define COPY_COLORKEY_FX(type) \
4671 do { \
4672 const type *s; \
4673 type *d = (type *)dbuf, *dx, tmp; \
4674 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
4676 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
4677 dx = d; \
4678 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4680 tmp = s[sx >> 16]; \
4681 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
4682 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
4684 dx[0] = tmp; \
4686 dx = (type *)(((BYTE *)dx) + dstxinc); \
4688 d = (type *)(((BYTE *)d) + dstyinc); \
4690 } while(0)
4692 switch (bpp)
4694 case 1:
4695 COPY_COLORKEY_FX(BYTE);
4696 break;
4697 case 2:
4698 COPY_COLORKEY_FX(WORD);
4699 break;
4700 case 4:
4701 COPY_COLORKEY_FX(DWORD);
4702 break;
4703 case 3:
4705 const BYTE *s;
4706 BYTE *d = dbuf, *dx;
4707 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4709 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4710 dx = d;
4711 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
4713 DWORD pixel, dpixel = 0;
4714 s = sbuf + 3 * (sx>>16);
4715 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4716 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
4717 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
4718 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
4720 dx[0] = (pixel ) & 0xff;
4721 dx[1] = (pixel >> 8) & 0xff;
4722 dx[2] = (pixel >> 16) & 0xff;
4724 dx += dstxinc;
4726 d += dstyinc;
4728 break;
4730 default:
4731 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
4732 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
4733 hr = WINED3DERR_NOTAVAILABLE;
4734 goto error;
4735 #undef COPY_COLORKEY_FX
4740 error:
4741 if (flags && FIXME_ON(d3d_surface))
4743 FIXME("\tUnsupported flags: %#x.\n", flags);
4746 release:
4747 wined3d_surface_unmap(dst_surface);
4748 if (src_surface && src_surface != dst_surface)
4749 wined3d_surface_unmap(src_surface);
4750 /* Release the converted surface, if any. */
4751 if (src_texture)
4752 wined3d_texture_decref(src_texture);
4754 return hr;
4757 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
4758 const RECT *rect, const struct wined3d_color *color)
4760 struct wined3d_surface *surface = wined3d_rendertarget_view_get_surface(view);
4761 static const RECT src_rect;
4762 WINEDDBLTFX BltFx;
4764 memset(&BltFx, 0, sizeof(BltFx));
4765 BltFx.dwSize = sizeof(BltFx);
4766 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(surface, color);
4767 return surface_cpu_blt(surface, rect, NULL, &src_rect,
4768 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
4771 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
4772 struct wined3d_rendertarget_view *view, const RECT *rect, float depth)
4774 FIXME("Depth filling not implemented by cpu_blit.\n");
4775 return WINED3DERR_INVALIDCALL;
4778 static void cpu_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
4779 struct wined3d_surface *src_surface, const RECT *src_rect,
4780 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4781 const struct wined3d_color_key *color_key)
4783 /* FIXME: Remove error returns from surface_blt_cpu. */
4784 ERR("Blit method not implemented by cpu_blit.\n");
4787 const struct blit_shader cpu_blit = {
4788 cpu_blit_alloc,
4789 cpu_blit_free,
4790 cpu_blit_set,
4791 cpu_blit_unset,
4792 cpu_blit_supported,
4793 cpu_blit_color_fill,
4794 cpu_blit_depth_fill,
4795 cpu_blit_blit_surface,
4798 HRESULT wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4799 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4800 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4802 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
4803 struct wined3d_device *device = dst_surface->resource.device;
4804 DWORD src_ds_flags, dst_ds_flags;
4805 BOOL scale, convert;
4807 static const DWORD simple_blit = WINEDDBLT_ASYNC
4808 | WINEDDBLT_COLORFILL
4809 | WINEDDBLT_KEYSRC
4810 | WINEDDBLT_KEYSRCOVERRIDE
4811 | WINEDDBLT_WAIT
4812 | WINEDDBLT_DEPTHFILL
4813 | WINEDDBLT_DONOTWAIT
4814 | WINEDDBLT_ALPHATEST;
4816 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4817 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4818 flags, fx, debug_d3dtexturefiltertype(filter));
4819 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
4821 if (fx)
4823 TRACE("dwSize %#x.\n", fx->dwSize);
4824 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
4825 TRACE("dwROP %#x.\n", fx->dwROP);
4826 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
4827 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
4828 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
4829 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
4830 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
4831 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
4832 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
4833 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
4834 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
4835 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
4836 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
4837 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
4838 TRACE("dwReserved %#x.\n", fx->dwReserved);
4839 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
4840 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
4841 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
4842 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
4843 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
4844 TRACE("ddckDestColorkey {%#x, %#x}.\n",
4845 fx->ddckDestColorkey.color_space_low_value,
4846 fx->ddckDestColorkey.color_space_high_value);
4847 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
4848 fx->ddckSrcColorkey.color_space_low_value,
4849 fx->ddckSrcColorkey.color_space_high_value);
4852 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
4854 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
4855 return WINEDDERR_SURFACEBUSY;
4858 if (dst_rect->left >= dst_rect->right || dst_rect->top >= dst_rect->bottom
4859 || dst_rect->left > dst_surface->resource.width || dst_rect->left < 0
4860 || dst_rect->top > dst_surface->resource.height || dst_rect->top < 0
4861 || dst_rect->right > dst_surface->resource.width || dst_rect->right < 0
4862 || dst_rect->bottom > dst_surface->resource.height || dst_rect->bottom < 0)
4864 WARN("The application gave us a bad destination rectangle.\n");
4865 return WINEDDERR_INVALIDRECT;
4868 if (src_surface)
4870 if (src_rect->left >= src_rect->right || src_rect->top >= src_rect->bottom
4871 || src_rect->left > src_surface->resource.width || src_rect->left < 0
4872 || src_rect->top > src_surface->resource.height || src_rect->top < 0
4873 || src_rect->right > src_surface->resource.width || src_rect->right < 0
4874 || src_rect->bottom > src_surface->resource.height || src_rect->bottom < 0)
4876 WARN("The application gave us a bad source rectangle.\n");
4877 return WINEDDERR_INVALIDRECT;
4881 if (!fx || !(fx->dwDDFX))
4882 flags &= ~WINEDDBLT_DDFX;
4884 if (flags & WINEDDBLT_WAIT)
4885 flags &= ~WINEDDBLT_WAIT;
4887 if (flags & WINEDDBLT_ASYNC)
4889 static unsigned int once;
4891 if (!once++)
4892 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
4893 flags &= ~WINEDDBLT_ASYNC;
4896 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
4897 if (flags & WINEDDBLT_DONOTWAIT)
4899 static unsigned int once;
4901 if (!once++)
4902 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
4903 flags &= ~WINEDDBLT_DONOTWAIT;
4906 if (!device->d3d_initialized)
4908 WARN("D3D not initialized, using fallback.\n");
4909 goto cpu;
4912 /* We want to avoid invalidating the sysmem location for converted
4913 * surfaces, since otherwise we'd have to convert the data back when
4914 * locking them. */
4915 if (dst_surface->container->flags & WINED3D_TEXTURE_CONVERTED
4916 || dst_surface->container->resource.format->convert
4917 || wined3d_format_get_color_key_conversion(dst_surface->container, TRUE))
4919 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
4920 goto cpu;
4923 if (flags & ~simple_blit)
4925 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
4926 goto fallback;
4929 if (src_surface)
4930 src_swapchain = src_surface->container->swapchain;
4931 else
4932 src_swapchain = NULL;
4934 dst_swapchain = dst_surface->container->swapchain;
4936 /* This isn't strictly needed. FBO blits for example could deal with
4937 * cross-swapchain blits by first downloading the source to a texture
4938 * before switching to the destination context. We just have this here to
4939 * not have to deal with the issue, since cross-swapchain blits should be
4940 * rare. */
4941 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
4943 FIXME("Using fallback for cross-swapchain blit.\n");
4944 goto fallback;
4947 scale = src_surface
4948 && (src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
4949 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top);
4950 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
4952 dst_ds_flags = dst_surface->container->resource.format_flags
4953 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4954 if (src_surface)
4955 src_ds_flags = src_surface->container->resource.format_flags
4956 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4957 else
4958 src_ds_flags = 0;
4960 if (src_ds_flags || dst_ds_flags)
4962 if (flags & WINEDDBLT_DEPTHFILL)
4964 float depth;
4966 TRACE("Depth fill.\n");
4968 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
4969 return WINED3DERR_INVALIDCALL;
4971 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, dst_rect, depth)))
4972 return WINED3D_OK;
4974 else
4976 if (src_ds_flags != dst_ds_flags)
4978 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
4979 return WINED3DERR_INVALIDCALL;
4982 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
4983 src_rect, dst_surface, dst_surface->container->resource.draw_binding, dst_rect)))
4984 return WINED3D_OK;
4987 else
4989 const struct blit_shader *blitter;
4991 /* In principle this would apply to depth blits as well, but we don't
4992 * implement those in the CPU blitter at the moment. */
4993 if ((dst_surface->locations & dst_surface->resource.map_binding)
4994 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
4996 if (scale)
4997 TRACE("Not doing sysmem blit because of scaling.\n");
4998 else if (convert)
4999 TRACE("Not doing sysmem blit because of format conversion.\n");
5000 else
5001 goto cpu;
5004 if (flags & WINEDDBLT_COLORFILL)
5006 struct wined3d_color color;
5007 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
5009 TRACE("Color fill.\n");
5011 if (!wined3d_format_convert_color_to_float(dst_surface->resource.format,
5012 palette, fx->u5.dwFillColor, &color))
5013 goto fallback;
5015 if (SUCCEEDED(surface_color_fill(dst_surface, dst_rect, &color)))
5016 return WINED3D_OK;
5018 else
5020 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
5021 const struct wined3d_color_key *color_key = NULL;
5023 TRACE("Color blit.\n");
5024 if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5026 color_key = &fx->ddckSrcColorkey;
5027 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5029 else if (flags & WINEDDBLT_KEYSRC)
5031 color_key = &src_surface->container->async.src_blt_color_key;
5032 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5034 else if (flags & WINEDDBLT_ALPHATEST)
5036 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
5038 else if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5039 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5041 /* Upload */
5042 if (scale)
5043 TRACE("Not doing upload because of scaling.\n");
5044 else if (convert)
5045 TRACE("Not doing upload because of format conversion.\n");
5046 else
5048 POINT dst_point = {dst_rect->left, dst_rect->top};
5050 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
5052 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
5054 struct wined3d_context *context = context_acquire(device, dst_surface);
5055 surface_load_location(dst_surface, context, dst_surface->container->resource.draw_binding);
5056 context_release(context);
5058 return WINED3D_OK;
5062 else if (dst_swapchain && dst_swapchain->back_buffers
5063 && dst_surface->container == dst_swapchain->front_buffer
5064 && src_surface->container == dst_swapchain->back_buffers[0])
5066 /* Use present for back -> front blits. The idea behind this is
5067 * that present is potentially faster than a blit, in particular
5068 * when FBO blits aren't available. Some ddraw applications like
5069 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5070 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5071 * applications can't blit directly to the frontbuffer. */
5072 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5074 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5076 /* Set the swap effect to COPY, we don't want the backbuffer
5077 * to become undefined. */
5078 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5079 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5080 dst_swapchain->desc.swap_effect = swap_effect;
5082 return WINED3D_OK;
5085 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
5086 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5087 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5089 struct wined3d_context *context;
5090 TRACE("Using FBO blit.\n");
5092 context = context_acquire(device, NULL);
5093 surface_blt_fbo(device, context, filter,
5094 src_surface, src_surface->container->resource.draw_binding, src_rect,
5095 dst_surface, dst_surface->container->resource.draw_binding, dst_rect);
5096 context_release(context);
5098 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5099 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5101 return WINED3D_OK;
5104 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
5105 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5106 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format);
5107 if (blitter)
5109 blitter->blit_surface(device, blit_op, filter, src_surface,
5110 src_rect, dst_surface, dst_rect, color_key);
5111 return WINED3D_OK;
5116 fallback:
5117 /* Special cases for render targets. */
5118 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
5119 return WINED3D_OK;
5121 cpu:
5122 return surface_cpu_blt(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter);
5125 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5126 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5128 struct wined3d_device *device = container->resource.device;
5129 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5130 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5131 BOOL lockable = flags & WINED3D_TEXTURE_CREATE_MAPPABLE;
5132 UINT multisample_quality = desc->multisample_quality;
5133 unsigned int resource_size;
5134 HRESULT hr;
5136 /* Quick lockable sanity check.
5137 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5138 * this function is too deep to need to care about things like this.
5139 * Levels need to be checked too, since they all affect what can be done. */
5140 switch (desc->pool)
5142 case WINED3D_POOL_MANAGED:
5143 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5144 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5145 break;
5147 case WINED3D_POOL_DEFAULT:
5148 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5149 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5150 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5151 break;
5153 case WINED3D_POOL_SCRATCH:
5154 case WINED3D_POOL_SYSTEM_MEM:
5155 break;
5157 default:
5158 FIXME("Unknown pool %#x.\n", desc->pool);
5159 break;
5162 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5163 FIXME("Trying to create a render target that isn't in the default pool.\n");
5165 /* FIXME: Check that the format is supported by the device. */
5167 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5168 if (!resource_size)
5169 return WINED3DERR_INVALIDCALL;
5171 if (device->wined3d->flags & WINED3D_NO3D)
5172 surface->surface_ops = &gdi_surface_ops;
5173 else
5174 surface->surface_ops = &surface_ops;
5176 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE,
5177 format, desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height,
5178 1, resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5180 WARN("Failed to initialize resource, returning %#x.\n", hr);
5181 return hr;
5184 surface->container = container;
5185 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5186 list_init(&surface->renderbuffers);
5187 list_init(&surface->overlays);
5189 /* Flags */
5190 if (flags & WINED3D_TEXTURE_CREATE_DISCARD)
5191 surface->flags |= SFLAG_DISCARD;
5192 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5193 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5195 surface->texture_target = target;
5196 surface->texture_level = level;
5197 surface->texture_layer = layer;
5199 /* Call the private setup routine */
5200 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5202 ERR("Private setup failed, hr %#x.\n", hr);
5203 surface_cleanup(surface);
5204 return hr;
5207 /* Similar to lockable rendertargets above, creating the DIB section
5208 * during surface initialization prevents the sysmem pointer from changing
5209 * after a wined3d_texture_get_dc() call. */
5210 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5211 && SUCCEEDED(surface_create_dib_section(surface)))
5212 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5214 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5216 wined3d_resource_free_sysmem(&surface->resource);
5217 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5218 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5221 return hr;
5224 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5225 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
5227 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
5228 const struct wined3d_parent_ops *parent_ops;
5229 struct wined3d_surface *object;
5230 void *parent;
5231 HRESULT hr;
5233 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
5234 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
5235 container, desc->width, desc->height, debug_d3dformat(desc->format),
5236 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
5237 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
5239 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
5240 return E_OUTOFMEMORY;
5242 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
5244 WARN("Failed to initialize surface, returning %#x.\n", hr);
5245 HeapFree(GetProcessHeap(), 0, object);
5246 return hr;
5249 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
5250 container, layer * container->level_count + level, object, &parent, &parent_ops)))
5252 WARN("Failed to create surface parent, hr %#x.\n", hr);
5253 wined3d_surface_destroy(object);
5254 return hr;
5257 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
5259 object->resource.parent = parent;
5260 object->resource.parent_ops = parent_ops;
5261 *surface = object;
5263 return hr;
5266 /* Context activation is done by the caller. */
5267 void wined3d_surface_prepare(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5269 switch (location)
5271 case WINED3D_LOCATION_TEXTURE_RGB:
5272 wined3d_texture_prepare_texture(surface->container, context, FALSE);
5273 break;
5275 case WINED3D_LOCATION_TEXTURE_SRGB:
5276 wined3d_texture_prepare_texture(surface->container, context, TRUE);
5277 break;
5279 case WINED3D_LOCATION_RB_MULTISAMPLE:
5280 surface_prepare_rb(surface, context->gl_info, TRUE);
5281 break;
5283 case WINED3D_LOCATION_RB_RESOLVED:
5284 surface_prepare_rb(surface, context->gl_info, FALSE);
5285 break;