wined3d: Use wined3d_texture_get_pitch() in read_from_framebuffer().
[wine.git] / dlls / wined3d / surface.c
blobc63b6802731a5ac00eb37935d5daaf842fda6474
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 unsigned int row_pitch, slice_pitch;
364 SYSTEM_INFO sysInfo;
365 BITMAPINFO *b_info;
366 int extraline = 0;
367 DWORD *masks;
369 TRACE("surface %p.\n", surface);
371 if (!(format_flags & WINED3DFMT_FLAG_GETDC))
373 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
374 return WINED3DERR_INVALIDCALL;
377 switch (format->byte_count)
379 case 2:
380 case 4:
381 /* Allocate extra space to store the RGB bit masks. */
382 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
383 break;
385 case 3:
386 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
387 break;
389 default:
390 /* Allocate extra space for a palette. */
391 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
392 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1u << (format->byte_count * 8)));
393 break;
396 if (!b_info)
397 return E_OUTOFMEMORY;
399 /* Some applications access the surface in via DWORDs, and do not take
400 * the necessary care at the end of the surface. So we need at least
401 * 4 extra bytes at the end of the surface. Check against the page size,
402 * if the last page used for the surface has at least 4 spare bytes we're
403 * safe, otherwise add an extra line to the DIB section. */
404 GetSystemInfo(&sysInfo);
405 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
407 extraline = 1;
408 TRACE("Adding an extra line to the DIB section.\n");
411 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
412 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
413 wined3d_texture_get_pitch(surface->container, surface->texture_level, &row_pitch, &slice_pitch);
414 b_info->bmiHeader.biWidth = row_pitch / format->byte_count;
415 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
416 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline) * row_pitch;
417 b_info->bmiHeader.biPlanes = 1;
418 b_info->bmiHeader.biBitCount = format->byte_count * 8;
420 b_info->bmiHeader.biXPelsPerMeter = 0;
421 b_info->bmiHeader.biYPelsPerMeter = 0;
422 b_info->bmiHeader.biClrUsed = 0;
423 b_info->bmiHeader.biClrImportant = 0;
425 /* Get the bit masks */
426 masks = (DWORD *)b_info->bmiColors;
427 switch (surface->resource.format->id)
429 case WINED3DFMT_B8G8R8_UNORM:
430 b_info->bmiHeader.biCompression = BI_RGB;
431 break;
433 case WINED3DFMT_B5G5R5X1_UNORM:
434 case WINED3DFMT_B5G5R5A1_UNORM:
435 case WINED3DFMT_B4G4R4A4_UNORM:
436 case WINED3DFMT_B4G4R4X4_UNORM:
437 case WINED3DFMT_B2G3R3_UNORM:
438 case WINED3DFMT_B2G3R3A8_UNORM:
439 case WINED3DFMT_R10G10B10A2_UNORM:
440 case WINED3DFMT_R8G8B8A8_UNORM:
441 case WINED3DFMT_R8G8B8X8_UNORM:
442 case WINED3DFMT_B10G10R10A2_UNORM:
443 case WINED3DFMT_B5G6R5_UNORM:
444 case WINED3DFMT_R16G16B16A16_UNORM:
445 b_info->bmiHeader.biCompression = BI_BITFIELDS;
446 get_color_masks(format, masks);
447 break;
449 default:
450 /* Don't know palette */
451 b_info->bmiHeader.biCompression = BI_RGB;
452 break;
455 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
456 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
457 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
458 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
460 if (!surface->dib.DIBsection)
462 ERR("Failed to create DIB section.\n");
463 HeapFree(GetProcessHeap(), 0, b_info);
464 return HRESULT_FROM_WIN32(GetLastError());
467 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
468 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
470 HeapFree(GetProcessHeap(), 0, b_info);
472 /* Now allocate a DC. */
473 surface->hDC = CreateCompatibleDC(0);
474 SelectObject(surface->hDC, surface->dib.DIBsection);
476 surface->flags |= SFLAG_DIBSECTION;
478 return WINED3D_OK;
481 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
482 DWORD location)
484 if (location & WINED3D_LOCATION_BUFFER)
486 data->addr = NULL;
487 data->buffer_object = surface->pbo;
488 return;
490 if (location & WINED3D_LOCATION_USER_MEMORY)
492 data->addr = surface->container->user_memory;
493 data->buffer_object = 0;
494 return;
496 if (location & WINED3D_LOCATION_DIB)
498 data->addr = surface->dib.bitmap_data;
499 data->buffer_object = 0;
500 return;
502 if (location & WINED3D_LOCATION_SYSMEM)
504 data->addr = surface->resource.heap_memory;
505 data->buffer_object = 0;
506 return;
509 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
510 data->addr = NULL;
511 data->buffer_object = 0;
514 static void surface_prepare_buffer(struct wined3d_surface *surface)
516 struct wined3d_context *context;
517 GLenum error;
518 const struct wined3d_gl_info *gl_info;
520 if (surface->pbo)
521 return;
523 context = context_acquire(surface->resource.device, NULL);
524 gl_info = context->gl_info;
526 GL_EXTCALL(glGenBuffers(1, &surface->pbo));
527 error = gl_info->gl_ops.gl.p_glGetError();
528 if (!surface->pbo || error != GL_NO_ERROR)
529 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
531 TRACE("Binding PBO %u.\n", surface->pbo);
533 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
534 checkGLcall("glBindBuffer");
536 GL_EXTCALL(glBufferData(GL_PIXEL_UNPACK_BUFFER, surface->resource.size + 4,
537 NULL, GL_STREAM_DRAW));
538 checkGLcall("glBufferData");
540 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
541 checkGLcall("glBindBuffer");
543 context_release(context);
546 static void surface_prepare_system_memory(struct wined3d_surface *surface)
548 TRACE("surface %p.\n", surface);
550 if (surface->resource.heap_memory)
551 return;
553 /* Whatever surface we have, make sure that there is memory allocated
554 * for the downloaded copy, or a PBO to map. */
555 if (!wined3d_resource_allocate_sysmem(&surface->resource))
556 ERR("Failed to allocate system memory.\n");
558 if (surface->locations & WINED3D_LOCATION_SYSMEM)
559 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
562 void surface_prepare_map_memory(struct wined3d_surface *surface)
564 switch (surface->resource.map_binding)
566 case WINED3D_LOCATION_SYSMEM:
567 surface_prepare_system_memory(surface);
568 break;
570 case WINED3D_LOCATION_USER_MEMORY:
571 if (!surface->container->user_memory)
572 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
573 break;
575 case WINED3D_LOCATION_DIB:
576 if (!surface->dib.bitmap_data)
577 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
578 break;
580 case WINED3D_LOCATION_BUFFER:
581 surface_prepare_buffer(surface);
582 break;
584 default:
585 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
589 static void surface_evict_sysmem(struct wined3d_surface *surface)
591 /* In some conditions the surface memory must not be freed:
592 * WINED3D_TEXTURE_CONVERTED: Converting the data back would take too long
593 * WINED3D_TEXTURE_DYNAMIC_MAP: Avoid freeing the data for performance */
594 if (surface->resource.map_count || surface->container->flags & (WINED3D_TEXTURE_CONVERTED
595 | WINED3D_TEXTURE_PIN_SYSMEM | WINED3D_TEXTURE_DYNAMIC_MAP))
596 return;
598 wined3d_resource_free_sysmem(&surface->resource);
599 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
602 static BOOL surface_use_pbo(const struct wined3d_surface *surface)
604 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
605 struct wined3d_texture *texture = surface->container;
607 return texture->resource.pool == WINED3D_POOL_DEFAULT
608 && surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU
609 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
610 && !texture->resource.format->convert
611 && !(texture->flags & WINED3D_TEXTURE_PIN_SYSMEM)
612 && !(surface->flags & SFLAG_NONPOW2);
615 static HRESULT surface_private_setup(struct wined3d_surface *surface)
617 /* TODO: Check against the maximum texture sizes supported by the video card. */
618 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
619 unsigned int pow2Width, pow2Height;
621 TRACE("surface %p.\n", surface);
623 /* Non-power2 support */
624 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
625 || gl_info->supported[ARB_TEXTURE_RECTANGLE])
627 pow2Width = surface->resource.width;
628 pow2Height = surface->resource.height;
630 else
632 /* Find the nearest pow2 match */
633 pow2Width = pow2Height = 1;
634 while (pow2Width < surface->resource.width)
635 pow2Width <<= 1;
636 while (pow2Height < surface->resource.height)
637 pow2Height <<= 1;
639 surface->pow2Width = pow2Width;
640 surface->pow2Height = pow2Height;
642 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
644 /* TODO: Add support for non power two compressed textures. */
645 if (surface->container->resource.format_flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE))
647 FIXME("(%p) Compressed or height scaled non-power-two textures are not supported w(%d) h(%d)\n",
648 surface, surface->resource.width, surface->resource.height);
649 return WINED3DERR_NOTAVAILABLE;
653 if (pow2Width != surface->resource.width
654 || pow2Height != surface->resource.height)
656 surface->flags |= SFLAG_NONPOW2;
659 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
660 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
662 /* One of three options:
663 * 1: Do the same as we do with NPOT and scale the texture, (any
664 * texture ops would require the texture to be scaled which is
665 * potentially slow)
666 * 2: Set the texture to the maximum size (bad idea).
667 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
668 * 4: Create the surface, but allow it to be used only for DirectDraw
669 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
670 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
671 * the render target. */
672 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
674 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
675 return WINED3DERR_NOTAVAILABLE;
678 /* We should never use this surface in combination with OpenGL! */
679 TRACE("Creating an oversized surface: %ux%u.\n",
680 surface->pow2Width, surface->pow2Height);
683 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
684 surface->locations = WINED3D_LOCATION_DISCARDED;
686 if (surface_use_pbo(surface))
687 surface->resource.map_binding = WINED3D_LOCATION_BUFFER;
689 return WINED3D_OK;
692 static void surface_unmap(struct wined3d_surface *surface)
694 struct wined3d_device *device = surface->resource.device;
695 const struct wined3d_gl_info *gl_info;
696 struct wined3d_context *context;
698 TRACE("surface %p.\n", surface);
700 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
702 switch (surface->resource.map_binding)
704 case WINED3D_LOCATION_SYSMEM:
705 case WINED3D_LOCATION_USER_MEMORY:
706 case WINED3D_LOCATION_DIB:
707 break;
709 case WINED3D_LOCATION_BUFFER:
710 context = context_acquire(device, NULL);
711 gl_info = context->gl_info;
713 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
714 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
715 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
716 checkGLcall("glUnmapBuffer");
717 context_release(context);
718 break;
720 default:
721 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
724 if (surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_TEXTURE_RGB))
726 TRACE("Not dirtified, nothing to do.\n");
727 return;
730 if (surface->container->swapchain && surface->container->swapchain->front_buffer == surface->container)
732 context = context_acquire(device, surface);
733 surface_load_location(surface, context, surface->container->resource.draw_binding);
734 context_release(context);
736 else if (surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
737 FIXME("Depth / stencil buffer locking is not implemented.\n");
740 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
742 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
743 return FALSE;
744 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
745 return FALSE;
746 return TRUE;
749 static void surface_depth_blt_fbo(const struct wined3d_device *device,
750 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
751 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
753 const struct wined3d_gl_info *gl_info;
754 struct wined3d_context *context;
755 DWORD src_mask, dst_mask;
756 GLbitfield gl_mask;
758 TRACE("device %p\n", device);
759 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
760 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
761 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
762 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
764 src_mask = src_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
765 dst_mask = dst_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
767 if (src_mask != dst_mask)
769 ERR("Incompatible formats %s and %s.\n",
770 debug_d3dformat(src_surface->resource.format->id),
771 debug_d3dformat(dst_surface->resource.format->id));
772 return;
775 if (!src_mask)
777 ERR("Not a depth / stencil format: %s.\n",
778 debug_d3dformat(src_surface->resource.format->id));
779 return;
782 gl_mask = 0;
783 if (src_mask & WINED3DFMT_FLAG_DEPTH)
784 gl_mask |= GL_DEPTH_BUFFER_BIT;
785 if (src_mask & WINED3DFMT_FLAG_STENCIL)
786 gl_mask |= GL_STENCIL_BUFFER_BIT;
788 context = context_acquire(device, NULL);
789 if (!context->valid)
791 context_release(context);
792 WARN("Invalid context, skipping blit.\n");
793 return;
796 /* Make sure the locations are up-to-date. Loading the destination
797 * surface isn't required if the entire surface is overwritten. */
798 surface_load_location(src_surface, context, src_location);
799 if (!surface_is_full_rect(dst_surface, dst_rect))
800 surface_load_location(dst_surface, context, dst_location);
801 else
802 wined3d_surface_prepare(dst_surface, context, dst_location);
804 gl_info = context->gl_info;
806 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
807 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
809 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
810 context_set_draw_buffer(context, GL_NONE);
811 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
812 context_invalidate_state(context, STATE_FRAMEBUFFER);
814 if (gl_mask & GL_DEPTH_BUFFER_BIT)
816 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
817 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
819 if (gl_mask & GL_STENCIL_BUFFER_BIT)
821 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
823 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
824 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
826 gl_info->gl_ops.gl.p_glStencilMask(~0U);
827 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
830 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
831 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
833 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
834 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
835 checkGLcall("glBlitFramebuffer()");
837 if (wined3d_settings.strict_draw_ordering)
838 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
840 context_release(context);
843 /* Blit between surface locations. Onscreen on different swapchains is not supported.
844 * Depth / stencil is not supported. Context activation is done by the caller. */
845 static void surface_blt_fbo(const struct wined3d_device *device,
846 struct wined3d_context *old_ctx, enum wined3d_texture_filter_type filter,
847 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
848 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
850 const struct wined3d_gl_info *gl_info;
851 struct wined3d_context *context = old_ctx;
852 struct wined3d_surface *required_rt, *restore_rt = NULL;
853 RECT src_rect, dst_rect;
854 GLenum gl_filter;
855 GLenum buffer;
857 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
858 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
859 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
860 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
861 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
863 src_rect = *src_rect_in;
864 dst_rect = *dst_rect_in;
866 switch (filter)
868 case WINED3D_TEXF_LINEAR:
869 gl_filter = GL_LINEAR;
870 break;
872 default:
873 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
874 case WINED3D_TEXF_NONE:
875 case WINED3D_TEXF_POINT:
876 gl_filter = GL_NEAREST;
877 break;
880 /* Resolve the source surface first if needed. */
881 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
882 && (src_surface->resource.format->id != dst_surface->resource.format->id
883 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
884 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
885 src_location = WINED3D_LOCATION_RB_RESOLVED;
887 /* Make sure the locations are up-to-date. Loading the destination
888 * surface isn't required if the entire surface is overwritten. (And is
889 * in fact harmful if we're being called by surface_load_location() with
890 * the purpose of loading the destination surface.) */
891 surface_load_location(src_surface, old_ctx, src_location);
892 if (!surface_is_full_rect(dst_surface, &dst_rect))
893 surface_load_location(dst_surface, old_ctx, dst_location);
894 else
895 wined3d_surface_prepare(dst_surface, old_ctx, dst_location);
898 if (src_location == WINED3D_LOCATION_DRAWABLE) required_rt = src_surface;
899 else if (dst_location == WINED3D_LOCATION_DRAWABLE) required_rt = dst_surface;
900 else required_rt = NULL;
902 if (required_rt && required_rt != old_ctx->current_rt)
904 restore_rt = old_ctx->current_rt;
905 context = context_acquire(device, required_rt);
908 if (!context->valid)
910 context_release(context);
911 WARN("Invalid context, skipping blit.\n");
912 return;
915 gl_info = context->gl_info;
917 if (src_location == WINED3D_LOCATION_DRAWABLE)
919 TRACE("Source surface %p is onscreen.\n", src_surface);
920 buffer = surface_get_gl_buffer(src_surface);
921 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
923 else
925 TRACE("Source surface %p is offscreen.\n", src_surface);
926 buffer = GL_COLOR_ATTACHMENT0;
929 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
930 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
931 checkGLcall("glReadBuffer()");
932 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
934 if (dst_location == WINED3D_LOCATION_DRAWABLE)
936 TRACE("Destination surface %p is onscreen.\n", dst_surface);
937 buffer = surface_get_gl_buffer(dst_surface);
938 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
940 else
942 TRACE("Destination surface %p is offscreen.\n", dst_surface);
943 buffer = GL_COLOR_ATTACHMENT0;
946 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
947 context_set_draw_buffer(context, buffer);
948 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
949 context_invalidate_state(context, STATE_FRAMEBUFFER);
951 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
952 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
953 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
954 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
955 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
957 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
958 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
960 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
961 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
962 checkGLcall("glBlitFramebuffer()");
964 if (wined3d_settings.strict_draw_ordering
965 || (dst_location == WINED3D_LOCATION_DRAWABLE
966 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
967 gl_info->gl_ops.gl.p_glFlush();
969 if (restore_rt)
970 context_restore(context, restore_rt);
973 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
974 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
975 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
977 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
978 return FALSE;
980 /* Source and/or destination need to be on the GL side */
981 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
982 return FALSE;
984 switch (blit_op)
986 case WINED3D_BLIT_OP_COLOR_BLIT:
987 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
988 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
989 return FALSE;
990 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
991 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
992 return FALSE;
993 if (!(src_format->id == dst_format->id
994 || (is_identity_fixup(src_format->color_fixup)
995 && is_identity_fixup(dst_format->color_fixup))))
996 return FALSE;
997 break;
999 case WINED3D_BLIT_OP_DEPTH_BLIT:
1000 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1001 return FALSE;
1002 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1003 return FALSE;
1004 /* Accept pure swizzle fixups for depth formats. In general we
1005 * ignore the stencil component (if present) at the moment and the
1006 * swizzle is not relevant with just the depth component. */
1007 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
1008 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
1009 return FALSE;
1010 break;
1012 default:
1013 return FALSE;
1016 return TRUE;
1019 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1021 const struct wined3d_format *format = surface->resource.format;
1023 switch (format->id)
1025 case WINED3DFMT_S1_UINT_D15_UNORM:
1026 *float_depth = depth / (float)0x00007fff;
1027 break;
1029 case WINED3DFMT_D16_UNORM:
1030 *float_depth = depth / (float)0x0000ffff;
1031 break;
1033 case WINED3DFMT_D24_UNORM_S8_UINT:
1034 case WINED3DFMT_X8D24_UNORM:
1035 *float_depth = depth / (float)0x00ffffff;
1036 break;
1038 case WINED3DFMT_D32_UNORM:
1039 *float_depth = depth / (float)0xffffffff;
1040 break;
1042 default:
1043 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1044 return FALSE;
1047 return TRUE;
1050 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1052 struct wined3d_resource *resource = &surface->container->resource;
1053 struct wined3d_device *device = resource->device;
1054 struct wined3d_rendertarget_view_desc view_desc;
1055 struct wined3d_rendertarget_view *view;
1056 const struct blit_shader *blitter;
1057 HRESULT hr;
1059 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
1060 WINED3D_BLIT_OP_DEPTH_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
1062 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1063 return WINED3DERR_INVALIDCALL;
1066 view_desc.format_id = resource->format->id;
1067 view_desc.u.texture.level_idx = surface->texture_level;
1068 view_desc.u.texture.layer_idx = surface->texture_layer;
1069 view_desc.u.texture.layer_count = 1;
1070 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
1071 resource, NULL, &wined3d_null_parent_ops, &view)))
1073 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
1074 return hr;
1077 hr = blitter->depth_fill(device, view, rect, depth);
1078 wined3d_rendertarget_view_decref(view);
1080 return hr;
1083 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1084 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1086 struct wined3d_device *device = src_surface->resource.device;
1088 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1089 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1090 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1091 return WINED3DERR_INVALIDCALL;
1093 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1095 surface_modify_ds_location(dst_surface, dst_location,
1096 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1098 return WINED3D_OK;
1101 /* Context activation is done by the caller. */
1102 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1104 GL_EXTCALL(glDeleteBuffers(1, &surface->pbo));
1105 checkGLcall("glDeleteBuffers(1, &surface->pbo)");
1107 surface->pbo = 0;
1108 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1111 static ULONG surface_resource_incref(struct wined3d_resource *resource)
1113 struct wined3d_surface *surface = surface_from_resource(resource);
1115 TRACE("surface %p, container %p.\n", surface, surface->container);
1117 return wined3d_texture_incref(surface->container);
1120 static ULONG surface_resource_decref(struct wined3d_resource *resource)
1122 struct wined3d_surface *surface = surface_from_resource(resource);
1124 TRACE("surface %p, container %p.\n", surface, surface->container);
1126 return wined3d_texture_decref(surface->container);
1129 static void surface_unload(struct wined3d_resource *resource)
1131 struct wined3d_surface *surface = surface_from_resource(resource);
1132 struct wined3d_renderbuffer_entry *entry, *entry2;
1133 struct wined3d_device *device = resource->device;
1134 const struct wined3d_gl_info *gl_info;
1135 struct wined3d_context *context;
1137 TRACE("surface %p.\n", surface);
1139 context = context_acquire(device, NULL);
1140 gl_info = context->gl_info;
1142 if (resource->pool == WINED3D_POOL_DEFAULT)
1144 /* Default pool resources are supposed to be destroyed before Reset is called.
1145 * Implicit resources stay however. So this means we have an implicit render target
1146 * or depth stencil. The content may be destroyed, but we still have to tear down
1147 * opengl resources, so we cannot leave early.
1149 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1150 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1151 * or the depth stencil into an FBO the texture or render buffer will be removed
1152 * and all flags get lost */
1153 if (resource->usage & WINED3DUSAGE_DEPTHSTENCIL)
1155 surface_validate_location(surface, WINED3D_LOCATION_DISCARDED);
1156 surface_invalidate_location(surface, ~WINED3D_LOCATION_DISCARDED);
1158 else
1160 surface_prepare_system_memory(surface);
1161 memset(surface->resource.heap_memory, 0, surface->resource.size);
1162 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1163 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1166 else
1168 surface_prepare_map_memory(surface);
1169 surface_load_location(surface, context, surface->resource.map_binding);
1170 surface_invalidate_location(surface, ~surface->resource.map_binding);
1173 /* Destroy PBOs, but load them into real sysmem before */
1174 if (surface->pbo)
1175 surface_remove_pbo(surface, gl_info);
1177 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1178 * all application-created targets the application has to release the surface
1179 * before calling _Reset
1181 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1183 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1184 list_remove(&entry->entry);
1185 HeapFree(GetProcessHeap(), 0, entry);
1187 list_init(&surface->renderbuffers);
1188 surface->current_renderbuffer = NULL;
1190 if (surface->rb_multisample)
1192 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1193 surface->rb_multisample = 0;
1195 if (surface->rb_resolved)
1197 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1198 surface->rb_resolved = 0;
1201 context_release(context);
1203 resource_unload(resource);
1206 static HRESULT surface_resource_sub_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx,
1207 struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
1209 ERR("Not supported on sub-resources.\n");
1210 return WINED3DERR_INVALIDCALL;
1213 static HRESULT surface_resource_sub_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx)
1215 ERR("Not supported on sub-resources.\n");
1216 return WINED3DERR_INVALIDCALL;
1219 static const struct wined3d_resource_ops surface_resource_ops =
1221 surface_resource_incref,
1222 surface_resource_decref,
1223 surface_unload,
1224 surface_resource_sub_resource_map,
1225 surface_resource_sub_resource_unmap,
1228 static const struct wined3d_surface_ops surface_ops =
1230 surface_private_setup,
1231 surface_unmap,
1234 /*****************************************************************************
1235 * Initializes the GDI surface, aka creates the DIB section we render to
1236 * The DIB section creation is done by calling GetDC, which will create the
1237 * section and releasing the dc to allow the app to use it. The dib section
1238 * will stay until the surface is released
1240 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1241 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1242 * avoid confusion in the shared surface code.
1244 * Returns:
1245 * WINED3D_OK on success
1246 * The return values of called methods on failure
1248 *****************************************************************************/
1249 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1251 HRESULT hr;
1253 TRACE("surface %p.\n", surface);
1255 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1257 ERR("Overlays not yet supported by GDI surfaces.\n");
1258 return WINED3DERR_INVALIDCALL;
1261 /* Sysmem textures have memory already allocated - release it,
1262 * this avoids an unnecessary memcpy. */
1263 hr = surface_create_dib_section(surface);
1264 if (FAILED(hr))
1265 return hr;
1266 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1268 /* We don't mind the nonpow2 stuff in GDI. */
1269 surface->pow2Width = surface->resource.width;
1270 surface->pow2Height = surface->resource.height;
1272 return WINED3D_OK;
1275 static void gdi_surface_unmap(struct wined3d_surface *surface)
1277 TRACE("surface %p.\n", surface);
1279 /* Tell the swapchain to update the screen. */
1280 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
1281 x11_copy_to_screen(surface->container->swapchain, &surface->lockedRect);
1283 memset(&surface->lockedRect, 0, sizeof(RECT));
1286 static const struct wined3d_surface_ops gdi_surface_ops =
1288 gdi_surface_private_setup,
1289 gdi_surface_unmap,
1292 /* This call just downloads data, the caller is responsible for binding the
1293 * correct texture. */
1294 /* Context activation is done by the caller. */
1295 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1296 DWORD dst_location)
1298 const struct wined3d_format *format = surface->resource.format;
1299 struct wined3d_bo_address data;
1301 /* Only support read back of converted P8 surfaces. */
1302 if (surface->container->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1304 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1305 return;
1308 surface_get_memory(surface, &data, dst_location);
1310 if (surface->container->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
1312 TRACE("(%p) : Calling glGetCompressedTexImage level %d, format %#x, type %#x, data %p.\n",
1313 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1315 if (data.buffer_object)
1317 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1318 checkGLcall("glBindBuffer");
1319 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, NULL));
1320 checkGLcall("glGetCompressedTexImage");
1321 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1322 checkGLcall("glBindBuffer");
1324 else
1326 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target,
1327 surface->texture_level, data.addr));
1328 checkGLcall("glGetCompressedTexImage");
1331 else
1333 unsigned int dst_row_pitch, dst_slice_pitch;
1334 unsigned int src_row_pitch, src_slice_pitch;
1335 GLenum gl_format = format->glFormat;
1336 GLenum gl_type = format->glType;
1337 void *mem;
1339 if (surface->flags & SFLAG_NONPOW2)
1341 wined3d_texture_get_pitch(surface->container, surface->texture_level, &dst_row_pitch, &dst_slice_pitch);
1342 wined3d_format_calculate_pitch(format, surface->resource.device->surface_alignment,
1343 surface->pow2Width, surface->pow2Height, &src_row_pitch, &src_slice_pitch);
1344 mem = HeapAlloc(GetProcessHeap(), 0, src_slice_pitch);
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("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
1430 for (y = 0; y < surface->resource.height; ++y)
1432 memcpy(dst_data, src_data, dst_row_pitch);
1433 src_data += src_row_pitch;
1434 dst_data += dst_row_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 unsigned int src_row_pitch, src_slice_pitch;
1594 const struct wined3d_format *src_format;
1595 const struct wined3d_format *dst_format;
1596 unsigned int src_fmt_flags, dst_fmt_flags;
1597 const struct wined3d_gl_info *gl_info;
1598 struct wined3d_context *context;
1599 struct wined3d_bo_address data;
1600 UINT update_w, update_h;
1601 UINT dst_w, dst_h;
1602 RECT r, dst_rect;
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 wined3d_texture_get_pitch(src_surface->container, src_surface->texture_level, &src_row_pitch, &src_slice_pitch);
1692 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1693 src_row_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 wined3d_texture_get_pitch(surface->container, surface->texture_level, &row_pitch, &slice_pitch);
1894 TRACE("Returning %u.\n", row_pitch);
1896 return row_pitch;
1899 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1901 struct wined3d_resource *texture_resource = &surface->container->resource;
1902 unsigned int width, height;
1903 BOOL create_dib = FALSE;
1904 DWORD valid_location = 0;
1905 HRESULT hr;
1907 if (surface->flags & SFLAG_DIBSECTION)
1909 DeleteDC(surface->hDC);
1910 DeleteObject(surface->dib.DIBsection);
1911 surface->dib.bitmap_data = NULL;
1912 surface->flags &= ~SFLAG_DIBSECTION;
1913 create_dib = TRUE;
1916 surface->locations = 0;
1917 wined3d_resource_free_sysmem(&surface->resource);
1919 width = texture_resource->width;
1920 height = texture_resource->height;
1921 surface->resource.width = width;
1922 surface->resource.height = height;
1923 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
1924 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
1926 surface->pow2Width = width;
1927 surface->pow2Height = height;
1929 else
1931 surface->pow2Width = surface->pow2Height = 1;
1932 while (surface->pow2Width < width)
1933 surface->pow2Width <<= 1;
1934 while (surface->pow2Height < height)
1935 surface->pow2Height <<= 1;
1938 if (surface->pow2Width != width || surface->pow2Height != height)
1939 surface->flags |= SFLAG_NONPOW2;
1940 else
1941 surface->flags &= ~SFLAG_NONPOW2;
1943 if (surface->container->user_memory)
1945 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
1946 valid_location = WINED3D_LOCATION_USER_MEMORY;
1948 surface->resource.format = texture_resource->format;
1949 surface->resource.multisample_type = texture_resource->multisample_type;
1950 surface->resource.multisample_quality = texture_resource->multisample_quality;
1951 surface->resource.size = surface->container->slice_pitch;
1953 /* The format might be changed to a format that needs conversion.
1954 * If the surface didn't use PBOs previously but could now, don't
1955 * change it - whatever made us not use PBOs might come back, e.g.
1956 * color keys. */
1957 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
1958 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
1960 if (create_dib)
1962 if (FAILED(hr = surface_create_dib_section(surface)))
1964 ERR("Failed to create dib section, hr %#x.\n", hr);
1965 return hr;
1967 if (!valid_location)
1968 valid_location = WINED3D_LOCATION_DIB;
1971 if (!valid_location)
1973 surface_prepare_system_memory(surface);
1974 valid_location = WINED3D_LOCATION_SYSMEM;
1977 surface_validate_location(surface, valid_location);
1979 return WINED3D_OK;
1982 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
1983 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1985 unsigned short *dst_s;
1986 const float *src_f;
1987 unsigned int x, y;
1989 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1991 for (y = 0; y < h; ++y)
1993 src_f = (const float *)(src + y * pitch_in);
1994 dst_s = (unsigned short *) (dst + y * pitch_out);
1995 for (x = 0; x < w; ++x)
1997 dst_s[x] = float_32_to_16(src_f + x);
2002 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2003 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2005 static const unsigned char convert_5to8[] =
2007 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2008 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2009 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2010 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2012 static const unsigned char convert_6to8[] =
2014 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2015 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2016 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2017 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2018 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2019 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2020 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2021 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2023 unsigned int x, y;
2025 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2027 for (y = 0; y < h; ++y)
2029 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2030 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2031 for (x = 0; x < w; ++x)
2033 WORD pixel = src_line[x];
2034 dst_line[x] = 0xff000000u
2035 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
2036 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
2037 | convert_5to8[(pixel & 0x001fu)];
2042 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2043 * in both cases we're just setting the X / Alpha channel to 0xff. */
2044 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2045 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2047 unsigned int x, y;
2049 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2051 for (y = 0; y < h; ++y)
2053 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2054 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2056 for (x = 0; x < w; ++x)
2058 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2063 static inline BYTE cliptobyte(int x)
2065 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2068 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2069 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2071 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2072 unsigned int x, y;
2074 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2076 for (y = 0; y < h; ++y)
2078 const BYTE *src_line = src + y * pitch_in;
2079 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2080 for (x = 0; x < w; ++x)
2082 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2083 * C = Y - 16; D = U - 128; E = V - 128;
2084 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2085 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2086 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2087 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2088 * U and V are shared between the pixels. */
2089 if (!(x & 1)) /* For every even pixel, read new U and V. */
2091 d = (int) src_line[1] - 128;
2092 e = (int) src_line[3] - 128;
2093 r2 = 409 * e + 128;
2094 g2 = - 100 * d - 208 * e + 128;
2095 b2 = 516 * d + 128;
2097 c2 = 298 * ((int) src_line[0] - 16);
2098 dst_line[x] = 0xff000000
2099 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2100 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2101 | cliptobyte((c2 + b2) >> 8); /* blue */
2102 /* Scale RGB values to 0..255 range,
2103 * then clip them if still not in range (may be negative),
2104 * then shift them within DWORD if necessary. */
2105 src_line += 2;
2110 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2111 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2113 unsigned int x, y;
2114 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2116 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2118 for (y = 0; y < h; ++y)
2120 const BYTE *src_line = src + y * pitch_in;
2121 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2122 for (x = 0; x < w; ++x)
2124 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2125 * C = Y - 16; D = U - 128; E = V - 128;
2126 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2127 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2128 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2129 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2130 * U and V are shared between the pixels. */
2131 if (!(x & 1)) /* For every even pixel, read new U and V. */
2133 d = (int) src_line[1] - 128;
2134 e = (int) src_line[3] - 128;
2135 r2 = 409 * e + 128;
2136 g2 = - 100 * d - 208 * e + 128;
2137 b2 = 516 * d + 128;
2139 c2 = 298 * ((int) src_line[0] - 16);
2140 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2141 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2142 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2143 /* Scale RGB values to 0..255 range,
2144 * then clip them if still not in range (may be negative),
2145 * then shift them within DWORD if necessary. */
2146 src_line += 2;
2151 struct d3dfmt_converter_desc
2153 enum wined3d_format_id from, to;
2154 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2157 static const struct d3dfmt_converter_desc converters[] =
2159 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2160 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2161 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2162 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2163 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2164 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2167 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2168 enum wined3d_format_id to)
2170 unsigned int i;
2172 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2174 if (converters[i].from == from && converters[i].to == to)
2175 return &converters[i];
2178 return NULL;
2181 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2183 struct wined3d_map_desc src_map, dst_map;
2184 const struct d3dfmt_converter_desc *conv;
2185 struct wined3d_texture *ret = NULL;
2186 struct wined3d_resource_desc desc;
2187 struct wined3d_surface *dst;
2189 conv = find_converter(source->resource.format->id, to_fmt);
2190 if (!conv)
2192 FIXME("Cannot find a conversion function from format %s to %s.\n",
2193 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2194 return NULL;
2197 /* FIXME: Multisampled conversion? */
2198 wined3d_resource_get_desc(&source->resource, &desc);
2199 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
2200 desc.format = to_fmt;
2201 desc.usage = 0;
2202 desc.pool = WINED3D_POOL_SCRATCH;
2203 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2204 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
2205 NULL, NULL, &wined3d_null_parent_ops, &ret)))
2207 ERR("Failed to create a destination surface for conversion.\n");
2208 return NULL;
2210 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2212 memset(&src_map, 0, sizeof(src_map));
2213 memset(&dst_map, 0, sizeof(dst_map));
2215 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2217 ERR("Failed to lock the source surface.\n");
2218 wined3d_texture_decref(ret);
2219 return NULL;
2221 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2223 ERR("Failed to lock the destination surface.\n");
2224 wined3d_surface_unmap(source);
2225 wined3d_texture_decref(ret);
2226 return NULL;
2229 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2230 source->resource.width, source->resource.height);
2232 wined3d_surface_unmap(dst);
2233 wined3d_surface_unmap(source);
2235 return ret;
2238 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2239 unsigned int bpp, UINT pitch, DWORD color)
2241 BYTE *first;
2242 unsigned int x, y;
2244 /* Do first row */
2246 #define COLORFILL_ROW(type) \
2247 do { \
2248 type *d = (type *)buf; \
2249 for (x = 0; x < width; ++x) \
2250 d[x] = (type)color; \
2251 } while(0)
2253 switch (bpp)
2255 case 1:
2256 COLORFILL_ROW(BYTE);
2257 break;
2259 case 2:
2260 COLORFILL_ROW(WORD);
2261 break;
2263 case 3:
2265 BYTE *d = buf;
2266 for (x = 0; x < width; ++x, d += 3)
2268 d[0] = (color ) & 0xff;
2269 d[1] = (color >> 8) & 0xff;
2270 d[2] = (color >> 16) & 0xff;
2272 break;
2274 case 4:
2275 COLORFILL_ROW(DWORD);
2276 break;
2278 default:
2279 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2280 return WINED3DERR_NOTAVAILABLE;
2283 #undef COLORFILL_ROW
2285 /* Now copy first row. */
2286 first = buf;
2287 for (y = 1; y < height; ++y)
2289 buf += pitch;
2290 memcpy(buf, first, width * bpp);
2293 return WINED3D_OK;
2296 HRESULT wined3d_surface_unmap(struct wined3d_surface *surface)
2298 TRACE("surface %p.\n", surface);
2300 if (!surface->resource.map_count)
2302 WARN("Trying to unmap unmapped surface.\n");
2303 return WINEDDERR_NOTLOCKED;
2305 --surface->resource.map_count;
2307 surface->surface_ops->surface_unmap(surface);
2309 return WINED3D_OK;
2312 HRESULT wined3d_surface_map(struct wined3d_surface *surface, struct wined3d_map_desc *map_desc,
2313 const struct wined3d_box *box, DWORD flags)
2315 const struct wined3d_format *format = surface->resource.format;
2316 unsigned int fmt_flags = surface->container->resource.format_flags;
2317 struct wined3d_device *device = surface->resource.device;
2318 struct wined3d_context *context;
2319 const struct wined3d_gl_info *gl_info;
2320 BYTE *base_memory;
2322 TRACE("surface %p, map_desc %p, box %s, flags %#x.\n",
2323 surface, map_desc, debug_box(box), flags);
2325 if (surface->resource.map_count)
2327 WARN("Surface is already mapped.\n");
2328 return WINED3DERR_INVALIDCALL;
2331 if ((fmt_flags & WINED3DFMT_FLAG_BLOCKS) && box
2332 && !surface_check_block_align(surface, box))
2334 WARN("Map box %s is misaligned for %ux%u blocks.\n",
2335 debug_box(box), format->block_width, format->block_height);
2337 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2338 return WINED3DERR_INVALIDCALL;
2341 ++surface->resource.map_count;
2343 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2344 WARN("Trying to lock unlockable surface.\n");
2346 /* Performance optimization: Count how often a surface is mapped, if it is
2347 * mapped regularly do not throw away the system memory copy. This avoids
2348 * the need to download the surface from OpenGL all the time. The surface
2349 * is still downloaded if the OpenGL texture is changed. Note that this
2350 * only really makes sense for managed textures.*/
2351 if (!(surface->container->flags & WINED3D_TEXTURE_DYNAMIC_MAP)
2352 && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2354 if (++surface->lockCount > MAXLOCKCOUNT)
2356 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2357 surface->container->flags |= WINED3D_TEXTURE_DYNAMIC_MAP;
2361 surface_prepare_map_memory(surface);
2362 if (flags & WINED3D_MAP_DISCARD)
2364 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2365 wined3d_debug_location(surface->resource.map_binding));
2366 surface_validate_location(surface, surface->resource.map_binding);
2368 else
2370 struct wined3d_context *context = NULL;
2372 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2373 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2375 if (surface->resource.device->d3d_initialized)
2376 context = context_acquire(surface->resource.device, NULL);
2377 surface_load_location(surface, context, surface->resource.map_binding);
2378 if (context)
2379 context_release(context);
2382 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2383 surface_invalidate_location(surface, ~surface->resource.map_binding);
2385 switch (surface->resource.map_binding)
2387 case WINED3D_LOCATION_SYSMEM:
2388 base_memory = surface->resource.heap_memory;
2389 break;
2391 case WINED3D_LOCATION_USER_MEMORY:
2392 base_memory = surface->container->user_memory;
2393 break;
2395 case WINED3D_LOCATION_DIB:
2396 base_memory = surface->dib.bitmap_data;
2397 break;
2399 case WINED3D_LOCATION_BUFFER:
2400 context = context_acquire(device, NULL);
2401 gl_info = context->gl_info;
2403 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
2404 base_memory = GL_EXTCALL(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE));
2405 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
2406 checkGLcall("map PBO");
2408 context_release(context);
2409 break;
2411 default:
2412 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2413 base_memory = NULL;
2416 if (fmt_flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2418 map_desc->row_pitch = surface->resource.width * format->byte_count;
2419 map_desc->slice_pitch = surface->resource.height * map_desc->row_pitch;
2421 else
2423 wined3d_texture_get_pitch(surface->container, surface->texture_level,
2424 &map_desc->row_pitch, &map_desc->slice_pitch);
2427 if (!box)
2429 map_desc->data = base_memory;
2430 surface->lockedRect.left = 0;
2431 surface->lockedRect.top = 0;
2432 surface->lockedRect.right = surface->resource.width;
2433 surface->lockedRect.bottom = surface->resource.height;
2435 else
2437 if ((fmt_flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2439 /* Compressed textures are block based, so calculate the offset of
2440 * the block that contains the top-left pixel of the locked rectangle. */
2441 map_desc->data = base_memory
2442 + ((box->top / format->block_height) * map_desc->row_pitch)
2443 + ((box->left / format->block_width) * format->block_byte_count);
2445 else
2447 map_desc->data = base_memory
2448 + (map_desc->row_pitch * box->top)
2449 + (box->left * format->byte_count);
2451 surface->lockedRect.left = box->left;
2452 surface->lockedRect.top = box->top;
2453 surface->lockedRect.right = box->right;
2454 surface->lockedRect.bottom = box->bottom;
2457 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2458 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2460 return WINED3D_OK;
2463 static void read_from_framebuffer(struct wined3d_surface *surface,
2464 struct wined3d_context *old_ctx, DWORD dst_location)
2466 struct wined3d_device *device = surface->resource.device;
2467 const struct wined3d_gl_info *gl_info;
2468 struct wined3d_context *context = old_ctx;
2469 struct wined3d_surface *restore_rt = NULL;
2470 unsigned int row_pitch, slice_pitch;
2471 BYTE *mem;
2472 BYTE *row, *top, *bottom;
2473 int i;
2474 BOOL srcIsUpsideDown;
2475 struct wined3d_bo_address data;
2477 surface_get_memory(surface, &data, dst_location);
2479 if (surface != old_ctx->current_rt)
2481 restore_rt = old_ctx->current_rt;
2482 context = context_acquire(device, surface);
2485 context_apply_blit_state(context, device);
2486 gl_info = context->gl_info;
2488 /* Select the correct read buffer, and give some debug output.
2489 * There is no need to keep track of the current read buffer or reset it, every part of the code
2490 * that reads sets the read buffer as desired.
2492 if (wined3d_resource_is_offscreen(&surface->container->resource))
2494 /* Mapping the primary render target which is not on a swapchain.
2495 * Read from the back buffer. */
2496 TRACE("Mapping offscreen render target.\n");
2497 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2498 srcIsUpsideDown = TRUE;
2500 else
2502 /* Onscreen surfaces are always part of a swapchain */
2503 GLenum buffer = surface_get_gl_buffer(surface);
2504 TRACE("Mapping %#x buffer.\n", buffer);
2505 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2506 checkGLcall("glReadBuffer");
2507 srcIsUpsideDown = FALSE;
2510 if (data.buffer_object)
2512 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
2513 checkGLcall("glBindBuffer");
2516 wined3d_texture_get_pitch(surface->container, surface->texture_level, &row_pitch, &slice_pitch);
2518 /* Setup pixel store pack state -- to glReadPixels into the correct place */
2519 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / surface->resource.format->byte_count);
2520 checkGLcall("glPixelStorei");
2522 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
2523 surface->resource.width, surface->resource.height,
2524 surface->resource.format->glFormat,
2525 surface->resource.format->glType, data.addr);
2526 checkGLcall("glReadPixels");
2528 /* Reset previous pixel store pack state */
2529 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
2530 checkGLcall("glPixelStorei");
2532 if (!srcIsUpsideDown)
2534 /* glReadPixels returns the image upside down, and there is no way to
2535 * prevent this. Flip the lines in software. */
2537 if (!(row = HeapAlloc(GetProcessHeap(), 0, row_pitch)))
2538 goto error;
2540 if (data.buffer_object)
2542 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
2543 checkGLcall("glMapBuffer");
2545 else
2546 mem = data.addr;
2548 top = mem;
2549 bottom = mem + row_pitch * (surface->resource.height - 1);
2550 for (i = 0; i < surface->resource.height / 2; i++)
2552 memcpy(row, top, row_pitch);
2553 memcpy(top, bottom, row_pitch);
2554 memcpy(bottom, row, row_pitch);
2555 top += row_pitch;
2556 bottom -= row_pitch;
2558 HeapFree(GetProcessHeap(), 0, row);
2560 if (data.buffer_object)
2561 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
2564 error:
2565 if (data.buffer_object)
2567 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2568 checkGLcall("glBindBuffer");
2571 if (restore_rt)
2572 context_restore(context, restore_rt);
2575 /* Read the framebuffer contents into a texture. Note that this function
2576 * doesn't do any kind of flipping. Using this on an onscreen surface will
2577 * result in a flipped D3D texture.
2579 * Context activation is done by the caller. This function may temporarily
2580 * switch to a different context and restore the original one before return. */
2581 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
2583 struct wined3d_device *device = surface->resource.device;
2584 const struct wined3d_gl_info *gl_info;
2585 struct wined3d_context *context = old_ctx;
2586 struct wined3d_surface *restore_rt = NULL;
2588 if (old_ctx->current_rt != surface)
2590 restore_rt = old_ctx->current_rt;
2591 context = context_acquire(device, surface);
2594 gl_info = context->gl_info;
2595 device_invalidate_state(device, STATE_FRAMEBUFFER);
2597 wined3d_texture_prepare_texture(surface->container, context, srgb);
2598 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
2600 TRACE("Reading back offscreen render target %p.\n", surface);
2602 if (wined3d_resource_is_offscreen(&surface->container->resource))
2603 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2604 else
2605 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
2606 checkGLcall("glReadBuffer");
2608 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
2609 0, 0, 0, 0, surface->resource.width, surface->resource.height);
2610 checkGLcall("glCopyTexSubImage2D");
2612 if (restore_rt)
2613 context_restore(context, restore_rt);
2616 static void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2618 if (multisample)
2620 DWORD samples;
2622 if (surface->rb_multisample)
2623 return;
2625 /* TODO: Nvidia exposes their Coverage Sample Anti-Aliasing (CSAA) feature
2626 * through type == MULTISAMPLE_XX and quality != 0. This could be mapped
2627 * to GL_NV_framebuffer_multisample_coverage.
2629 * AMD has a similar feature called Enhanced Quality Anti-Aliasing (EQAA),
2630 * but it does not have an equivalent OpenGL extension. */
2632 /* We advertise as many WINED3D_MULTISAMPLE_NON_MASKABLE quality levels
2633 * as the count of advertised multisample types for the surface format. */
2634 if (surface->resource.multisample_type == WINED3D_MULTISAMPLE_NON_MASKABLE)
2636 const struct wined3d_format *format = surface->resource.format;
2637 unsigned int i, count = 0;
2639 for (i = 0; i < sizeof(format->multisample_types) * 8; ++i)
2641 if (format->multisample_types & 1u << i)
2643 if (surface->resource.multisample_quality == count++)
2644 break;
2647 samples = i + 1;
2649 else
2651 samples = surface->resource.multisample_type;
2654 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2655 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2656 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
2657 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
2658 checkGLcall("glRenderbufferStorageMultisample()");
2659 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2661 else
2663 if (surface->rb_resolved)
2664 return;
2666 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2667 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2668 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
2669 surface->pow2Width, surface->pow2Height);
2670 checkGLcall("glRenderbufferStorage()");
2671 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2675 /* Does a direct frame buffer -> texture copy. Stretching is done with single
2676 * pixel copy calls. */
2677 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2678 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2680 struct wined3d_device *device = dst_surface->resource.device;
2681 const struct wined3d_gl_info *gl_info;
2682 float xrel, yrel;
2683 struct wined3d_context *context;
2684 BOOL upsidedown = FALSE;
2685 RECT dst_rect = *dst_rect_in;
2687 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2688 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2690 if(dst_rect.top > dst_rect.bottom) {
2691 UINT tmp = dst_rect.bottom;
2692 dst_rect.bottom = dst_rect.top;
2693 dst_rect.top = tmp;
2694 upsidedown = TRUE;
2697 context = context_acquire(device, src_surface);
2698 gl_info = context->gl_info;
2699 context_apply_blit_state(context, device);
2700 wined3d_texture_load(dst_surface->container, context, FALSE);
2702 /* Bind the target texture */
2703 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
2704 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
2706 TRACE("Reading from an offscreen target\n");
2707 upsidedown = !upsidedown;
2708 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2710 else
2712 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
2714 checkGLcall("glReadBuffer");
2716 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
2717 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
2719 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2721 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2723 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2724 ERR("Texture filtering not supported in direct blit.\n");
2726 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2727 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2729 ERR("Texture filtering not supported in direct blit\n");
2732 if (upsidedown
2733 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2734 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2736 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
2737 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2738 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
2739 src_rect->left, src_surface->resource.height - src_rect->bottom,
2740 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2742 else
2744 LONG row;
2745 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
2746 /* I have to process this row by row to swap the image,
2747 * otherwise it would be upside down, so stretching in y direction
2748 * doesn't cost extra time
2750 * However, stretching in x direction can be avoided if not necessary
2752 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
2753 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2755 /* Well, that stuff works, but it's very slow.
2756 * find a better way instead
2758 LONG col;
2760 for (col = dst_rect.left; col < dst_rect.right; ++col)
2762 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2763 dst_rect.left + col /* x offset */, row /* y offset */,
2764 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
2767 else
2769 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2770 dst_rect.left /* x offset */, row /* y offset */,
2771 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
2775 checkGLcall("glCopyTexSubImage2D");
2777 context_release(context);
2779 /* The texture is now most up to date - If the surface is a render target
2780 * and has a drawable, this path is never entered. */
2781 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
2782 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
2785 /* Uses the hardware to stretch and flip the image */
2786 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2787 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2789 struct wined3d_device *device = dst_surface->resource.device;
2790 GLuint src, backup = 0;
2791 float left, right, top, bottom; /* Texture coordinates */
2792 UINT fbwidth = src_surface->resource.width;
2793 UINT fbheight = src_surface->resource.height;
2794 const struct wined3d_gl_info *gl_info;
2795 struct wined3d_context *context;
2796 GLenum drawBuffer = GL_BACK;
2797 GLenum texture_target;
2798 BOOL noBackBufferBackup;
2799 BOOL src_offscreen;
2800 BOOL upsidedown = FALSE;
2801 RECT dst_rect = *dst_rect_in;
2803 TRACE("Using hwstretch blit\n");
2804 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2805 context = context_acquire(device, src_surface);
2806 gl_info = context->gl_info;
2807 context_apply_blit_state(context, device);
2808 wined3d_texture_load(dst_surface->container, context, FALSE);
2810 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
2811 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2812 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
2814 /* Get it a description */
2815 wined3d_texture_load(src_surface->container, context, FALSE);
2818 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2819 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2821 if (context->aux_buffers >= 2)
2823 /* Got more than one aux buffer? Use the 2nd aux buffer */
2824 drawBuffer = GL_AUX1;
2826 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
2828 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2829 drawBuffer = GL_AUX0;
2832 if (noBackBufferBackup)
2834 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
2835 checkGLcall("glGenTextures");
2836 context_bind_texture(context, GL_TEXTURE_2D, backup);
2837 texture_target = GL_TEXTURE_2D;
2839 else
2841 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2842 * we are reading from the back buffer, the backup can be used as source texture
2844 texture_target = src_surface->texture_target;
2845 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
2846 gl_info->gl_ops.gl.p_glEnable(texture_target);
2847 checkGLcall("glEnable(texture_target)");
2849 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2850 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
2853 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2854 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2856 if(dst_rect.top > dst_rect.bottom) {
2857 UINT tmp = dst_rect.bottom;
2858 dst_rect.bottom = dst_rect.top;
2859 dst_rect.top = tmp;
2860 upsidedown = TRUE;
2863 if (src_offscreen)
2865 TRACE("Reading from an offscreen target\n");
2866 upsidedown = !upsidedown;
2867 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2869 else
2871 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
2874 /* TODO: Only back up the part that will be overwritten */
2875 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
2877 checkGLcall("glCopyTexSubImage2D");
2879 /* No issue with overriding these - the sampler is dirty due to blit usage */
2880 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
2881 checkGLcall("glTexParameteri");
2882 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2883 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
2884 checkGLcall("glTexParameteri");
2886 if (!src_surface->container->swapchain
2887 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
2889 src = backup ? backup : src_surface->container->texture_rgb.name;
2891 else
2893 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
2894 checkGLcall("glReadBuffer(GL_FRONT)");
2896 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
2897 checkGLcall("glGenTextures(1, &src)");
2898 context_bind_texture(context, GL_TEXTURE_2D, src);
2900 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
2901 * out for power of 2 sizes
2903 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
2904 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2905 checkGLcall("glTexImage2D");
2906 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
2908 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2909 checkGLcall("glTexParameteri");
2910 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2911 checkGLcall("glTexParameteri");
2913 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
2914 checkGLcall("glReadBuffer(GL_BACK)");
2916 if (texture_target != GL_TEXTURE_2D)
2918 gl_info->gl_ops.gl.p_glDisable(texture_target);
2919 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2920 texture_target = GL_TEXTURE_2D;
2923 checkGLcall("glEnd and previous");
2925 left = src_rect->left;
2926 right = src_rect->right;
2928 if (!upsidedown)
2930 top = src_surface->resource.height - src_rect->top;
2931 bottom = src_surface->resource.height - src_rect->bottom;
2933 else
2935 top = src_surface->resource.height - src_rect->bottom;
2936 bottom = src_surface->resource.height - src_rect->top;
2939 if (src_surface->container->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
2941 left /= src_surface->pow2Width;
2942 right /= src_surface->pow2Width;
2943 top /= src_surface->pow2Height;
2944 bottom /= src_surface->pow2Height;
2947 /* draw the source texture stretched and upside down. The correct surface is bound already */
2948 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2949 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2951 context_set_draw_buffer(context, drawBuffer);
2952 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
2954 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2955 /* bottom left */
2956 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
2957 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2959 /* top left */
2960 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
2961 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
2963 /* top right */
2964 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
2965 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2967 /* bottom right */
2968 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
2969 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
2970 gl_info->gl_ops.gl.p_glEnd();
2971 checkGLcall("glEnd and previous");
2973 if (texture_target != dst_surface->texture_target)
2975 gl_info->gl_ops.gl.p_glDisable(texture_target);
2976 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
2977 texture_target = dst_surface->texture_target;
2980 /* Now read the stretched and upside down image into the destination texture */
2981 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
2982 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
2984 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
2985 0, 0, /* We blitted the image to the origin */
2986 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2987 checkGLcall("glCopyTexSubImage2D");
2989 if (drawBuffer == GL_BACK)
2991 /* Write the back buffer backup back. */
2992 if (backup)
2994 if (texture_target != GL_TEXTURE_2D)
2996 gl_info->gl_ops.gl.p_glDisable(texture_target);
2997 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2998 texture_target = GL_TEXTURE_2D;
3000 context_bind_texture(context, GL_TEXTURE_2D, backup);
3002 else
3004 if (texture_target != src_surface->texture_target)
3006 gl_info->gl_ops.gl.p_glDisable(texture_target);
3007 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3008 texture_target = src_surface->texture_target;
3010 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3013 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3014 /* top left */
3015 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3016 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3018 /* bottom left */
3019 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3020 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3022 /* bottom right */
3023 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3024 (float)fbheight / (float)src_surface->pow2Height);
3025 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3027 /* top right */
3028 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3029 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3030 gl_info->gl_ops.gl.p_glEnd();
3032 gl_info->gl_ops.gl.p_glDisable(texture_target);
3033 checkGLcall("glDisable(texture_target)");
3035 /* Cleanup */
3036 if (src != src_surface->container->texture_rgb.name && src != backup)
3038 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3039 checkGLcall("glDeleteTextures(1, &src)");
3041 if (backup)
3043 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3044 checkGLcall("glDeleteTextures(1, &backup)");
3047 if (wined3d_settings.strict_draw_ordering)
3048 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3050 context_release(context);
3052 /* The texture is now most up to date - If the surface is a render target
3053 * and has a drawable, this path is never entered. */
3054 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3055 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3058 /* Front buffer coordinates are always full screen coordinates, but our GL
3059 * drawable is limited to the window's client area. The sysmem and texture
3060 * copies do have the full screen size. Note that GL has a bottom-left
3061 * origin, while D3D has a top-left origin. */
3062 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3064 UINT drawable_height;
3066 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3068 POINT offset = {0, 0};
3069 RECT windowsize;
3071 ScreenToClient(window, &offset);
3072 OffsetRect(rect, offset.x, offset.y);
3074 GetClientRect(window, &windowsize);
3075 drawable_height = windowsize.bottom - windowsize.top;
3077 else
3079 drawable_height = surface->resource.height;
3082 rect->top = drawable_height - rect->top;
3083 rect->bottom = drawable_height - rect->bottom;
3086 /* Context activation is done by the caller. */
3087 static void surface_blt_to_drawable(const struct wined3d_device *device,
3088 struct wined3d_context *old_ctx,
3089 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3090 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3091 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3093 const struct wined3d_gl_info *gl_info;
3094 struct wined3d_context *context = old_ctx;
3095 struct wined3d_surface *restore_rt = NULL;
3096 RECT src_rect, dst_rect;
3098 src_rect = *src_rect_in;
3099 dst_rect = *dst_rect_in;
3102 if (old_ctx->current_rt != dst_surface)
3104 restore_rt = old_ctx->current_rt;
3105 context = context_acquire(device, dst_surface);
3108 gl_info = context->gl_info;
3110 /* Make sure the surface is up-to-date. This should probably use
3111 * surface_load_location() and worry about the destination surface too,
3112 * unless we're overwriting it completely. */
3113 wined3d_texture_load(src_surface->container, context, FALSE);
3115 /* Activate the destination context, set it up for blitting */
3116 context_apply_blit_state(context, device);
3118 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3119 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3121 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
3123 if (alpha_test)
3125 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3126 checkGLcall("glEnable(GL_ALPHA_TEST)");
3128 /* For P8 surfaces, the alpha component contains the palette index.
3129 * Which means that the colorkey is one of the palette entries. In
3130 * other cases pixels that should be masked away have alpha set to 0. */
3131 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3132 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3133 (float)src_surface->container->async.src_blt_color_key.color_space_low_value / 255.0f);
3134 else
3135 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3136 checkGLcall("glAlphaFunc");
3138 else
3140 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3141 checkGLcall("glDisable(GL_ALPHA_TEST)");
3144 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3146 if (alpha_test)
3148 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3149 checkGLcall("glDisable(GL_ALPHA_TEST)");
3152 /* Leave the opengl state valid for blitting */
3153 device->blitter->unset_shader(context->gl_info);
3155 if (wined3d_settings.strict_draw_ordering
3156 || (dst_surface->container->swapchain
3157 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3158 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3160 if (restore_rt)
3161 context_restore(context, restore_rt);
3164 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3166 struct wined3d_resource *resource = &s->container->resource;
3167 struct wined3d_device *device = resource->device;
3168 struct wined3d_rendertarget_view_desc view_desc;
3169 struct wined3d_rendertarget_view *view;
3170 const struct blit_shader *blitter;
3171 HRESULT hr;
3173 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
3174 WINED3D_BLIT_OP_COLOR_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
3176 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3177 return WINED3DERR_INVALIDCALL;
3180 view_desc.format_id = resource->format->id;
3181 view_desc.u.texture.level_idx = s->texture_level;
3182 view_desc.u.texture.layer_idx = s->texture_layer;
3183 view_desc.u.texture.layer_count = 1;
3184 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
3185 resource, NULL, &wined3d_null_parent_ops, &view)))
3187 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
3188 return hr;
3191 hr = blitter->color_fill(device, view, rect, color);
3192 wined3d_rendertarget_view_decref(view);
3194 return hr;
3197 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3198 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3199 enum wined3d_texture_filter_type filter)
3201 struct wined3d_device *device = dst_surface->resource.device;
3202 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3203 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3205 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3206 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3207 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3209 /* Get the swapchain. One of the surfaces has to be a primary surface */
3210 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3212 WARN("Destination is in sysmem, rejecting gl blt\n");
3213 return WINED3DERR_INVALIDCALL;
3216 dst_swapchain = dst_surface->container->swapchain;
3218 if (src_surface)
3220 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3222 WARN("Src is in sysmem, rejecting gl blt\n");
3223 return WINED3DERR_INVALIDCALL;
3226 src_swapchain = src_surface->container->swapchain;
3228 else
3230 src_swapchain = NULL;
3233 /* Early sort out of cases where no render target is used */
3234 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
3236 TRACE("No surface is render target, not using hardware blit.\n");
3237 return WINED3DERR_INVALIDCALL;
3240 /* No destination color keying supported */
3241 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
3243 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3244 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3245 return WINED3DERR_INVALIDCALL;
3248 if (dst_swapchain && dst_swapchain == src_swapchain)
3250 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3251 return WINED3DERR_INVALIDCALL;
3254 if (dst_swapchain && src_swapchain)
3256 FIXME("Implement hardware blit between two different swapchains\n");
3257 return WINED3DERR_INVALIDCALL;
3260 if (dst_swapchain)
3262 /* Handled with regular texture -> swapchain blit */
3263 if (src_surface == rt)
3264 TRACE("Blit from active render target to a swapchain\n");
3266 else if (src_swapchain && dst_surface == rt)
3268 FIXME("Implement blit from a swapchain to the active render target\n");
3269 return WINED3DERR_INVALIDCALL;
3272 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
3274 /* Blit from render target to texture */
3275 BOOL stretchx;
3277 /* P8 read back is not implemented */
3278 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
3279 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
3281 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3282 return WINED3DERR_INVALIDCALL;
3285 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3287 TRACE("Color keying not supported by frame buffer to texture blit\n");
3288 return WINED3DERR_INVALIDCALL;
3289 /* Destination color key is checked above */
3292 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
3293 stretchx = TRUE;
3294 else
3295 stretchx = FALSE;
3297 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3298 * flip the image nor scale it.
3300 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3301 * -> If the app wants an image width an unscaled width, copy it line per line
3302 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
3303 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3304 * back buffer. This is slower than reading line per line, thus not used for flipping
3305 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3306 * pixel by pixel. */
3307 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
3308 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
3310 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
3311 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
3313 else
3315 TRACE("Using hardware stretching to flip / stretch the texture.\n");
3316 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
3319 surface_evict_sysmem(dst_surface);
3321 return WINED3D_OK;
3324 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3325 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3326 return WINED3DERR_INVALIDCALL;
3329 /* Context activation is done by the caller. */
3330 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
3331 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
3333 struct wined3d_device *device = surface->resource.device;
3334 const struct wined3d_gl_info *gl_info = context->gl_info;
3335 GLint compare_mode = GL_NONE;
3336 struct blt_info info;
3337 GLint old_binding = 0;
3338 RECT rect;
3340 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
3342 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
3343 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
3344 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3345 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
3346 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
3347 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
3348 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
3349 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
3350 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3351 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
3352 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
3354 SetRect(&rect, 0, h, w, 0);
3355 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
3356 context_active_texture(context, context->gl_info, 0);
3357 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
3358 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
3359 if (gl_info->supported[ARB_SHADOW])
3361 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
3362 if (compare_mode != GL_NONE)
3363 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
3366 device->shader_backend->shader_select_depth_blt(device->shader_priv,
3367 gl_info, info.tex_type, &surface->ds_current_size);
3369 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
3370 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
3371 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
3372 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
3373 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
3374 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
3375 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
3376 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
3377 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
3378 gl_info->gl_ops.gl.p_glEnd();
3380 if (compare_mode != GL_NONE)
3381 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
3382 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
3384 gl_info->gl_ops.gl.p_glPopAttrib();
3386 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
3389 void surface_modify_ds_location(struct wined3d_surface *surface,
3390 DWORD location, UINT w, UINT h)
3392 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
3394 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
3395 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
3396 wined3d_texture_set_dirty(surface->container);
3398 surface->ds_current_size.cx = w;
3399 surface->ds_current_size.cy = h;
3400 surface->locations = location;
3403 /* Context activation is done by the caller. */
3404 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3406 const struct wined3d_gl_info *gl_info = context->gl_info;
3407 struct wined3d_device *device = surface->resource.device;
3408 GLsizei w, h;
3410 TRACE("surface %p, context %p, new location %#x.\n", surface, context, location);
3412 /* TODO: Make this work for modes other than FBO */
3413 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3415 if (!(surface->locations & location))
3417 w = surface->ds_current_size.cx;
3418 h = surface->ds_current_size.cy;
3419 surface->ds_current_size.cx = 0;
3420 surface->ds_current_size.cy = 0;
3422 else
3424 w = surface->resource.width;
3425 h = surface->resource.height;
3428 if (surface->ds_current_size.cx == surface->resource.width
3429 && surface->ds_current_size.cy == surface->resource.height)
3431 TRACE("Location (%#x) is already up to date.\n", location);
3432 return;
3435 if (surface->current_renderbuffer)
3437 FIXME("Not supported with fixed up depth stencil.\n");
3438 return;
3441 wined3d_surface_prepare(surface, context, location);
3442 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3444 TRACE("Surface was discarded, no need copy data.\n");
3445 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
3446 surface->locations |= location;
3447 surface->ds_current_size.cx = surface->resource.width;
3448 surface->ds_current_size.cy = surface->resource.height;
3449 return;
3452 if (!surface->locations)
3454 FIXME("No up to date depth stencil location.\n");
3455 surface->locations |= location;
3456 surface->ds_current_size.cx = surface->resource.width;
3457 surface->ds_current_size.cy = surface->resource.height;
3458 return;
3461 if (location == WINED3D_LOCATION_TEXTURE_RGB)
3463 GLint old_binding = 0;
3464 GLenum bind_target;
3466 /* The render target is allowed to be smaller than the depth/stencil
3467 * buffer, so the onscreen depth/stencil buffer is potentially smaller
3468 * than the offscreen surface. Don't overwrite the offscreen surface
3469 * with undefined data. */
3470 w = min(w, context->swapchain->desc.backbuffer_width);
3471 h = min(h, context->swapchain->desc.backbuffer_height);
3473 TRACE("Copying onscreen depth buffer to depth texture.\n");
3475 if (!device->depth_blt_texture)
3476 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
3478 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3479 * directly on the FBO texture. That's because we need to flip. */
3480 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3481 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3482 NULL, WINED3D_LOCATION_DRAWABLE);
3483 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
3485 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
3486 bind_target = GL_TEXTURE_RECTANGLE_ARB;
3488 else
3490 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3491 bind_target = GL_TEXTURE_2D;
3493 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
3494 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
3495 * internal format, because the internal format might include stencil
3496 * data. In principle we should copy stencil data as well, but unless
3497 * the driver supports stencil export it's hard to do, and doesn't
3498 * seem to be needed in practice. If the hardware doesn't support
3499 * writing stencil data, the glCopyTexImage2D() call might trigger
3500 * software fallbacks. */
3501 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
3502 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3503 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3504 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3505 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3506 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
3507 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
3509 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3510 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
3511 context_set_draw_buffer(context, GL_NONE);
3513 /* Do the actual blit */
3514 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
3515 checkGLcall("depth_blt");
3517 context_invalidate_state(context, STATE_FRAMEBUFFER);
3519 if (wined3d_settings.strict_draw_ordering)
3520 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3522 else if (location == WINED3D_LOCATION_DRAWABLE)
3524 TRACE("Copying depth texture to onscreen depth buffer.\n");
3526 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3527 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3528 NULL, WINED3D_LOCATION_DRAWABLE);
3529 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
3530 0, surface->pow2Height - h, w, h, surface->texture_target);
3531 checkGLcall("depth_blt");
3533 context_invalidate_state(context, STATE_FRAMEBUFFER);
3535 if (wined3d_settings.strict_draw_ordering)
3536 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3538 else
3540 ERR("Invalid location (%#x) specified.\n", location);
3543 surface->locations |= location;
3544 surface->ds_current_size.cx = surface->resource.width;
3545 surface->ds_current_size.cy = surface->resource.height;
3548 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
3550 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3552 surface->locations |= location;
3555 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
3557 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3559 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3560 wined3d_texture_set_dirty(surface->container);
3561 surface->locations &= ~location;
3563 if (!surface->locations)
3564 ERR("Surface %p does not have any up to date location.\n", surface);
3567 static DWORD resource_access_from_location(DWORD location)
3569 switch (location)
3571 case WINED3D_LOCATION_SYSMEM:
3572 case WINED3D_LOCATION_USER_MEMORY:
3573 case WINED3D_LOCATION_DIB:
3574 case WINED3D_LOCATION_BUFFER:
3575 return WINED3D_RESOURCE_ACCESS_CPU;
3577 case WINED3D_LOCATION_DRAWABLE:
3578 case WINED3D_LOCATION_TEXTURE_SRGB:
3579 case WINED3D_LOCATION_TEXTURE_RGB:
3580 case WINED3D_LOCATION_RB_MULTISAMPLE:
3581 case WINED3D_LOCATION_RB_RESOLVED:
3582 return WINED3D_RESOURCE_ACCESS_GPU;
3584 default:
3585 FIXME("Unhandled location %#x.\n", location);
3586 return 0;
3590 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
3592 struct wined3d_device *device = surface->resource.device;
3593 struct wined3d_context *context;
3594 const struct wined3d_gl_info *gl_info;
3595 struct wined3d_bo_address dst, src;
3596 UINT size = surface->resource.size;
3598 surface_get_memory(surface, &dst, location);
3599 surface_get_memory(surface, &src, surface->locations);
3601 if (dst.buffer_object)
3603 context = context_acquire(device, NULL);
3604 gl_info = context->gl_info;
3605 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
3606 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr));
3607 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
3608 checkGLcall("Upload PBO");
3609 context_release(context);
3610 return;
3612 if (src.buffer_object)
3614 context = context_acquire(device, NULL);
3615 gl_info = context->gl_info;
3616 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
3617 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr));
3618 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
3619 checkGLcall("Download PBO");
3620 context_release(context);
3621 return;
3623 memcpy(dst.addr, src.addr, size);
3626 /* Context activation is done by the caller. */
3627 static void surface_load_sysmem(struct wined3d_surface *surface,
3628 struct wined3d_context *context, DWORD dst_location)
3630 const struct wined3d_gl_info *gl_info = context->gl_info;
3632 if (surface->locations & surface_simple_locations)
3634 surface_copy_simple_location(surface, dst_location);
3635 return;
3638 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
3639 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3641 /* Download the surface to system memory. */
3642 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3644 wined3d_texture_bind_and_dirtify(surface->container, context,
3645 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
3646 surface_download_data(surface, gl_info, dst_location);
3648 return;
3651 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
3653 read_from_framebuffer(surface, context, dst_location);
3654 return;
3657 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
3658 surface, wined3d_debug_location(surface->locations));
3661 /* Context activation is done by the caller. */
3662 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
3663 struct wined3d_context *context)
3665 RECT r;
3667 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3668 && wined3d_resource_is_offscreen(&surface->container->resource))
3670 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
3671 return WINED3DERR_INVALIDCALL;
3674 surface_get_rect(surface, NULL, &r);
3675 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3676 surface_blt_to_drawable(surface->resource.device, context,
3677 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
3679 return WINED3D_OK;
3682 static HRESULT surface_load_texture(struct wined3d_surface *surface,
3683 struct wined3d_context *context, BOOL srgb)
3685 const struct wined3d_gl_info *gl_info = context->gl_info;
3686 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
3687 unsigned int width, src_pitch, dst_row_pitch, dst_slice_pitch;
3688 struct wined3d_device *device = surface->resource.device;
3689 const struct wined3d_color_key_conversion *conversion;
3690 struct wined3d_texture *texture = surface->container;
3691 struct wined3d_bo_address data;
3692 struct wined3d_format format;
3693 POINT dst_point = {0, 0};
3694 BYTE *mem = NULL;
3696 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
3697 && wined3d_resource_is_offscreen(&texture->resource)
3698 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
3700 surface_load_fb_texture(surface, srgb, context);
3702 return WINED3D_OK;
3705 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
3706 && (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
3707 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3708 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3709 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3711 if (srgb)
3712 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
3713 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
3714 else
3715 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
3716 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
3718 return WINED3D_OK;
3721 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
3722 && (!srgb || (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
3723 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3724 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3725 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3727 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
3728 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
3729 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
3730 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3732 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
3733 &rect, surface, dst_location, &rect);
3735 return WINED3D_OK;
3738 /* Upload from system memory */
3740 if (srgb)
3742 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
3743 == WINED3D_LOCATION_TEXTURE_RGB)
3745 /* Performance warning... */
3746 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
3747 surface_prepare_map_memory(surface);
3748 surface_load_location(surface, context, surface->resource.map_binding);
3751 else
3753 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
3754 == WINED3D_LOCATION_TEXTURE_SRGB)
3756 /* Performance warning... */
3757 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
3758 surface_prepare_map_memory(surface);
3759 surface_load_location(surface, context, surface->resource.map_binding);
3763 if (!(surface->locations & surface_simple_locations))
3765 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
3766 /* Lets hope we get it from somewhere... */
3767 surface_prepare_system_memory(surface);
3768 surface_load_location(surface, context, WINED3D_LOCATION_SYSMEM);
3771 wined3d_texture_prepare_texture(texture, context, srgb);
3772 wined3d_texture_bind_and_dirtify(texture, context, srgb);
3774 width = surface->resource.width;
3775 src_pitch = wined3d_surface_get_pitch(surface);
3777 format = *texture->resource.format;
3778 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
3779 format = *wined3d_get_format(gl_info, conversion->dst_format);
3781 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
3782 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
3783 * getting called. */
3784 if ((format.convert || conversion) && surface->pbo)
3786 TRACE("Removing the pbo attached to surface %p.\n", surface);
3788 if (surface->flags & SFLAG_DIBSECTION)
3789 surface->resource.map_binding = WINED3D_LOCATION_DIB;
3790 else
3791 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
3793 surface_prepare_map_memory(surface);
3794 surface_load_location(surface, context, surface->resource.map_binding);
3795 surface_remove_pbo(surface, gl_info);
3798 surface_get_memory(surface, &data, surface->locations);
3799 if (format.convert)
3801 /* This code is entered for texture formats which need a fixup. */
3802 UINT height = surface->resource.height;
3804 format.byte_count = format.conv_byte_count;
3805 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
3807 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
3809 ERR("Out of memory (%u).\n", dst_slice_pitch);
3810 context_release(context);
3811 return E_OUTOFMEMORY;
3813 format.convert(data.addr, mem, src_pitch, src_pitch * height,
3814 dst_row_pitch, dst_slice_pitch, width, height, 1);
3815 src_pitch = dst_row_pitch;
3816 data.addr = mem;
3818 else if (conversion)
3820 /* This code is only entered for color keying fixups */
3821 struct wined3d_palette *palette = NULL;
3822 UINT height = surface->resource.height;
3824 wined3d_format_calculate_pitch(&format, device->surface_alignment,
3825 width, height, &dst_row_pitch, &dst_slice_pitch);
3827 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
3829 ERR("Out of memory (%u).\n", dst_slice_pitch);
3830 context_release(context);
3831 return E_OUTOFMEMORY;
3833 if (texture->swapchain && texture->swapchain->palette)
3834 palette = texture->swapchain->palette;
3835 conversion->convert(data.addr, src_pitch, mem, dst_row_pitch,
3836 width, height, palette, &texture->async.gl_color_key);
3837 src_pitch = dst_row_pitch;
3838 data.addr = mem;
3841 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
3842 src_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
3844 HeapFree(GetProcessHeap(), 0, mem);
3846 return WINED3D_OK;
3849 /* Context activation is done by the caller. */
3850 static void surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
3851 DWORD dst_location)
3853 const RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3854 DWORD src_location;
3856 if (surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE)
3857 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
3858 else if (surface->locations & WINED3D_LOCATION_RB_RESOLVED)
3859 src_location = WINED3D_LOCATION_RB_RESOLVED;
3860 else if (surface->locations & WINED3D_LOCATION_TEXTURE_SRGB)
3861 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
3862 else /* surface_blt_fbo will load the source location if necessary. */
3863 src_location = WINED3D_LOCATION_TEXTURE_RGB;
3865 surface_blt_fbo(surface->resource.device, context, WINED3D_TEXF_POINT,
3866 surface, src_location, &rect, surface, dst_location, &rect);
3869 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
3870 HRESULT surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3872 HRESULT hr;
3874 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3876 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3878 if (location == WINED3D_LOCATION_TEXTURE_RGB
3879 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
3881 surface_load_ds_location(surface, context, location);
3882 return WINED3D_OK;
3884 else if (location & surface->locations
3885 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
3887 /* Already up to date, nothing to do. */
3888 return WINED3D_OK;
3890 else
3892 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
3893 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
3894 return WINED3DERR_INVALIDCALL;
3898 if (surface->locations & location)
3900 TRACE("Location already up to date.\n");
3901 return WINED3D_OK;
3904 if (WARN_ON(d3d_surface))
3906 DWORD required_access = resource_access_from_location(location);
3907 if ((surface->resource.access_flags & required_access) != required_access)
3908 WARN("Operation requires %#x access, but surface only has %#x.\n",
3909 required_access, surface->resource.access_flags);
3912 if (!surface->locations)
3914 ERR("Surface %p does not have any up to date location.\n", surface);
3915 return WINED3DERR_INVALIDCALL;
3918 switch (location)
3920 case WINED3D_LOCATION_DIB:
3921 case WINED3D_LOCATION_USER_MEMORY:
3922 case WINED3D_LOCATION_SYSMEM:
3923 case WINED3D_LOCATION_BUFFER:
3924 surface_load_sysmem(surface, context, location);
3925 break;
3927 case WINED3D_LOCATION_DRAWABLE:
3928 if (FAILED(hr = surface_load_drawable(surface, context)))
3929 return hr;
3930 break;
3932 case WINED3D_LOCATION_RB_RESOLVED:
3933 case WINED3D_LOCATION_RB_MULTISAMPLE:
3934 surface_load_renderbuffer(surface, context, location);
3935 break;
3937 case WINED3D_LOCATION_TEXTURE_RGB:
3938 case WINED3D_LOCATION_TEXTURE_SRGB:
3939 if (FAILED(hr = surface_load_texture(surface, context,
3940 location == WINED3D_LOCATION_TEXTURE_SRGB)))
3941 return hr;
3942 break;
3944 default:
3945 ERR("Don't know how to handle location %#x.\n", location);
3946 break;
3949 surface_validate_location(surface, location);
3951 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
3952 surface_evict_sysmem(surface);
3954 return WINED3D_OK;
3957 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
3958 /* Context activation is done by the caller. */
3959 static void ffp_blit_free(struct wined3d_device *device) { }
3961 /* Context activation is done by the caller. */
3962 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
3963 const struct wined3d_color_key *color_key)
3965 const struct wined3d_gl_info *gl_info = context->gl_info;
3967 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
3968 checkGLcall("glEnable(target)");
3970 return WINED3D_OK;
3973 /* Context activation is done by the caller. */
3974 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
3976 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
3977 checkGLcall("glDisable(GL_TEXTURE_2D)");
3978 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
3980 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3981 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3983 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
3985 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
3986 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
3990 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
3991 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
3992 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
3993 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
3995 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
3997 TRACE("Source or destination is in system memory.\n");
3998 return FALSE;
4001 switch (blit_op)
4003 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
4004 if (d3d_info->shader_color_key)
4006 TRACE("Color keying requires converted textures.\n");
4007 return FALSE;
4009 case WINED3D_BLIT_OP_COLOR_BLIT:
4010 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
4011 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4013 TRACE("Checking support for fixup:\n");
4014 dump_color_fixup_desc(src_format->color_fixup);
4017 /* We only support identity conversions. */
4018 if (!is_identity_fixup(src_format->color_fixup)
4019 || !is_identity_fixup(dst_format->color_fixup))
4021 TRACE("Fixups are not supported.\n");
4022 return FALSE;
4025 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4027 TRACE("Can only blit to render targets.\n");
4028 return FALSE;
4030 return TRUE;
4032 case WINED3D_BLIT_OP_COLOR_FILL:
4033 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4035 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
4036 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4037 return FALSE;
4039 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4041 TRACE("Color fill not supported\n");
4042 return FALSE;
4045 /* FIXME: We should reject color fills on formats with fixups,
4046 * but this would break P8 color fills for example. */
4048 return TRUE;
4050 case WINED3D_BLIT_OP_DEPTH_FILL:
4051 return TRUE;
4053 default:
4054 TRACE("Unsupported blit_op=%d\n", blit_op);
4055 return FALSE;
4059 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
4060 const RECT *rect, const struct wined3d_color *color)
4062 const RECT draw_rect = {0, 0, view->width, view->height};
4063 struct wined3d_fb_state fb = {&view, NULL};
4065 device_clear_render_targets(device, 1, &fb, 1, rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4067 return WINED3D_OK;
4070 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
4071 struct wined3d_rendertarget_view *view, const RECT *rect, float depth)
4073 const RECT draw_rect = {0, 0, view->width, view->height};
4074 struct wined3d_fb_state fb = {NULL, view};
4076 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4078 return WINED3D_OK;
4081 static void ffp_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
4082 struct wined3d_surface *src_surface, const RECT *src_rect,
4083 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4084 const struct wined3d_color_key *color_key)
4086 struct wined3d_context *context;
4088 /* Blit from offscreen surface to render target */
4089 struct wined3d_color_key old_blt_key = src_surface->container->async.src_blt_color_key;
4090 DWORD old_color_key_flags = src_surface->container->async.color_key_flags;
4092 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4094 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT, color_key);
4096 context = context_acquire(device, dst_surface);
4098 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
4099 glEnable(GL_ALPHA_TEST);
4101 surface_blt_to_drawable(device, context, filter,
4102 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
4104 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
4105 glDisable(GL_ALPHA_TEST);
4107 context_release(context);
4109 /* Restore the color key parameters */
4110 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT,
4111 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
4113 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
4114 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
4117 const struct blit_shader ffp_blit = {
4118 ffp_blit_alloc,
4119 ffp_blit_free,
4120 ffp_blit_set,
4121 ffp_blit_unset,
4122 ffp_blit_supported,
4123 ffp_blit_color_fill,
4124 ffp_blit_depth_fill,
4125 ffp_blit_blit_surface,
4128 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4130 return WINED3D_OK;
4133 /* Context activation is done by the caller. */
4134 static void cpu_blit_free(struct wined3d_device *device)
4138 /* Context activation is done by the caller. */
4139 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4140 const struct wined3d_color_key *color_key)
4142 return WINED3D_OK;
4145 /* Context activation is done by the caller. */
4146 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4150 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
4151 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4152 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4153 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4155 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4157 return TRUE;
4160 return FALSE;
4163 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4164 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4165 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4167 UINT row_block_count;
4168 const BYTE *src_row;
4169 BYTE *dst_row;
4170 UINT x, y;
4172 src_row = src_data;
4173 dst_row = dst_data;
4175 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4177 if (!flags)
4179 for (y = 0; y < update_h; y += format->block_height)
4181 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4182 src_row += src_pitch;
4183 dst_row += dst_pitch;
4186 return WINED3D_OK;
4189 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4191 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4193 switch (format->id)
4195 case WINED3DFMT_DXT1:
4196 for (y = 0; y < update_h; y += format->block_height)
4198 struct block
4200 WORD color[2];
4201 BYTE control_row[4];
4204 const struct block *s = (const struct block *)src_row;
4205 struct block *d = (struct block *)dst_row;
4207 for (x = 0; x < row_block_count; ++x)
4209 d[x].color[0] = s[x].color[0];
4210 d[x].color[1] = s[x].color[1];
4211 d[x].control_row[0] = s[x].control_row[3];
4212 d[x].control_row[1] = s[x].control_row[2];
4213 d[x].control_row[2] = s[x].control_row[1];
4214 d[x].control_row[3] = s[x].control_row[0];
4216 src_row -= src_pitch;
4217 dst_row += dst_pitch;
4219 return WINED3D_OK;
4221 case WINED3DFMT_DXT2:
4222 case WINED3DFMT_DXT3:
4223 for (y = 0; y < update_h; y += format->block_height)
4225 struct block
4227 WORD alpha_row[4];
4228 WORD color[2];
4229 BYTE control_row[4];
4232 const struct block *s = (const struct block *)src_row;
4233 struct block *d = (struct block *)dst_row;
4235 for (x = 0; x < row_block_count; ++x)
4237 d[x].alpha_row[0] = s[x].alpha_row[3];
4238 d[x].alpha_row[1] = s[x].alpha_row[2];
4239 d[x].alpha_row[2] = s[x].alpha_row[1];
4240 d[x].alpha_row[3] = s[x].alpha_row[0];
4241 d[x].color[0] = s[x].color[0];
4242 d[x].color[1] = s[x].color[1];
4243 d[x].control_row[0] = s[x].control_row[3];
4244 d[x].control_row[1] = s[x].control_row[2];
4245 d[x].control_row[2] = s[x].control_row[1];
4246 d[x].control_row[3] = s[x].control_row[0];
4248 src_row -= src_pitch;
4249 dst_row += dst_pitch;
4251 return WINED3D_OK;
4253 default:
4254 FIXME("Compressed flip not implemented for format %s.\n",
4255 debug_d3dformat(format->id));
4256 return E_NOTIMPL;
4260 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
4261 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
4263 return E_NOTIMPL;
4266 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4267 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4268 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4270 const struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
4271 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
4272 const struct wined3d_format *src_format, *dst_format;
4273 unsigned int src_fmt_flags, dst_fmt_flags;
4274 struct wined3d_texture *src_texture = NULL;
4275 struct wined3d_map_desc dst_map, src_map;
4276 const BYTE *sbase = NULL;
4277 HRESULT hr = WINED3D_OK;
4278 const BYTE *sbuf;
4279 BYTE *dbuf;
4280 int x, y;
4282 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4283 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4284 flags, fx, debug_d3dtexturefiltertype(filter));
4286 if (src_surface == dst_surface)
4288 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
4289 src_map = dst_map;
4290 src_format = dst_surface->resource.format;
4291 dst_format = src_format;
4292 dst_fmt_flags = dst_surface->container->resource.format_flags;
4293 src_fmt_flags = dst_fmt_flags;
4295 else
4297 dst_format = dst_surface->resource.format;
4298 dst_fmt_flags = dst_surface->container->resource.format_flags;
4299 if (src_surface)
4301 if (dst_surface->resource.format->id != src_surface->resource.format->id)
4303 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
4305 /* The conv function writes a FIXME */
4306 WARN("Cannot convert source surface format to dest format.\n");
4307 goto release;
4309 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
4311 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
4312 src_format = src_surface->resource.format;
4313 src_fmt_flags = src_surface->container->resource.format_flags;
4315 else
4317 src_format = dst_format;
4318 src_fmt_flags = dst_fmt_flags;
4321 wined3d_surface_map(dst_surface, &dst_map, &dst_box, 0);
4324 bpp = dst_surface->resource.format->byte_count;
4325 srcheight = src_rect->bottom - src_rect->top;
4326 srcwidth = src_rect->right - src_rect->left;
4327 dstheight = dst_rect->bottom - dst_rect->top;
4328 dstwidth = dst_rect->right - dst_rect->left;
4329 width = (dst_rect->right - dst_rect->left) * bpp;
4331 if (src_surface)
4332 sbase = (BYTE *)src_map.data
4333 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
4334 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
4335 if (src_surface != dst_surface)
4336 dbuf = dst_map.data;
4337 else
4338 dbuf = (BYTE *)dst_map.data
4339 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
4340 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
4342 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
4344 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
4346 if (src_surface == dst_surface)
4348 FIXME("Only plain blits supported on compressed surfaces.\n");
4349 hr = E_NOTIMPL;
4350 goto release;
4353 if (srcheight != dstheight || srcwidth != dstwidth)
4355 WARN("Stretching not supported on compressed surfaces.\n");
4356 hr = WINED3DERR_INVALIDCALL;
4357 goto release;
4360 if (!surface_check_block_align_rect(src_surface, src_rect))
4362 WARN("Source rectangle not block-aligned.\n");
4363 hr = WINED3DERR_INVALIDCALL;
4364 goto release;
4367 if (!surface_check_block_align_rect(dst_surface, dst_rect))
4369 WARN("Destination rectangle not block-aligned.\n");
4370 hr = WINED3DERR_INVALIDCALL;
4371 goto release;
4374 hr = surface_cpu_blt_compressed(sbase, dbuf,
4375 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
4376 src_format, flags, fx);
4377 goto release;
4380 /* First, all the 'source-less' blits */
4381 if (flags & WINEDDBLT_COLORFILL)
4383 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
4384 flags &= ~WINEDDBLT_COLORFILL;
4387 if (flags & WINEDDBLT_DEPTHFILL)
4389 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
4391 if (flags & WINEDDBLT_DDROPS)
4393 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
4395 /* Now the 'with source' blits. */
4396 if (src_surface)
4398 int sx, xinc, sy, yinc;
4400 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
4401 goto release;
4403 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
4404 && (srcwidth != dstwidth || srcheight != dstheight))
4406 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
4407 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
4410 xinc = (srcwidth << 16) / dstwidth;
4411 yinc = (srcheight << 16) / dstheight;
4413 if (!flags)
4415 /* No effects, we can cheat here. */
4416 if (dstwidth == srcwidth)
4418 if (dstheight == srcheight)
4420 /* No stretching in either direction. This needs to be as
4421 * fast as possible. */
4422 sbuf = sbase;
4424 /* Check for overlapping surfaces. */
4425 if (src_surface != dst_surface || dst_rect->top < src_rect->top
4426 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
4428 /* No overlap, or dst above src, so copy from top downwards. */
4429 for (y = 0; y < dstheight; ++y)
4431 memcpy(dbuf, sbuf, width);
4432 sbuf += src_map.row_pitch;
4433 dbuf += dst_map.row_pitch;
4436 else if (dst_rect->top > src_rect->top)
4438 /* Copy from bottom upwards. */
4439 sbuf += src_map.row_pitch * dstheight;
4440 dbuf += dst_map.row_pitch * dstheight;
4441 for (y = 0; y < dstheight; ++y)
4443 sbuf -= src_map.row_pitch;
4444 dbuf -= dst_map.row_pitch;
4445 memcpy(dbuf, sbuf, width);
4448 else
4450 /* Src and dst overlapping on the same line, use memmove. */
4451 for (y = 0; y < dstheight; ++y)
4453 memmove(dbuf, sbuf, width);
4454 sbuf += src_map.row_pitch;
4455 dbuf += dst_map.row_pitch;
4459 else
4461 /* Stretching in y direction only. */
4462 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4464 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4465 memcpy(dbuf, sbuf, width);
4466 dbuf += dst_map.row_pitch;
4470 else
4472 /* Stretching in X direction. */
4473 int last_sy = -1;
4474 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4476 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4478 if ((sy >> 16) == (last_sy >> 16))
4480 /* This source row is the same as last source row -
4481 * Copy the already stretched row. */
4482 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
4484 else
4486 #define STRETCH_ROW(type) \
4487 do { \
4488 const type *s = (const type *)sbuf; \
4489 type *d = (type *)dbuf; \
4490 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4491 d[x] = s[sx >> 16]; \
4492 } while(0)
4494 switch(bpp)
4496 case 1:
4497 STRETCH_ROW(BYTE);
4498 break;
4499 case 2:
4500 STRETCH_ROW(WORD);
4501 break;
4502 case 4:
4503 STRETCH_ROW(DWORD);
4504 break;
4505 case 3:
4507 const BYTE *s;
4508 BYTE *d = dbuf;
4509 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
4511 DWORD pixel;
4513 s = sbuf + 3 * (sx >> 16);
4514 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4515 d[0] = (pixel ) & 0xff;
4516 d[1] = (pixel >> 8) & 0xff;
4517 d[2] = (pixel >> 16) & 0xff;
4518 d += 3;
4520 break;
4522 default:
4523 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
4524 hr = WINED3DERR_NOTAVAILABLE;
4525 goto error;
4527 #undef STRETCH_ROW
4529 dbuf += dst_map.row_pitch;
4530 last_sy = sy;
4534 else
4536 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
4537 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
4538 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
4539 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
4541 /* The color keying flags are checked for correctness in ddraw */
4542 if (flags & WINEDDBLT_KEYSRC)
4544 keylow = src_surface->container->async.src_blt_color_key.color_space_low_value;
4545 keyhigh = src_surface->container->async.src_blt_color_key.color_space_high_value;
4547 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4549 keylow = fx->ddckSrcColorkey.color_space_low_value;
4550 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
4553 if (flags & WINEDDBLT_KEYDEST)
4555 /* Destination color keys are taken from the source surface! */
4556 destkeylow = src_surface->container->async.dst_blt_color_key.color_space_low_value;
4557 destkeyhigh = src_surface->container->async.dst_blt_color_key.color_space_high_value;
4559 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
4561 destkeylow = fx->ddckDestColorkey.color_space_low_value;
4562 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
4565 if (bpp == 1)
4567 keymask = 0xff;
4569 else
4571 DWORD masks[3];
4572 get_color_masks(src_format, masks);
4573 keymask = masks[0]
4574 | masks[1]
4575 | masks[2];
4577 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
4580 if (flags & WINEDDBLT_DDFX)
4582 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
4583 LONG tmpxy;
4584 dTopLeft = dbuf;
4585 dTopRight = dbuf + ((dstwidth - 1) * bpp);
4586 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
4587 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
4589 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
4591 /* I don't think we need to do anything about this flag */
4592 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
4594 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
4596 tmp = dTopRight;
4597 dTopRight = dTopLeft;
4598 dTopLeft = tmp;
4599 tmp = dBottomRight;
4600 dBottomRight = dBottomLeft;
4601 dBottomLeft = tmp;
4602 dstxinc = dstxinc * -1;
4604 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
4606 tmp = dTopLeft;
4607 dTopLeft = dBottomLeft;
4608 dBottomLeft = tmp;
4609 tmp = dTopRight;
4610 dTopRight = dBottomRight;
4611 dBottomRight = tmp;
4612 dstyinc = dstyinc * -1;
4614 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
4616 /* I don't think we need to do anything about this flag */
4617 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
4619 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
4621 tmp = dBottomRight;
4622 dBottomRight = dTopLeft;
4623 dTopLeft = tmp;
4624 tmp = dBottomLeft;
4625 dBottomLeft = dTopRight;
4626 dTopRight = tmp;
4627 dstxinc = dstxinc * -1;
4628 dstyinc = dstyinc * -1;
4630 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
4632 tmp = dTopLeft;
4633 dTopLeft = dBottomLeft;
4634 dBottomLeft = dBottomRight;
4635 dBottomRight = dTopRight;
4636 dTopRight = tmp;
4637 tmpxy = dstxinc;
4638 dstxinc = dstyinc;
4639 dstyinc = tmpxy;
4640 dstxinc = dstxinc * -1;
4642 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
4644 tmp = dTopLeft;
4645 dTopLeft = dTopRight;
4646 dTopRight = dBottomRight;
4647 dBottomRight = dBottomLeft;
4648 dBottomLeft = tmp;
4649 tmpxy = dstxinc;
4650 dstxinc = dstyinc;
4651 dstyinc = tmpxy;
4652 dstyinc = dstyinc * -1;
4654 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
4656 /* I don't think we need to do anything about this flag */
4657 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
4659 dbuf = dTopLeft;
4660 flags &= ~(WINEDDBLT_DDFX);
4663 #define COPY_COLORKEY_FX(type) \
4664 do { \
4665 const type *s; \
4666 type *d = (type *)dbuf, *dx, tmp; \
4667 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
4669 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
4670 dx = d; \
4671 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4673 tmp = s[sx >> 16]; \
4674 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
4675 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
4677 dx[0] = tmp; \
4679 dx = (type *)(((BYTE *)dx) + dstxinc); \
4681 d = (type *)(((BYTE *)d) + dstyinc); \
4683 } while(0)
4685 switch (bpp)
4687 case 1:
4688 COPY_COLORKEY_FX(BYTE);
4689 break;
4690 case 2:
4691 COPY_COLORKEY_FX(WORD);
4692 break;
4693 case 4:
4694 COPY_COLORKEY_FX(DWORD);
4695 break;
4696 case 3:
4698 const BYTE *s;
4699 BYTE *d = dbuf, *dx;
4700 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4702 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4703 dx = d;
4704 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
4706 DWORD pixel, dpixel = 0;
4707 s = sbuf + 3 * (sx>>16);
4708 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4709 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
4710 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
4711 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
4713 dx[0] = (pixel ) & 0xff;
4714 dx[1] = (pixel >> 8) & 0xff;
4715 dx[2] = (pixel >> 16) & 0xff;
4717 dx += dstxinc;
4719 d += dstyinc;
4721 break;
4723 default:
4724 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
4725 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
4726 hr = WINED3DERR_NOTAVAILABLE;
4727 goto error;
4728 #undef COPY_COLORKEY_FX
4733 error:
4734 if (flags && FIXME_ON(d3d_surface))
4736 FIXME("\tUnsupported flags: %#x.\n", flags);
4739 release:
4740 wined3d_surface_unmap(dst_surface);
4741 if (src_surface && src_surface != dst_surface)
4742 wined3d_surface_unmap(src_surface);
4743 /* Release the converted surface, if any. */
4744 if (src_texture)
4745 wined3d_texture_decref(src_texture);
4747 return hr;
4750 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
4751 const RECT *rect, const struct wined3d_color *color)
4753 struct wined3d_surface *surface = wined3d_rendertarget_view_get_surface(view);
4754 static const RECT src_rect;
4755 WINEDDBLTFX BltFx;
4757 memset(&BltFx, 0, sizeof(BltFx));
4758 BltFx.dwSize = sizeof(BltFx);
4759 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(surface, color);
4760 return surface_cpu_blt(surface, rect, NULL, &src_rect,
4761 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
4764 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
4765 struct wined3d_rendertarget_view *view, const RECT *rect, float depth)
4767 FIXME("Depth filling not implemented by cpu_blit.\n");
4768 return WINED3DERR_INVALIDCALL;
4771 static void cpu_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
4772 struct wined3d_surface *src_surface, const RECT *src_rect,
4773 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4774 const struct wined3d_color_key *color_key)
4776 /* FIXME: Remove error returns from surface_blt_cpu. */
4777 ERR("Blit method not implemented by cpu_blit.\n");
4780 const struct blit_shader cpu_blit = {
4781 cpu_blit_alloc,
4782 cpu_blit_free,
4783 cpu_blit_set,
4784 cpu_blit_unset,
4785 cpu_blit_supported,
4786 cpu_blit_color_fill,
4787 cpu_blit_depth_fill,
4788 cpu_blit_blit_surface,
4791 HRESULT wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4792 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4793 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4795 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
4796 struct wined3d_device *device = dst_surface->resource.device;
4797 DWORD src_ds_flags, dst_ds_flags;
4798 BOOL scale, convert;
4800 static const DWORD simple_blit = WINEDDBLT_ASYNC
4801 | WINEDDBLT_COLORFILL
4802 | WINEDDBLT_KEYSRC
4803 | WINEDDBLT_KEYSRCOVERRIDE
4804 | WINEDDBLT_WAIT
4805 | WINEDDBLT_DEPTHFILL
4806 | WINEDDBLT_DONOTWAIT
4807 | WINEDDBLT_ALPHATEST;
4809 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4810 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4811 flags, fx, debug_d3dtexturefiltertype(filter));
4812 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
4814 if (fx)
4816 TRACE("dwSize %#x.\n", fx->dwSize);
4817 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
4818 TRACE("dwROP %#x.\n", fx->dwROP);
4819 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
4820 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
4821 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
4822 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
4823 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
4824 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
4825 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
4826 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
4827 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
4828 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
4829 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
4830 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
4831 TRACE("dwReserved %#x.\n", fx->dwReserved);
4832 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
4833 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
4834 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
4835 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
4836 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
4837 TRACE("ddckDestColorkey {%#x, %#x}.\n",
4838 fx->ddckDestColorkey.color_space_low_value,
4839 fx->ddckDestColorkey.color_space_high_value);
4840 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
4841 fx->ddckSrcColorkey.color_space_low_value,
4842 fx->ddckSrcColorkey.color_space_high_value);
4845 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
4847 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
4848 return WINEDDERR_SURFACEBUSY;
4851 if (dst_rect->left >= dst_rect->right || dst_rect->top >= dst_rect->bottom
4852 || dst_rect->left > dst_surface->resource.width || dst_rect->left < 0
4853 || dst_rect->top > dst_surface->resource.height || dst_rect->top < 0
4854 || dst_rect->right > dst_surface->resource.width || dst_rect->right < 0
4855 || dst_rect->bottom > dst_surface->resource.height || dst_rect->bottom < 0)
4857 WARN("The application gave us a bad destination rectangle.\n");
4858 return WINEDDERR_INVALIDRECT;
4861 if (src_surface)
4863 if (src_rect->left >= src_rect->right || src_rect->top >= src_rect->bottom
4864 || src_rect->left > src_surface->resource.width || src_rect->left < 0
4865 || src_rect->top > src_surface->resource.height || src_rect->top < 0
4866 || src_rect->right > src_surface->resource.width || src_rect->right < 0
4867 || src_rect->bottom > src_surface->resource.height || src_rect->bottom < 0)
4869 WARN("The application gave us a bad source rectangle.\n");
4870 return WINEDDERR_INVALIDRECT;
4874 if (!fx || !(fx->dwDDFX))
4875 flags &= ~WINEDDBLT_DDFX;
4877 if (flags & WINEDDBLT_WAIT)
4878 flags &= ~WINEDDBLT_WAIT;
4880 if (flags & WINEDDBLT_ASYNC)
4882 static unsigned int once;
4884 if (!once++)
4885 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
4886 flags &= ~WINEDDBLT_ASYNC;
4889 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
4890 if (flags & WINEDDBLT_DONOTWAIT)
4892 static unsigned int once;
4894 if (!once++)
4895 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
4896 flags &= ~WINEDDBLT_DONOTWAIT;
4899 if (!device->d3d_initialized)
4901 WARN("D3D not initialized, using fallback.\n");
4902 goto cpu;
4905 /* We want to avoid invalidating the sysmem location for converted
4906 * surfaces, since otherwise we'd have to convert the data back when
4907 * locking them. */
4908 if (dst_surface->container->flags & WINED3D_TEXTURE_CONVERTED
4909 || dst_surface->container->resource.format->convert
4910 || wined3d_format_get_color_key_conversion(dst_surface->container, TRUE))
4912 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
4913 goto cpu;
4916 if (flags & ~simple_blit)
4918 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
4919 goto fallback;
4922 if (src_surface)
4923 src_swapchain = src_surface->container->swapchain;
4924 else
4925 src_swapchain = NULL;
4927 dst_swapchain = dst_surface->container->swapchain;
4929 /* This isn't strictly needed. FBO blits for example could deal with
4930 * cross-swapchain blits by first downloading the source to a texture
4931 * before switching to the destination context. We just have this here to
4932 * not have to deal with the issue, since cross-swapchain blits should be
4933 * rare. */
4934 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
4936 FIXME("Using fallback for cross-swapchain blit.\n");
4937 goto fallback;
4940 scale = src_surface
4941 && (src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
4942 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top);
4943 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
4945 dst_ds_flags = dst_surface->container->resource.format_flags
4946 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4947 if (src_surface)
4948 src_ds_flags = src_surface->container->resource.format_flags
4949 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4950 else
4951 src_ds_flags = 0;
4953 if (src_ds_flags || dst_ds_flags)
4955 if (flags & WINEDDBLT_DEPTHFILL)
4957 float depth;
4959 TRACE("Depth fill.\n");
4961 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
4962 return WINED3DERR_INVALIDCALL;
4964 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, dst_rect, depth)))
4965 return WINED3D_OK;
4967 else
4969 if (src_ds_flags != dst_ds_flags)
4971 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
4972 return WINED3DERR_INVALIDCALL;
4975 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
4976 src_rect, dst_surface, dst_surface->container->resource.draw_binding, dst_rect)))
4977 return WINED3D_OK;
4980 else
4982 const struct blit_shader *blitter;
4984 /* In principle this would apply to depth blits as well, but we don't
4985 * implement those in the CPU blitter at the moment. */
4986 if ((dst_surface->locations & dst_surface->resource.map_binding)
4987 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
4989 if (scale)
4990 TRACE("Not doing sysmem blit because of scaling.\n");
4991 else if (convert)
4992 TRACE("Not doing sysmem blit because of format conversion.\n");
4993 else
4994 goto cpu;
4997 if (flags & WINEDDBLT_COLORFILL)
4999 struct wined3d_color color;
5000 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
5002 TRACE("Color fill.\n");
5004 if (!wined3d_format_convert_color_to_float(dst_surface->resource.format,
5005 palette, fx->u5.dwFillColor, &color))
5006 goto fallback;
5008 if (SUCCEEDED(surface_color_fill(dst_surface, dst_rect, &color)))
5009 return WINED3D_OK;
5011 else
5013 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
5014 const struct wined3d_color_key *color_key = NULL;
5016 TRACE("Color blit.\n");
5017 if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5019 color_key = &fx->ddckSrcColorkey;
5020 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5022 else if (flags & WINEDDBLT_KEYSRC)
5024 color_key = &src_surface->container->async.src_blt_color_key;
5025 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5027 else if (flags & WINEDDBLT_ALPHATEST)
5029 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
5031 else if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5032 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5034 /* Upload */
5035 if (scale)
5036 TRACE("Not doing upload because of scaling.\n");
5037 else if (convert)
5038 TRACE("Not doing upload because of format conversion.\n");
5039 else
5041 POINT dst_point = {dst_rect->left, dst_rect->top};
5043 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
5045 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
5047 struct wined3d_context *context = context_acquire(device, dst_surface);
5048 surface_load_location(dst_surface, context, dst_surface->container->resource.draw_binding);
5049 context_release(context);
5051 return WINED3D_OK;
5055 else if (dst_swapchain && dst_swapchain->back_buffers
5056 && dst_surface->container == dst_swapchain->front_buffer
5057 && src_surface->container == dst_swapchain->back_buffers[0])
5059 /* Use present for back -> front blits. The idea behind this is
5060 * that present is potentially faster than a blit, in particular
5061 * when FBO blits aren't available. Some ddraw applications like
5062 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5063 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5064 * applications can't blit directly to the frontbuffer. */
5065 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5067 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5069 /* Set the swap effect to COPY, we don't want the backbuffer
5070 * to become undefined. */
5071 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5072 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5073 dst_swapchain->desc.swap_effect = swap_effect;
5075 return WINED3D_OK;
5078 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
5079 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5080 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5082 struct wined3d_context *context;
5083 TRACE("Using FBO blit.\n");
5085 context = context_acquire(device, NULL);
5086 surface_blt_fbo(device, context, filter,
5087 src_surface, src_surface->container->resource.draw_binding, src_rect,
5088 dst_surface, dst_surface->container->resource.draw_binding, dst_rect);
5089 context_release(context);
5091 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5092 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5094 return WINED3D_OK;
5097 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
5098 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5099 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format);
5100 if (blitter)
5102 blitter->blit_surface(device, blit_op, filter, src_surface,
5103 src_rect, dst_surface, dst_rect, color_key);
5104 return WINED3D_OK;
5109 fallback:
5110 /* Special cases for render targets. */
5111 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
5112 return WINED3D_OK;
5114 cpu:
5115 return surface_cpu_blt(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter);
5118 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5119 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5121 struct wined3d_device *device = container->resource.device;
5122 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5123 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5124 BOOL lockable = flags & WINED3D_TEXTURE_CREATE_MAPPABLE;
5125 UINT multisample_quality = desc->multisample_quality;
5126 unsigned int resource_size;
5127 HRESULT hr;
5129 /* Quick lockable sanity check.
5130 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5131 * this function is too deep to need to care about things like this.
5132 * Levels need to be checked too, since they all affect what can be done. */
5133 switch (desc->pool)
5135 case WINED3D_POOL_MANAGED:
5136 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5137 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5138 break;
5140 case WINED3D_POOL_DEFAULT:
5141 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5142 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5143 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5144 break;
5146 case WINED3D_POOL_SCRATCH:
5147 case WINED3D_POOL_SYSTEM_MEM:
5148 break;
5150 default:
5151 FIXME("Unknown pool %#x.\n", desc->pool);
5152 break;
5155 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5156 FIXME("Trying to create a render target that isn't in the default pool.\n");
5158 /* FIXME: Check that the format is supported by the device. */
5160 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5161 if (!resource_size)
5162 return WINED3DERR_INVALIDCALL;
5164 if (device->wined3d->flags & WINED3D_NO3D)
5165 surface->surface_ops = &gdi_surface_ops;
5166 else
5167 surface->surface_ops = &surface_ops;
5169 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE,
5170 format, desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height,
5171 1, resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5173 WARN("Failed to initialize resource, returning %#x.\n", hr);
5174 return hr;
5177 surface->container = container;
5178 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5179 list_init(&surface->renderbuffers);
5180 list_init(&surface->overlays);
5182 /* Flags */
5183 if (flags & WINED3D_TEXTURE_CREATE_DISCARD)
5184 surface->flags |= SFLAG_DISCARD;
5185 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5186 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5188 surface->texture_target = target;
5189 surface->texture_level = level;
5190 surface->texture_layer = layer;
5192 /* Call the private setup routine */
5193 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5195 ERR("Private setup failed, hr %#x.\n", hr);
5196 surface_cleanup(surface);
5197 return hr;
5200 /* Similar to lockable rendertargets above, creating the DIB section
5201 * during surface initialization prevents the sysmem pointer from changing
5202 * after a wined3d_texture_get_dc() call. */
5203 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5204 && SUCCEEDED(surface_create_dib_section(surface)))
5205 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5207 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5209 wined3d_resource_free_sysmem(&surface->resource);
5210 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5211 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5214 return hr;
5217 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5218 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
5220 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
5221 const struct wined3d_parent_ops *parent_ops;
5222 struct wined3d_surface *object;
5223 void *parent;
5224 HRESULT hr;
5226 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
5227 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
5228 container, desc->width, desc->height, debug_d3dformat(desc->format),
5229 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
5230 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
5232 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
5233 return E_OUTOFMEMORY;
5235 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
5237 WARN("Failed to initialize surface, returning %#x.\n", hr);
5238 HeapFree(GetProcessHeap(), 0, object);
5239 return hr;
5242 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
5243 container, layer * container->level_count + level, object, &parent, &parent_ops)))
5245 WARN("Failed to create surface parent, hr %#x.\n", hr);
5246 wined3d_surface_destroy(object);
5247 return hr;
5250 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
5252 object->resource.parent = parent;
5253 object->resource.parent_ops = parent_ops;
5254 *surface = object;
5256 return hr;
5259 /* Context activation is done by the caller. */
5260 void wined3d_surface_prepare(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5262 switch (location)
5264 case WINED3D_LOCATION_TEXTURE_RGB:
5265 wined3d_texture_prepare_texture(surface->container, context, FALSE);
5266 break;
5268 case WINED3D_LOCATION_TEXTURE_SRGB:
5269 wined3d_texture_prepare_texture(surface->container, context, TRUE);
5270 break;
5272 case WINED3D_LOCATION_RB_MULTISAMPLE:
5273 surface_prepare_rb(surface, context->gl_info, TRUE);
5274 break;
5276 case WINED3D_LOCATION_RB_RESOLVED:
5277 surface_prepare_rb(surface, context->gl_info, FALSE);
5278 break;