wined3d: Introduce wined3d_texture_check_block_align().
[wine.git] / dlls / wined3d / surface.c
blob7858d151403b2741b89c5063cb7c07ff6ca9d033
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 BITMAPINFO *b_info;
365 DWORD *masks;
367 TRACE("surface %p.\n", surface);
369 if (!(format_flags & WINED3DFMT_FLAG_GETDC))
371 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
372 return WINED3DERR_INVALIDCALL;
375 switch (format->byte_count)
377 case 2:
378 case 4:
379 /* Allocate extra space to store the RGB bit masks. */
380 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, FIELD_OFFSET(BITMAPINFO, bmiColors[3]));
381 break;
383 case 3:
384 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, FIELD_OFFSET(BITMAPINFO, bmiColors[0]));
385 break;
387 default:
388 /* Allocate extra space for a palette. */
389 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
390 FIELD_OFFSET(BITMAPINFO, bmiColors[1u << (format->byte_count * 8)]));
391 break;
394 if (!b_info)
395 return E_OUTOFMEMORY;
397 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
398 wined3d_texture_get_pitch(surface->container, surface->texture_level, &row_pitch, &slice_pitch);
399 b_info->bmiHeader.biWidth = row_pitch / format->byte_count;
400 b_info->bmiHeader.biHeight = 0 - surface->resource.height;
401 b_info->bmiHeader.biSizeImage = slice_pitch;
402 b_info->bmiHeader.biPlanes = 1;
403 b_info->bmiHeader.biBitCount = format->byte_count * 8;
405 b_info->bmiHeader.biXPelsPerMeter = 0;
406 b_info->bmiHeader.biYPelsPerMeter = 0;
407 b_info->bmiHeader.biClrUsed = 0;
408 b_info->bmiHeader.biClrImportant = 0;
410 /* Get the bit masks */
411 masks = (DWORD *)b_info->bmiColors;
412 switch (surface->resource.format->id)
414 case WINED3DFMT_B8G8R8_UNORM:
415 b_info->bmiHeader.biCompression = BI_RGB;
416 break;
418 case WINED3DFMT_B5G5R5X1_UNORM:
419 case WINED3DFMT_B5G5R5A1_UNORM:
420 case WINED3DFMT_B4G4R4A4_UNORM:
421 case WINED3DFMT_B4G4R4X4_UNORM:
422 case WINED3DFMT_B2G3R3_UNORM:
423 case WINED3DFMT_B2G3R3A8_UNORM:
424 case WINED3DFMT_R10G10B10A2_UNORM:
425 case WINED3DFMT_R8G8B8A8_UNORM:
426 case WINED3DFMT_R8G8B8X8_UNORM:
427 case WINED3DFMT_B10G10R10A2_UNORM:
428 case WINED3DFMT_B5G6R5_UNORM:
429 case WINED3DFMT_R16G16B16A16_UNORM:
430 b_info->bmiHeader.biCompression = BI_BITFIELDS;
431 get_color_masks(format, masks);
432 break;
434 default:
435 /* Don't know palette */
436 b_info->bmiHeader.biCompression = BI_RGB;
437 break;
440 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
441 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
442 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
443 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
445 if (!surface->dib.DIBsection)
447 ERR("Failed to create DIB section.\n");
448 HeapFree(GetProcessHeap(), 0, b_info);
449 return HRESULT_FROM_WIN32(GetLastError());
452 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
453 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
455 HeapFree(GetProcessHeap(), 0, b_info);
457 /* Now allocate a DC. */
458 surface->hDC = CreateCompatibleDC(0);
459 SelectObject(surface->hDC, surface->dib.DIBsection);
461 surface->flags |= SFLAG_DIBSECTION;
463 return WINED3D_OK;
466 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
467 DWORD location)
469 if (location & WINED3D_LOCATION_BUFFER)
471 data->addr = NULL;
472 data->buffer_object = surface->pbo;
473 return;
475 if (location & WINED3D_LOCATION_USER_MEMORY)
477 data->addr = surface->container->user_memory;
478 data->buffer_object = 0;
479 return;
481 if (location & WINED3D_LOCATION_DIB)
483 data->addr = surface->dib.bitmap_data;
484 data->buffer_object = 0;
485 return;
487 if (location & WINED3D_LOCATION_SYSMEM)
489 data->addr = surface->resource.heap_memory;
490 data->buffer_object = 0;
491 return;
494 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
495 data->addr = NULL;
496 data->buffer_object = 0;
499 static void surface_prepare_buffer(struct wined3d_surface *surface)
501 struct wined3d_context *context;
502 GLenum error;
503 const struct wined3d_gl_info *gl_info;
505 if (surface->pbo)
506 return;
508 context = context_acquire(surface->resource.device, NULL);
509 gl_info = context->gl_info;
511 GL_EXTCALL(glGenBuffers(1, &surface->pbo));
512 error = gl_info->gl_ops.gl.p_glGetError();
513 if (!surface->pbo || error != GL_NO_ERROR)
514 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
516 TRACE("Binding PBO %u.\n", surface->pbo);
518 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
519 checkGLcall("glBindBuffer");
521 GL_EXTCALL(glBufferData(GL_PIXEL_UNPACK_BUFFER, surface->resource.size + 4,
522 NULL, GL_STREAM_DRAW));
523 checkGLcall("glBufferData");
525 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
526 checkGLcall("glBindBuffer");
528 context_release(context);
531 static void surface_prepare_system_memory(struct wined3d_surface *surface)
533 TRACE("surface %p.\n", surface);
535 if (surface->resource.heap_memory)
536 return;
538 /* Whatever surface we have, make sure that there is memory allocated
539 * for the downloaded copy, or a PBO to map. */
540 if (!wined3d_resource_allocate_sysmem(&surface->resource))
541 ERR("Failed to allocate system memory.\n");
543 if (surface->locations & WINED3D_LOCATION_SYSMEM)
544 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
547 void surface_prepare_map_memory(struct wined3d_surface *surface)
549 switch (surface->resource.map_binding)
551 case WINED3D_LOCATION_SYSMEM:
552 surface_prepare_system_memory(surface);
553 break;
555 case WINED3D_LOCATION_USER_MEMORY:
556 if (!surface->container->user_memory)
557 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
558 break;
560 case WINED3D_LOCATION_DIB:
561 if (!surface->dib.bitmap_data)
562 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
563 break;
565 case WINED3D_LOCATION_BUFFER:
566 surface_prepare_buffer(surface);
567 break;
569 default:
570 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
574 static void surface_evict_sysmem(struct wined3d_surface *surface)
576 /* In some conditions the surface memory must not be freed:
577 * WINED3D_TEXTURE_CONVERTED: Converting the data back would take too long
578 * WINED3D_TEXTURE_DYNAMIC_MAP: Avoid freeing the data for performance */
579 if (surface->resource.map_count || surface->container->flags & (WINED3D_TEXTURE_CONVERTED
580 | WINED3D_TEXTURE_PIN_SYSMEM | WINED3D_TEXTURE_DYNAMIC_MAP))
581 return;
583 wined3d_resource_free_sysmem(&surface->resource);
584 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
587 static BOOL surface_use_pbo(const struct wined3d_surface *surface)
589 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
590 struct wined3d_texture *texture = surface->container;
592 return texture->resource.pool == WINED3D_POOL_DEFAULT
593 && surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU
594 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
595 && !texture->resource.format->convert
596 && !(texture->flags & WINED3D_TEXTURE_PIN_SYSMEM)
597 && !(surface->flags & SFLAG_NONPOW2);
600 static HRESULT surface_private_setup(struct wined3d_surface *surface)
602 /* TODO: Check against the maximum texture sizes supported by the video card. */
603 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
604 unsigned int pow2Width, pow2Height;
606 TRACE("surface %p.\n", surface);
608 /* Non-power2 support */
609 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
610 || gl_info->supported[ARB_TEXTURE_RECTANGLE])
612 pow2Width = surface->resource.width;
613 pow2Height = surface->resource.height;
615 else
617 /* Find the nearest pow2 match */
618 pow2Width = pow2Height = 1;
619 while (pow2Width < surface->resource.width)
620 pow2Width <<= 1;
621 while (pow2Height < surface->resource.height)
622 pow2Height <<= 1;
624 surface->pow2Width = pow2Width;
625 surface->pow2Height = pow2Height;
627 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
629 /* TODO: Add support for non power two compressed textures. */
630 if (surface->container->resource.format_flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE))
632 FIXME("(%p) Compressed or height scaled non-power-two textures are not supported w(%d) h(%d)\n",
633 surface, surface->resource.width, surface->resource.height);
634 return WINED3DERR_NOTAVAILABLE;
638 if (pow2Width != surface->resource.width
639 || pow2Height != surface->resource.height)
641 surface->flags |= SFLAG_NONPOW2;
644 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
645 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
647 /* One of three options:
648 * 1: Do the same as we do with NPOT and scale the texture, (any
649 * texture ops would require the texture to be scaled which is
650 * potentially slow)
651 * 2: Set the texture to the maximum size (bad idea).
652 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
653 * 4: Create the surface, but allow it to be used only for DirectDraw
654 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
655 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
656 * the render target. */
657 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
659 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
660 return WINED3DERR_NOTAVAILABLE;
663 /* We should never use this surface in combination with OpenGL! */
664 TRACE("Creating an oversized surface: %ux%u.\n",
665 surface->pow2Width, surface->pow2Height);
668 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
669 surface->locations = WINED3D_LOCATION_DISCARDED;
671 if (surface_use_pbo(surface))
672 surface->resource.map_binding = WINED3D_LOCATION_BUFFER;
674 return WINED3D_OK;
677 static void surface_unmap(struct wined3d_surface *surface)
679 struct wined3d_device *device = surface->resource.device;
680 const struct wined3d_gl_info *gl_info;
681 struct wined3d_context *context;
683 TRACE("surface %p.\n", surface);
685 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
687 switch (surface->resource.map_binding)
689 case WINED3D_LOCATION_SYSMEM:
690 case WINED3D_LOCATION_USER_MEMORY:
691 case WINED3D_LOCATION_DIB:
692 break;
694 case WINED3D_LOCATION_BUFFER:
695 context = context_acquire(device, NULL);
696 gl_info = context->gl_info;
698 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
699 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
700 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
701 checkGLcall("glUnmapBuffer");
702 context_release(context);
703 break;
705 default:
706 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
709 if (surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_TEXTURE_RGB))
711 TRACE("Not dirtified, nothing to do.\n");
712 return;
715 if (surface->container->swapchain && surface->container->swapchain->front_buffer == surface->container)
717 context = context_acquire(device, surface);
718 surface_load_location(surface, context, surface->container->resource.draw_binding);
719 context_release(context);
721 else if (surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
722 FIXME("Depth / stencil buffer locking is not implemented.\n");
725 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
727 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
728 return FALSE;
729 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
730 return FALSE;
731 return TRUE;
734 static void surface_depth_blt_fbo(const struct wined3d_device *device,
735 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
736 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
738 const struct wined3d_gl_info *gl_info;
739 struct wined3d_context *context;
740 DWORD src_mask, dst_mask;
741 GLbitfield gl_mask;
743 TRACE("device %p\n", device);
744 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
745 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
746 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
747 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
749 src_mask = src_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
750 dst_mask = dst_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
752 if (src_mask != dst_mask)
754 ERR("Incompatible formats %s and %s.\n",
755 debug_d3dformat(src_surface->resource.format->id),
756 debug_d3dformat(dst_surface->resource.format->id));
757 return;
760 if (!src_mask)
762 ERR("Not a depth / stencil format: %s.\n",
763 debug_d3dformat(src_surface->resource.format->id));
764 return;
767 gl_mask = 0;
768 if (src_mask & WINED3DFMT_FLAG_DEPTH)
769 gl_mask |= GL_DEPTH_BUFFER_BIT;
770 if (src_mask & WINED3DFMT_FLAG_STENCIL)
771 gl_mask |= GL_STENCIL_BUFFER_BIT;
773 context = context_acquire(device, NULL);
774 if (!context->valid)
776 context_release(context);
777 WARN("Invalid context, skipping blit.\n");
778 return;
781 /* Make sure the locations are up-to-date. Loading the destination
782 * surface isn't required if the entire surface is overwritten. */
783 surface_load_location(src_surface, context, src_location);
784 if (!surface_is_full_rect(dst_surface, dst_rect))
785 surface_load_location(dst_surface, context, dst_location);
786 else
787 wined3d_surface_prepare(dst_surface, context, dst_location);
789 gl_info = context->gl_info;
791 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
792 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
794 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
795 context_set_draw_buffer(context, GL_NONE);
796 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
797 context_invalidate_state(context, STATE_FRAMEBUFFER);
799 if (gl_mask & GL_DEPTH_BUFFER_BIT)
801 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
802 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
804 if (gl_mask & GL_STENCIL_BUFFER_BIT)
806 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
808 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
809 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
811 gl_info->gl_ops.gl.p_glStencilMask(~0U);
812 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
815 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
816 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
818 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
819 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
820 checkGLcall("glBlitFramebuffer()");
822 if (wined3d_settings.strict_draw_ordering)
823 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
825 context_release(context);
828 /* Blit between surface locations. Onscreen on different swapchains is not supported.
829 * Depth / stencil is not supported. Context activation is done by the caller. */
830 static void surface_blt_fbo(const struct wined3d_device *device,
831 struct wined3d_context *old_ctx, enum wined3d_texture_filter_type filter,
832 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
833 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
835 const struct wined3d_gl_info *gl_info;
836 struct wined3d_context *context = old_ctx;
837 struct wined3d_surface *required_rt, *restore_rt = NULL;
838 RECT src_rect, dst_rect;
839 GLenum gl_filter;
840 GLenum buffer;
842 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
843 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
844 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
845 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
846 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
848 src_rect = *src_rect_in;
849 dst_rect = *dst_rect_in;
851 switch (filter)
853 case WINED3D_TEXF_LINEAR:
854 gl_filter = GL_LINEAR;
855 break;
857 default:
858 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
859 case WINED3D_TEXF_NONE:
860 case WINED3D_TEXF_POINT:
861 gl_filter = GL_NEAREST;
862 break;
865 /* Resolve the source surface first if needed. */
866 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
867 && (src_surface->resource.format->id != dst_surface->resource.format->id
868 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
869 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
870 src_location = WINED3D_LOCATION_RB_RESOLVED;
872 /* Make sure the locations are up-to-date. Loading the destination
873 * surface isn't required if the entire surface is overwritten. (And is
874 * in fact harmful if we're being called by surface_load_location() with
875 * the purpose of loading the destination surface.) */
876 surface_load_location(src_surface, old_ctx, src_location);
877 if (!surface_is_full_rect(dst_surface, &dst_rect))
878 surface_load_location(dst_surface, old_ctx, dst_location);
879 else
880 wined3d_surface_prepare(dst_surface, old_ctx, dst_location);
883 if (src_location == WINED3D_LOCATION_DRAWABLE) required_rt = src_surface;
884 else if (dst_location == WINED3D_LOCATION_DRAWABLE) required_rt = dst_surface;
885 else required_rt = NULL;
887 if (required_rt && required_rt != old_ctx->current_rt)
889 restore_rt = old_ctx->current_rt;
890 context = context_acquire(device, required_rt);
893 if (!context->valid)
895 context_release(context);
896 WARN("Invalid context, skipping blit.\n");
897 return;
900 gl_info = context->gl_info;
902 if (src_location == WINED3D_LOCATION_DRAWABLE)
904 TRACE("Source surface %p is onscreen.\n", src_surface);
905 buffer = surface_get_gl_buffer(src_surface);
906 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
908 else
910 TRACE("Source surface %p is offscreen.\n", src_surface);
911 buffer = GL_COLOR_ATTACHMENT0;
914 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
915 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
916 checkGLcall("glReadBuffer()");
917 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
919 if (dst_location == WINED3D_LOCATION_DRAWABLE)
921 TRACE("Destination surface %p is onscreen.\n", dst_surface);
922 buffer = surface_get_gl_buffer(dst_surface);
923 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
925 else
927 TRACE("Destination surface %p is offscreen.\n", dst_surface);
928 buffer = GL_COLOR_ATTACHMENT0;
931 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
932 context_set_draw_buffer(context, buffer);
933 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
934 context_invalidate_state(context, STATE_FRAMEBUFFER);
936 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
937 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
938 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
939 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
940 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
942 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
943 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
945 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
946 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
947 checkGLcall("glBlitFramebuffer()");
949 if (wined3d_settings.strict_draw_ordering
950 || (dst_location == WINED3D_LOCATION_DRAWABLE
951 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
952 gl_info->gl_ops.gl.p_glFlush();
954 if (restore_rt)
955 context_restore(context, restore_rt);
958 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
959 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
960 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
962 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
963 return FALSE;
965 /* Source and/or destination need to be on the GL side */
966 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
967 return FALSE;
969 switch (blit_op)
971 case WINED3D_BLIT_OP_COLOR_BLIT:
972 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
973 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
974 return FALSE;
975 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
976 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
977 return FALSE;
978 if (!(src_format->id == dst_format->id
979 || (is_identity_fixup(src_format->color_fixup)
980 && is_identity_fixup(dst_format->color_fixup))))
981 return FALSE;
982 break;
984 case WINED3D_BLIT_OP_DEPTH_BLIT:
985 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
986 return FALSE;
987 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
988 return FALSE;
989 /* Accept pure swizzle fixups for depth formats. In general we
990 * ignore the stencil component (if present) at the moment and the
991 * swizzle is not relevant with just the depth component. */
992 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
993 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
994 return FALSE;
995 break;
997 default:
998 return FALSE;
1001 return TRUE;
1004 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1006 const struct wined3d_format *format = surface->resource.format;
1008 switch (format->id)
1010 case WINED3DFMT_S1_UINT_D15_UNORM:
1011 *float_depth = depth / (float)0x00007fff;
1012 break;
1014 case WINED3DFMT_D16_UNORM:
1015 *float_depth = depth / (float)0x0000ffff;
1016 break;
1018 case WINED3DFMT_D24_UNORM_S8_UINT:
1019 case WINED3DFMT_X8D24_UNORM:
1020 *float_depth = depth / (float)0x00ffffff;
1021 break;
1023 case WINED3DFMT_D32_UNORM:
1024 *float_depth = depth / (float)0xffffffff;
1025 break;
1027 default:
1028 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1029 return FALSE;
1032 return TRUE;
1035 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1037 struct wined3d_resource *resource = &surface->container->resource;
1038 struct wined3d_device *device = resource->device;
1039 struct wined3d_rendertarget_view_desc view_desc;
1040 struct wined3d_rendertarget_view *view;
1041 const struct blit_shader *blitter;
1042 HRESULT hr;
1044 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
1045 WINED3D_BLIT_OP_DEPTH_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
1047 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1048 return WINED3DERR_INVALIDCALL;
1051 view_desc.format_id = resource->format->id;
1052 view_desc.u.texture.level_idx = surface->texture_level;
1053 view_desc.u.texture.layer_idx = surface->texture_layer;
1054 view_desc.u.texture.layer_count = 1;
1055 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
1056 resource, NULL, &wined3d_null_parent_ops, &view)))
1058 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
1059 return hr;
1062 hr = blitter->depth_fill(device, view, rect, depth);
1063 wined3d_rendertarget_view_decref(view);
1065 return hr;
1068 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1069 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1071 struct wined3d_device *device = src_surface->resource.device;
1073 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1074 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1075 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1076 return WINED3DERR_INVALIDCALL;
1078 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1080 surface_modify_ds_location(dst_surface, dst_location,
1081 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1083 return WINED3D_OK;
1086 /* Context activation is done by the caller. */
1087 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1089 GL_EXTCALL(glDeleteBuffers(1, &surface->pbo));
1090 checkGLcall("glDeleteBuffers(1, &surface->pbo)");
1092 surface->pbo = 0;
1093 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1096 static ULONG surface_resource_incref(struct wined3d_resource *resource)
1098 struct wined3d_surface *surface = surface_from_resource(resource);
1100 TRACE("surface %p, container %p.\n", surface, surface->container);
1102 return wined3d_texture_incref(surface->container);
1105 static ULONG surface_resource_decref(struct wined3d_resource *resource)
1107 struct wined3d_surface *surface = surface_from_resource(resource);
1109 TRACE("surface %p, container %p.\n", surface, surface->container);
1111 return wined3d_texture_decref(surface->container);
1114 static void surface_unload(struct wined3d_resource *resource)
1116 struct wined3d_surface *surface = surface_from_resource(resource);
1117 struct wined3d_renderbuffer_entry *entry, *entry2;
1118 struct wined3d_device *device = resource->device;
1119 const struct wined3d_gl_info *gl_info;
1120 struct wined3d_context *context;
1122 TRACE("surface %p.\n", surface);
1124 context = context_acquire(device, NULL);
1125 gl_info = context->gl_info;
1127 if (resource->pool == WINED3D_POOL_DEFAULT)
1129 /* Default pool resources are supposed to be destroyed before Reset is called.
1130 * Implicit resources stay however. So this means we have an implicit render target
1131 * or depth stencil. The content may be destroyed, but we still have to tear down
1132 * opengl resources, so we cannot leave early.
1134 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1135 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1136 * or the depth stencil into an FBO the texture or render buffer will be removed
1137 * and all flags get lost */
1138 if (resource->usage & WINED3DUSAGE_DEPTHSTENCIL)
1140 surface_validate_location(surface, WINED3D_LOCATION_DISCARDED);
1141 surface_invalidate_location(surface, ~WINED3D_LOCATION_DISCARDED);
1143 else
1145 surface_prepare_system_memory(surface);
1146 memset(surface->resource.heap_memory, 0, surface->resource.size);
1147 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1148 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1151 else
1153 surface_prepare_map_memory(surface);
1154 surface_load_location(surface, context, surface->resource.map_binding);
1155 surface_invalidate_location(surface, ~surface->resource.map_binding);
1158 /* Destroy PBOs, but load them into real sysmem before */
1159 if (surface->pbo)
1160 surface_remove_pbo(surface, gl_info);
1162 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1163 * all application-created targets the application has to release the surface
1164 * before calling _Reset
1166 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1168 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1169 list_remove(&entry->entry);
1170 HeapFree(GetProcessHeap(), 0, entry);
1172 list_init(&surface->renderbuffers);
1173 surface->current_renderbuffer = NULL;
1175 if (surface->rb_multisample)
1177 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1178 surface->rb_multisample = 0;
1180 if (surface->rb_resolved)
1182 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1183 surface->rb_resolved = 0;
1186 context_release(context);
1188 resource_unload(resource);
1191 static HRESULT surface_resource_sub_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx,
1192 struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
1194 ERR("Not supported on sub-resources.\n");
1195 return WINED3DERR_INVALIDCALL;
1198 static HRESULT surface_resource_sub_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx)
1200 ERR("Not supported on sub-resources.\n");
1201 return WINED3DERR_INVALIDCALL;
1204 static const struct wined3d_resource_ops surface_resource_ops =
1206 surface_resource_incref,
1207 surface_resource_decref,
1208 surface_unload,
1209 surface_resource_sub_resource_map,
1210 surface_resource_sub_resource_unmap,
1213 static const struct wined3d_surface_ops surface_ops =
1215 surface_private_setup,
1216 surface_unmap,
1219 /*****************************************************************************
1220 * Initializes the GDI surface, aka creates the DIB section we render to
1221 * The DIB section creation is done by calling GetDC, which will create the
1222 * section and releasing the dc to allow the app to use it. The dib section
1223 * will stay until the surface is released
1225 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1226 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1227 * avoid confusion in the shared surface code.
1229 * Returns:
1230 * WINED3D_OK on success
1231 * The return values of called methods on failure
1233 *****************************************************************************/
1234 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1236 HRESULT hr;
1238 TRACE("surface %p.\n", surface);
1240 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1242 ERR("Overlays not yet supported by GDI surfaces.\n");
1243 return WINED3DERR_INVALIDCALL;
1246 /* Sysmem textures have memory already allocated - release it,
1247 * this avoids an unnecessary memcpy. */
1248 hr = surface_create_dib_section(surface);
1249 if (FAILED(hr))
1250 return hr;
1251 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1253 /* We don't mind the nonpow2 stuff in GDI. */
1254 surface->pow2Width = surface->resource.width;
1255 surface->pow2Height = surface->resource.height;
1257 return WINED3D_OK;
1260 static void gdi_surface_unmap(struct wined3d_surface *surface)
1262 TRACE("surface %p.\n", surface);
1264 /* Tell the swapchain to update the screen. */
1265 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
1266 x11_copy_to_screen(surface->container->swapchain, &surface->lockedRect);
1268 memset(&surface->lockedRect, 0, sizeof(RECT));
1271 static const struct wined3d_surface_ops gdi_surface_ops =
1273 gdi_surface_private_setup,
1274 gdi_surface_unmap,
1277 /* This call just downloads data, the caller is responsible for binding the
1278 * correct texture. */
1279 /* Context activation is done by the caller. */
1280 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1281 DWORD dst_location)
1283 const struct wined3d_format *format = surface->resource.format;
1284 struct wined3d_bo_address data;
1286 /* Only support read back of converted P8 surfaces. */
1287 if (surface->container->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1289 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1290 return;
1293 surface_get_memory(surface, &data, dst_location);
1295 if (surface->container->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
1297 TRACE("(%p) : Calling glGetCompressedTexImage level %d, format %#x, type %#x, data %p.\n",
1298 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1300 if (data.buffer_object)
1302 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1303 checkGLcall("glBindBuffer");
1304 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, NULL));
1305 checkGLcall("glGetCompressedTexImage");
1306 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1307 checkGLcall("glBindBuffer");
1309 else
1311 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target,
1312 surface->texture_level, data.addr));
1313 checkGLcall("glGetCompressedTexImage");
1316 else
1318 unsigned int dst_row_pitch, dst_slice_pitch;
1319 unsigned int src_row_pitch, src_slice_pitch;
1320 GLenum gl_format = format->glFormat;
1321 GLenum gl_type = format->glType;
1322 void *mem;
1324 if (surface->flags & SFLAG_NONPOW2)
1326 wined3d_texture_get_pitch(surface->container, surface->texture_level, &dst_row_pitch, &dst_slice_pitch);
1327 wined3d_format_calculate_pitch(format, surface->resource.device->surface_alignment,
1328 surface->pow2Width, surface->pow2Height, &src_row_pitch, &src_slice_pitch);
1329 mem = HeapAlloc(GetProcessHeap(), 0, src_slice_pitch);
1331 else
1333 mem = data.addr;
1336 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1337 surface, surface->texture_level, gl_format, gl_type, mem);
1339 if (data.buffer_object)
1341 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1342 checkGLcall("glBindBuffer");
1344 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1345 gl_format, gl_type, NULL);
1346 checkGLcall("glGetTexImage");
1348 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1349 checkGLcall("glBindBuffer");
1351 else
1353 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1354 gl_format, gl_type, mem);
1355 checkGLcall("glGetTexImage");
1358 if (surface->flags & SFLAG_NONPOW2)
1360 const BYTE *src_data;
1361 BYTE *dst_data;
1362 UINT y;
1364 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1365 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1366 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1368 * We're doing this...
1370 * instead of boxing the texture :
1371 * |<-texture width ->| -->pow2width| /\
1372 * |111111111111111111| | |
1373 * |222 Texture 222222| boxed empty | texture height
1374 * |3333 Data 33333333| | |
1375 * |444444444444444444| | \/
1376 * ----------------------------------- |
1377 * | boxed empty | boxed empty | pow2height
1378 * | | | \/
1379 * -----------------------------------
1382 * we're repacking the data to the expected texture width
1384 * |<-texture width ->| -->pow2width| /\
1385 * |111111111111111111222222222222222| |
1386 * |222333333333333333333444444444444| texture height
1387 * |444444 | |
1388 * | | \/
1389 * | | |
1390 * | empty | pow2height
1391 * | | \/
1392 * -----------------------------------
1394 * == is the same as
1396 * |<-texture width ->| /\
1397 * |111111111111111111|
1398 * |222222222222222222|texture height
1399 * |333333333333333333|
1400 * |444444444444444444| \/
1401 * --------------------
1403 * This also means that any references to surface memory should work with the data as if it were a
1404 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1406 * internally the texture is still stored in a boxed format so any references to textureName will
1407 * get a boxed texture with width pow2width and not a texture of width resource.width.
1409 * Performance should not be an issue, because applications normally do not lock the surfaces when
1410 * rendering. If an app does, the WINED3D_TEXTURE_DYNAMIC_MAP flag will kick in and the memory copy
1411 * won't be released, and doesn't have to be re-read. */
1412 src_data = mem;
1413 dst_data = data.addr;
1414 TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
1415 for (y = 0; y < surface->resource.height; ++y)
1417 memcpy(dst_data, src_data, dst_row_pitch);
1418 src_data += src_row_pitch;
1419 dst_data += dst_row_pitch;
1422 HeapFree(GetProcessHeap(), 0, mem);
1427 /* This call just uploads data, the caller is responsible for binding the
1428 * correct texture. */
1429 /* Context activation is done by the caller. */
1430 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1431 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1432 BOOL srgb, const struct wined3d_const_bo_address *data)
1434 UINT update_w = src_rect->right - src_rect->left;
1435 UINT update_h = src_rect->bottom - src_rect->top;
1437 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1438 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1439 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1441 if (surface->resource.map_count)
1443 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
1444 surface->container->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
1447 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
1449 update_h *= format->height_scale.numerator;
1450 update_h /= format->height_scale.denominator;
1453 if (data->buffer_object)
1455 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
1456 checkGLcall("glBindBuffer");
1459 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
1461 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1462 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1463 const BYTE *addr = data->addr;
1464 GLenum internal;
1466 addr += (src_rect->top / format->block_height) * src_pitch;
1467 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1469 if (srgb)
1470 internal = format->glGammaInternal;
1471 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1472 && wined3d_resource_is_offscreen(&surface->container->resource))
1473 internal = format->rtInternal;
1474 else
1475 internal = format->glInternal;
1477 TRACE("glCompressedTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, "
1478 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1479 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1481 if (row_length == src_pitch)
1483 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1484 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1486 else
1488 UINT row, y;
1490 /* glCompressedTexSubImage2D() ignores pixel store state, so we
1491 * can't use the unpack row length like for glTexSubImage2D. */
1492 for (row = 0, y = dst_point->y; row < row_count; ++row)
1494 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1495 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1496 y += format->block_height;
1497 addr += src_pitch;
1500 checkGLcall("glCompressedTexSubImage2D");
1502 else
1504 const BYTE *addr = data->addr;
1506 addr += src_rect->top * src_pitch;
1507 addr += src_rect->left * format->byte_count;
1509 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1510 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1511 update_w, update_h, format->glFormat, format->glType, addr);
1513 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1514 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1515 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1516 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1517 checkGLcall("glTexSubImage2D");
1520 if (data->buffer_object)
1522 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1523 checkGLcall("glBindBuffer");
1526 if (wined3d_settings.strict_draw_ordering)
1527 gl_info->gl_ops.gl.p_glFlush();
1529 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1531 struct wined3d_device *device = surface->resource.device;
1532 unsigned int i;
1534 for (i = 0; i < device->context_count; ++i)
1536 context_surface_update(device->contexts[i], surface);
1541 static BOOL surface_check_block_align_rect(struct wined3d_surface *surface, const RECT *rect)
1543 struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
1545 return wined3d_texture_check_block_align(surface->container, surface->texture_level, &box);
1548 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1549 struct wined3d_surface *src_surface, const RECT *src_rect)
1551 unsigned int src_row_pitch, src_slice_pitch;
1552 const struct wined3d_format *src_format;
1553 const struct wined3d_format *dst_format;
1554 unsigned int src_fmt_flags, dst_fmt_flags;
1555 const struct wined3d_gl_info *gl_info;
1556 struct wined3d_context *context;
1557 struct wined3d_bo_address data;
1558 UINT update_w, update_h;
1559 UINT dst_w, dst_h;
1560 RECT r, dst_rect;
1561 POINT p;
1563 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1564 dst_surface, wine_dbgstr_point(dst_point),
1565 src_surface, wine_dbgstr_rect(src_rect));
1567 src_format = src_surface->resource.format;
1568 dst_format = dst_surface->resource.format;
1569 src_fmt_flags = src_surface->container->resource.format_flags;
1570 dst_fmt_flags = dst_surface->container->resource.format_flags;
1572 if (src_format->id != dst_format->id)
1574 WARN("Source and destination surfaces should have the same format.\n");
1575 return WINED3DERR_INVALIDCALL;
1578 if (!dst_point)
1580 p.x = 0;
1581 p.y = 0;
1582 dst_point = &p;
1584 else if (dst_point->x < 0 || dst_point->y < 0)
1586 WARN("Invalid destination point.\n");
1587 return WINED3DERR_INVALIDCALL;
1590 if (!src_rect)
1592 r.left = 0;
1593 r.top = 0;
1594 r.right = src_surface->resource.width;
1595 r.bottom = src_surface->resource.height;
1596 src_rect = &r;
1598 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1599 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1601 WARN("Invalid source rectangle.\n");
1602 return WINED3DERR_INVALIDCALL;
1605 dst_w = dst_surface->resource.width;
1606 dst_h = dst_surface->resource.height;
1608 update_w = src_rect->right - src_rect->left;
1609 update_h = src_rect->bottom - src_rect->top;
1611 if (update_w > dst_w || dst_point->x > dst_w - update_w
1612 || update_h > dst_h || dst_point->y > dst_h - update_h)
1614 WARN("Destination out of bounds.\n");
1615 return WINED3DERR_INVALIDCALL;
1618 if ((src_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(src_surface, src_rect))
1620 WARN("Source rectangle not block-aligned.\n");
1621 return WINED3DERR_INVALIDCALL;
1624 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1625 if ((dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(dst_surface, &dst_rect))
1627 WARN("Destination rectangle not block-aligned.\n");
1628 return WINED3DERR_INVALIDCALL;
1631 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1632 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_surface->container, FALSE))
1633 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1635 context = context_acquire(dst_surface->resource.device, NULL);
1636 gl_info = context->gl_info;
1638 /* Only load the surface for partial updates. For newly allocated texture
1639 * the texture wouldn't be the current location, and we'd upload zeroes
1640 * just to overwrite them again. */
1641 if (update_w == dst_w && update_h == dst_h)
1642 wined3d_texture_prepare_texture(dst_surface->container, context, FALSE);
1643 else
1644 surface_load_location(dst_surface, context, WINED3D_LOCATION_TEXTURE_RGB);
1645 wined3d_texture_bind_and_dirtify(dst_surface->container, context, FALSE);
1647 surface_get_memory(src_surface, &data, src_surface->locations);
1648 wined3d_texture_get_pitch(src_surface->container, src_surface->texture_level, &src_row_pitch, &src_slice_pitch);
1650 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1651 src_row_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1653 context_release(context);
1655 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1656 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1658 return WINED3D_OK;
1661 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1662 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1663 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1664 /* Context activation is done by the caller. */
1665 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1667 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1668 struct wined3d_renderbuffer_entry *entry;
1669 GLuint renderbuffer = 0;
1670 unsigned int src_width, src_height;
1671 unsigned int width, height;
1673 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1675 width = rt->pow2Width;
1676 height = rt->pow2Height;
1678 else
1680 width = surface->pow2Width;
1681 height = surface->pow2Height;
1684 src_width = surface->pow2Width;
1685 src_height = surface->pow2Height;
1687 /* A depth stencil smaller than the render target is not valid */
1688 if (width > src_width || height > src_height) return;
1690 /* Remove any renderbuffer set if the sizes match */
1691 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1692 || (width == src_width && height == src_height))
1694 surface->current_renderbuffer = NULL;
1695 return;
1698 /* Look if we've already got a renderbuffer of the correct dimensions */
1699 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1701 if (entry->width == width && entry->height == height)
1703 renderbuffer = entry->id;
1704 surface->current_renderbuffer = entry;
1705 break;
1709 if (!renderbuffer)
1711 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1712 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1713 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1714 surface->resource.format->glInternal, width, height);
1716 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1717 entry->width = width;
1718 entry->height = height;
1719 entry->id = renderbuffer;
1720 list_add_head(&surface->renderbuffers, &entry->entry);
1722 surface->current_renderbuffer = entry;
1725 checkGLcall("set_compatible_renderbuffer");
1728 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1730 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1732 TRACE("surface %p.\n", surface);
1734 if (!swapchain)
1736 ERR("Surface %p is not on a swapchain.\n", surface);
1737 return GL_NONE;
1740 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
1742 if (swapchain->render_to_fbo)
1744 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1745 return GL_COLOR_ATTACHMENT0;
1747 TRACE("Returning GL_BACK\n");
1748 return GL_BACK;
1750 else if (surface->container == swapchain->front_buffer)
1752 TRACE("Returning GL_FRONT\n");
1753 return GL_FRONT;
1756 FIXME("Higher back buffer, returning GL_BACK\n");
1757 return GL_BACK;
1760 /* Context activation is done by the caller. */
1761 void surface_load(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
1763 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1765 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1767 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
1768 ERR("Not supported on scratch surfaces.\n");
1770 if (surface->locations & location)
1772 TRACE("surface is already in texture\n");
1773 return;
1775 TRACE("Reloading because surface is dirty.\n");
1777 surface_load_location(surface, context, location);
1778 surface_evict_sysmem(surface);
1781 /* See also float_16_to_32() in wined3d_private.h */
1782 static inline unsigned short float_32_to_16(const float *in)
1784 int exp = 0;
1785 float tmp = fabsf(*in);
1786 unsigned int mantissa;
1787 unsigned short ret;
1789 /* Deal with special numbers */
1790 if (*in == 0.0f)
1791 return 0x0000;
1792 if (isnan(*in))
1793 return 0x7c01;
1794 if (isinf(*in))
1795 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1797 if (tmp < (float)(1u << 10))
1801 tmp = tmp * 2.0f;
1802 exp--;
1803 } while (tmp < (float)(1u << 10));
1805 else if (tmp >= (float)(1u << 11))
1809 tmp /= 2.0f;
1810 exp++;
1811 } while (tmp >= (float)(1u << 11));
1814 mantissa = (unsigned int)tmp;
1815 if (tmp - mantissa >= 0.5f)
1816 ++mantissa; /* Round to nearest, away from zero. */
1818 exp += 10; /* Normalize the mantissa. */
1819 exp += 15; /* Exponent is encoded with excess 15. */
1821 if (exp > 30) /* too big */
1823 ret = 0x7c00; /* INF */
1825 else if (exp <= 0)
1827 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1828 while (exp <= 0)
1830 mantissa = mantissa >> 1;
1831 ++exp;
1833 ret = mantissa & 0x3ff;
1835 else
1837 ret = (exp << 10) | (mantissa & 0x3ff);
1840 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1841 return ret;
1844 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1846 struct wined3d_resource *texture_resource = &surface->container->resource;
1847 unsigned int width, height;
1848 BOOL create_dib = FALSE;
1849 DWORD valid_location = 0;
1850 HRESULT hr;
1852 if (surface->flags & SFLAG_DIBSECTION)
1854 DeleteDC(surface->hDC);
1855 DeleteObject(surface->dib.DIBsection);
1856 surface->dib.bitmap_data = NULL;
1857 surface->flags &= ~SFLAG_DIBSECTION;
1858 create_dib = TRUE;
1861 surface->locations = 0;
1862 wined3d_resource_free_sysmem(&surface->resource);
1864 width = texture_resource->width;
1865 height = texture_resource->height;
1866 surface->resource.width = width;
1867 surface->resource.height = height;
1868 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
1869 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
1871 surface->pow2Width = width;
1872 surface->pow2Height = height;
1874 else
1876 surface->pow2Width = surface->pow2Height = 1;
1877 while (surface->pow2Width < width)
1878 surface->pow2Width <<= 1;
1879 while (surface->pow2Height < height)
1880 surface->pow2Height <<= 1;
1883 if (surface->pow2Width != width || surface->pow2Height != height)
1884 surface->flags |= SFLAG_NONPOW2;
1885 else
1886 surface->flags &= ~SFLAG_NONPOW2;
1888 if (surface->container->user_memory)
1890 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
1891 valid_location = WINED3D_LOCATION_USER_MEMORY;
1893 surface->resource.format = texture_resource->format;
1894 surface->resource.multisample_type = texture_resource->multisample_type;
1895 surface->resource.multisample_quality = texture_resource->multisample_quality;
1896 surface->resource.size = surface->container->slice_pitch;
1898 /* The format might be changed to a format that needs conversion.
1899 * If the surface didn't use PBOs previously but could now, don't
1900 * change it - whatever made us not use PBOs might come back, e.g.
1901 * color keys. */
1902 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
1903 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
1905 if (create_dib)
1907 if (FAILED(hr = surface_create_dib_section(surface)))
1909 ERR("Failed to create dib section, hr %#x.\n", hr);
1910 return hr;
1912 if (!valid_location)
1913 valid_location = WINED3D_LOCATION_DIB;
1916 if (!valid_location)
1918 surface_prepare_system_memory(surface);
1919 valid_location = WINED3D_LOCATION_SYSMEM;
1922 surface_validate_location(surface, valid_location);
1924 return WINED3D_OK;
1927 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
1928 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1930 unsigned short *dst_s;
1931 const float *src_f;
1932 unsigned int x, y;
1934 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1936 for (y = 0; y < h; ++y)
1938 src_f = (const float *)(src + y * pitch_in);
1939 dst_s = (unsigned short *) (dst + y * pitch_out);
1940 for (x = 0; x < w; ++x)
1942 dst_s[x] = float_32_to_16(src_f + x);
1947 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
1948 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1950 static const unsigned char convert_5to8[] =
1952 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
1953 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
1954 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
1955 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
1957 static const unsigned char convert_6to8[] =
1959 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
1960 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
1961 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
1962 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
1963 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
1964 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
1965 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
1966 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
1968 unsigned int x, y;
1970 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1972 for (y = 0; y < h; ++y)
1974 const WORD *src_line = (const WORD *)(src + y * pitch_in);
1975 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1976 for (x = 0; x < w; ++x)
1978 WORD pixel = src_line[x];
1979 dst_line[x] = 0xff000000u
1980 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
1981 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
1982 | convert_5to8[(pixel & 0x001fu)];
1987 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
1988 * in both cases we're just setting the X / Alpha channel to 0xff. */
1989 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
1990 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1992 unsigned int x, y;
1994 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1996 for (y = 0; y < h; ++y)
1998 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
1999 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2001 for (x = 0; x < w; ++x)
2003 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2008 static inline BYTE cliptobyte(int x)
2010 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2013 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2014 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2016 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2017 unsigned int x, y;
2019 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2021 for (y = 0; y < h; ++y)
2023 const BYTE *src_line = src + y * pitch_in;
2024 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2025 for (x = 0; x < w; ++x)
2027 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2028 * C = Y - 16; D = U - 128; E = V - 128;
2029 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2030 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2031 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2032 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2033 * U and V are shared between the pixels. */
2034 if (!(x & 1)) /* For every even pixel, read new U and V. */
2036 d = (int) src_line[1] - 128;
2037 e = (int) src_line[3] - 128;
2038 r2 = 409 * e + 128;
2039 g2 = - 100 * d - 208 * e + 128;
2040 b2 = 516 * d + 128;
2042 c2 = 298 * ((int) src_line[0] - 16);
2043 dst_line[x] = 0xff000000
2044 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2045 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2046 | cliptobyte((c2 + b2) >> 8); /* blue */
2047 /* Scale RGB values to 0..255 range,
2048 * then clip them if still not in range (may be negative),
2049 * then shift them within DWORD if necessary. */
2050 src_line += 2;
2055 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2056 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2058 unsigned int x, y;
2059 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2061 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2063 for (y = 0; y < h; ++y)
2065 const BYTE *src_line = src + y * pitch_in;
2066 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2067 for (x = 0; x < w; ++x)
2069 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2070 * C = Y - 16; D = U - 128; E = V - 128;
2071 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2072 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2073 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2074 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2075 * U and V are shared between the pixels. */
2076 if (!(x & 1)) /* For every even pixel, read new U and V. */
2078 d = (int) src_line[1] - 128;
2079 e = (int) src_line[3] - 128;
2080 r2 = 409 * e + 128;
2081 g2 = - 100 * d - 208 * e + 128;
2082 b2 = 516 * d + 128;
2084 c2 = 298 * ((int) src_line[0] - 16);
2085 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2086 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2087 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2088 /* Scale RGB values to 0..255 range,
2089 * then clip them if still not in range (may be negative),
2090 * then shift them within DWORD if necessary. */
2091 src_line += 2;
2096 struct d3dfmt_converter_desc
2098 enum wined3d_format_id from, to;
2099 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2102 static const struct d3dfmt_converter_desc converters[] =
2104 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2105 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2106 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2107 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2108 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2109 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2112 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2113 enum wined3d_format_id to)
2115 unsigned int i;
2117 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2119 if (converters[i].from == from && converters[i].to == to)
2120 return &converters[i];
2123 return NULL;
2126 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2128 struct wined3d_map_desc src_map, dst_map;
2129 const struct d3dfmt_converter_desc *conv;
2130 struct wined3d_texture *ret = NULL;
2131 struct wined3d_resource_desc desc;
2132 struct wined3d_surface *dst;
2134 conv = find_converter(source->resource.format->id, to_fmt);
2135 if (!conv)
2137 FIXME("Cannot find a conversion function from format %s to %s.\n",
2138 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2139 return NULL;
2142 /* FIXME: Multisampled conversion? */
2143 wined3d_resource_get_desc(&source->resource, &desc);
2144 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
2145 desc.format = to_fmt;
2146 desc.usage = 0;
2147 desc.pool = WINED3D_POOL_SCRATCH;
2148 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2149 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
2150 NULL, NULL, &wined3d_null_parent_ops, &ret)))
2152 ERR("Failed to create a destination surface for conversion.\n");
2153 return NULL;
2155 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2157 memset(&src_map, 0, sizeof(src_map));
2158 memset(&dst_map, 0, sizeof(dst_map));
2160 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2162 ERR("Failed to lock the source surface.\n");
2163 wined3d_texture_decref(ret);
2164 return NULL;
2166 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2168 ERR("Failed to lock the destination surface.\n");
2169 wined3d_surface_unmap(source);
2170 wined3d_texture_decref(ret);
2171 return NULL;
2174 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2175 source->resource.width, source->resource.height);
2177 wined3d_surface_unmap(dst);
2178 wined3d_surface_unmap(source);
2180 return ret;
2183 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2184 unsigned int bpp, UINT pitch, DWORD color)
2186 BYTE *first;
2187 unsigned int x, y;
2189 /* Do first row */
2191 #define COLORFILL_ROW(type) \
2192 do { \
2193 type *d = (type *)buf; \
2194 for (x = 0; x < width; ++x) \
2195 d[x] = (type)color; \
2196 } while(0)
2198 switch (bpp)
2200 case 1:
2201 COLORFILL_ROW(BYTE);
2202 break;
2204 case 2:
2205 COLORFILL_ROW(WORD);
2206 break;
2208 case 3:
2210 BYTE *d = buf;
2211 for (x = 0; x < width; ++x, d += 3)
2213 d[0] = (color ) & 0xff;
2214 d[1] = (color >> 8) & 0xff;
2215 d[2] = (color >> 16) & 0xff;
2217 break;
2219 case 4:
2220 COLORFILL_ROW(DWORD);
2221 break;
2223 default:
2224 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2225 return WINED3DERR_NOTAVAILABLE;
2228 #undef COLORFILL_ROW
2230 /* Now copy first row. */
2231 first = buf;
2232 for (y = 1; y < height; ++y)
2234 buf += pitch;
2235 memcpy(buf, first, width * bpp);
2238 return WINED3D_OK;
2241 HRESULT wined3d_surface_unmap(struct wined3d_surface *surface)
2243 TRACE("surface %p.\n", surface);
2245 if (!surface->resource.map_count)
2247 WARN("Trying to unmap unmapped surface.\n");
2248 return WINEDDERR_NOTLOCKED;
2250 --surface->resource.map_count;
2252 surface->surface_ops->surface_unmap(surface);
2254 return WINED3D_OK;
2257 HRESULT wined3d_surface_map(struct wined3d_surface *surface, struct wined3d_map_desc *map_desc,
2258 const struct wined3d_box *box, DWORD flags)
2260 const struct wined3d_format *format = surface->resource.format;
2261 unsigned int fmt_flags = surface->container->resource.format_flags;
2262 struct wined3d_device *device = surface->resource.device;
2263 struct wined3d_context *context;
2264 const struct wined3d_gl_info *gl_info;
2265 BYTE *base_memory;
2267 TRACE("surface %p, map_desc %p, box %s, flags %#x.\n",
2268 surface, map_desc, debug_box(box), flags);
2270 if (surface->resource.map_count)
2272 WARN("Surface is already mapped.\n");
2273 return WINED3DERR_INVALIDCALL;
2276 if ((fmt_flags & WINED3DFMT_FLAG_BLOCKS) && box
2277 && !wined3d_texture_check_block_align(surface->container, surface->texture_level, box))
2279 WARN("Map box %s is misaligned for %ux%u blocks.\n",
2280 debug_box(box), format->block_width, format->block_height);
2282 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2283 return WINED3DERR_INVALIDCALL;
2286 ++surface->resource.map_count;
2288 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2289 WARN("Trying to lock unlockable surface.\n");
2291 /* Performance optimization: Count how often a surface is mapped, if it is
2292 * mapped regularly do not throw away the system memory copy. This avoids
2293 * the need to download the surface from OpenGL all the time. The surface
2294 * is still downloaded if the OpenGL texture is changed. Note that this
2295 * only really makes sense for managed textures.*/
2296 if (!(surface->container->flags & WINED3D_TEXTURE_DYNAMIC_MAP)
2297 && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2299 if (++surface->lockCount > MAXLOCKCOUNT)
2301 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2302 surface->container->flags |= WINED3D_TEXTURE_DYNAMIC_MAP;
2306 surface_prepare_map_memory(surface);
2307 if (flags & WINED3D_MAP_DISCARD)
2309 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2310 wined3d_debug_location(surface->resource.map_binding));
2311 surface_validate_location(surface, surface->resource.map_binding);
2313 else
2315 struct wined3d_context *context = NULL;
2317 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2318 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2320 if (surface->resource.device->d3d_initialized)
2321 context = context_acquire(surface->resource.device, NULL);
2322 surface_load_location(surface, context, surface->resource.map_binding);
2323 if (context)
2324 context_release(context);
2327 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2328 surface_invalidate_location(surface, ~surface->resource.map_binding);
2330 switch (surface->resource.map_binding)
2332 case WINED3D_LOCATION_SYSMEM:
2333 base_memory = surface->resource.heap_memory;
2334 break;
2336 case WINED3D_LOCATION_USER_MEMORY:
2337 base_memory = surface->container->user_memory;
2338 break;
2340 case WINED3D_LOCATION_DIB:
2341 base_memory = surface->dib.bitmap_data;
2342 break;
2344 case WINED3D_LOCATION_BUFFER:
2345 context = context_acquire(device, NULL);
2346 gl_info = context->gl_info;
2348 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
2349 base_memory = GL_EXTCALL(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE));
2350 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
2351 checkGLcall("map PBO");
2353 context_release(context);
2354 break;
2356 default:
2357 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2358 base_memory = NULL;
2361 if (fmt_flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2363 map_desc->row_pitch = surface->resource.width * format->byte_count;
2364 map_desc->slice_pitch = surface->resource.height * map_desc->row_pitch;
2366 else
2368 wined3d_texture_get_pitch(surface->container, surface->texture_level,
2369 &map_desc->row_pitch, &map_desc->slice_pitch);
2372 if (!box)
2374 map_desc->data = base_memory;
2375 surface->lockedRect.left = 0;
2376 surface->lockedRect.top = 0;
2377 surface->lockedRect.right = surface->resource.width;
2378 surface->lockedRect.bottom = surface->resource.height;
2380 else
2382 if ((fmt_flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2384 /* Compressed textures are block based, so calculate the offset of
2385 * the block that contains the top-left pixel of the locked rectangle. */
2386 map_desc->data = base_memory
2387 + ((box->top / format->block_height) * map_desc->row_pitch)
2388 + ((box->left / format->block_width) * format->block_byte_count);
2390 else
2392 map_desc->data = base_memory
2393 + (map_desc->row_pitch * box->top)
2394 + (box->left * format->byte_count);
2396 surface->lockedRect.left = box->left;
2397 surface->lockedRect.top = box->top;
2398 surface->lockedRect.right = box->right;
2399 surface->lockedRect.bottom = box->bottom;
2402 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2403 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2405 return WINED3D_OK;
2408 static void read_from_framebuffer(struct wined3d_surface *surface,
2409 struct wined3d_context *old_ctx, DWORD dst_location)
2411 struct wined3d_device *device = surface->resource.device;
2412 const struct wined3d_gl_info *gl_info;
2413 struct wined3d_context *context = old_ctx;
2414 struct wined3d_surface *restore_rt = NULL;
2415 unsigned int row_pitch, slice_pitch;
2416 BYTE *mem;
2417 BYTE *row, *top, *bottom;
2418 int i;
2419 BOOL srcIsUpsideDown;
2420 struct wined3d_bo_address data;
2422 surface_get_memory(surface, &data, dst_location);
2424 if (surface != old_ctx->current_rt)
2426 restore_rt = old_ctx->current_rt;
2427 context = context_acquire(device, surface);
2430 context_apply_blit_state(context, device);
2431 gl_info = context->gl_info;
2433 /* Select the correct read buffer, and give some debug output.
2434 * There is no need to keep track of the current read buffer or reset it, every part of the code
2435 * that reads sets the read buffer as desired.
2437 if (wined3d_resource_is_offscreen(&surface->container->resource))
2439 /* Mapping the primary render target which is not on a swapchain.
2440 * Read from the back buffer. */
2441 TRACE("Mapping offscreen render target.\n");
2442 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2443 srcIsUpsideDown = TRUE;
2445 else
2447 /* Onscreen surfaces are always part of a swapchain */
2448 GLenum buffer = surface_get_gl_buffer(surface);
2449 TRACE("Mapping %#x buffer.\n", buffer);
2450 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2451 checkGLcall("glReadBuffer");
2452 srcIsUpsideDown = FALSE;
2455 if (data.buffer_object)
2457 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
2458 checkGLcall("glBindBuffer");
2461 wined3d_texture_get_pitch(surface->container, surface->texture_level, &row_pitch, &slice_pitch);
2463 /* Setup pixel store pack state -- to glReadPixels into the correct place */
2464 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / surface->resource.format->byte_count);
2465 checkGLcall("glPixelStorei");
2467 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
2468 surface->resource.width, surface->resource.height,
2469 surface->resource.format->glFormat,
2470 surface->resource.format->glType, data.addr);
2471 checkGLcall("glReadPixels");
2473 /* Reset previous pixel store pack state */
2474 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
2475 checkGLcall("glPixelStorei");
2477 if (!srcIsUpsideDown)
2479 /* glReadPixels returns the image upside down, and there is no way to
2480 * prevent this. Flip the lines in software. */
2482 if (!(row = HeapAlloc(GetProcessHeap(), 0, row_pitch)))
2483 goto error;
2485 if (data.buffer_object)
2487 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
2488 checkGLcall("glMapBuffer");
2490 else
2491 mem = data.addr;
2493 top = mem;
2494 bottom = mem + row_pitch * (surface->resource.height - 1);
2495 for (i = 0; i < surface->resource.height / 2; i++)
2497 memcpy(row, top, row_pitch);
2498 memcpy(top, bottom, row_pitch);
2499 memcpy(bottom, row, row_pitch);
2500 top += row_pitch;
2501 bottom -= row_pitch;
2503 HeapFree(GetProcessHeap(), 0, row);
2505 if (data.buffer_object)
2506 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
2509 error:
2510 if (data.buffer_object)
2512 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2513 checkGLcall("glBindBuffer");
2516 if (restore_rt)
2517 context_restore(context, restore_rt);
2520 /* Read the framebuffer contents into a texture. Note that this function
2521 * doesn't do any kind of flipping. Using this on an onscreen surface will
2522 * result in a flipped D3D texture.
2524 * Context activation is done by the caller. This function may temporarily
2525 * switch to a different context and restore the original one before return. */
2526 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
2528 struct wined3d_device *device = surface->resource.device;
2529 const struct wined3d_gl_info *gl_info;
2530 struct wined3d_context *context = old_ctx;
2531 struct wined3d_surface *restore_rt = NULL;
2533 if (old_ctx->current_rt != surface)
2535 restore_rt = old_ctx->current_rt;
2536 context = context_acquire(device, surface);
2539 gl_info = context->gl_info;
2540 device_invalidate_state(device, STATE_FRAMEBUFFER);
2542 wined3d_texture_prepare_texture(surface->container, context, srgb);
2543 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
2545 TRACE("Reading back offscreen render target %p.\n", surface);
2547 if (wined3d_resource_is_offscreen(&surface->container->resource))
2548 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2549 else
2550 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
2551 checkGLcall("glReadBuffer");
2553 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
2554 0, 0, 0, 0, surface->resource.width, surface->resource.height);
2555 checkGLcall("glCopyTexSubImage2D");
2557 if (restore_rt)
2558 context_restore(context, restore_rt);
2561 static void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2563 if (multisample)
2565 DWORD samples;
2567 if (surface->rb_multisample)
2568 return;
2570 /* TODO: Nvidia exposes their Coverage Sample Anti-Aliasing (CSAA) feature
2571 * through type == MULTISAMPLE_XX and quality != 0. This could be mapped
2572 * to GL_NV_framebuffer_multisample_coverage.
2574 * AMD has a similar feature called Enhanced Quality Anti-Aliasing (EQAA),
2575 * but it does not have an equivalent OpenGL extension. */
2577 /* We advertise as many WINED3D_MULTISAMPLE_NON_MASKABLE quality levels
2578 * as the count of advertised multisample types for the surface format. */
2579 if (surface->resource.multisample_type == WINED3D_MULTISAMPLE_NON_MASKABLE)
2581 const struct wined3d_format *format = surface->resource.format;
2582 unsigned int i, count = 0;
2584 for (i = 0; i < sizeof(format->multisample_types) * 8; ++i)
2586 if (format->multisample_types & 1u << i)
2588 if (surface->resource.multisample_quality == count++)
2589 break;
2592 samples = i + 1;
2594 else
2596 samples = surface->resource.multisample_type;
2599 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2600 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2601 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
2602 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
2603 checkGLcall("glRenderbufferStorageMultisample()");
2604 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2606 else
2608 if (surface->rb_resolved)
2609 return;
2611 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2612 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2613 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
2614 surface->pow2Width, surface->pow2Height);
2615 checkGLcall("glRenderbufferStorage()");
2616 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2620 /* Does a direct frame buffer -> texture copy. Stretching is done with single
2621 * pixel copy calls. */
2622 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2623 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2625 struct wined3d_device *device = dst_surface->resource.device;
2626 const struct wined3d_gl_info *gl_info;
2627 float xrel, yrel;
2628 struct wined3d_context *context;
2629 BOOL upsidedown = FALSE;
2630 RECT dst_rect = *dst_rect_in;
2632 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2633 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2635 if(dst_rect.top > dst_rect.bottom) {
2636 UINT tmp = dst_rect.bottom;
2637 dst_rect.bottom = dst_rect.top;
2638 dst_rect.top = tmp;
2639 upsidedown = TRUE;
2642 context = context_acquire(device, src_surface);
2643 gl_info = context->gl_info;
2644 context_apply_blit_state(context, device);
2645 wined3d_texture_load(dst_surface->container, context, FALSE);
2647 /* Bind the target texture */
2648 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
2649 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
2651 TRACE("Reading from an offscreen target\n");
2652 upsidedown = !upsidedown;
2653 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2655 else
2657 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
2659 checkGLcall("glReadBuffer");
2661 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
2662 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
2664 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2666 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2668 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2669 ERR("Texture filtering not supported in direct blit.\n");
2671 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2672 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2674 ERR("Texture filtering not supported in direct blit\n");
2677 if (upsidedown
2678 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2679 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2681 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
2682 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2683 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
2684 src_rect->left, src_surface->resource.height - src_rect->bottom,
2685 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2687 else
2689 LONG row;
2690 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
2691 /* I have to process this row by row to swap the image,
2692 * otherwise it would be upside down, so stretching in y direction
2693 * doesn't cost extra time
2695 * However, stretching in x direction can be avoided if not necessary
2697 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
2698 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2700 /* Well, that stuff works, but it's very slow.
2701 * find a better way instead
2703 LONG col;
2705 for (col = dst_rect.left; col < dst_rect.right; ++col)
2707 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2708 dst_rect.left + col /* x offset */, row /* y offset */,
2709 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
2712 else
2714 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2715 dst_rect.left /* x offset */, row /* y offset */,
2716 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
2720 checkGLcall("glCopyTexSubImage2D");
2722 context_release(context);
2724 /* The texture is now most up to date - If the surface is a render target
2725 * and has a drawable, this path is never entered. */
2726 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
2727 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
2730 /* Uses the hardware to stretch and flip the image */
2731 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2732 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2734 struct wined3d_device *device = dst_surface->resource.device;
2735 GLuint src, backup = 0;
2736 float left, right, top, bottom; /* Texture coordinates */
2737 UINT fbwidth = src_surface->resource.width;
2738 UINT fbheight = src_surface->resource.height;
2739 const struct wined3d_gl_info *gl_info;
2740 struct wined3d_context *context;
2741 GLenum drawBuffer = GL_BACK;
2742 GLenum texture_target;
2743 BOOL noBackBufferBackup;
2744 BOOL src_offscreen;
2745 BOOL upsidedown = FALSE;
2746 RECT dst_rect = *dst_rect_in;
2748 TRACE("Using hwstretch blit\n");
2749 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2750 context = context_acquire(device, src_surface);
2751 gl_info = context->gl_info;
2752 context_apply_blit_state(context, device);
2753 wined3d_texture_load(dst_surface->container, context, FALSE);
2755 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
2756 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2757 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
2759 /* Get it a description */
2760 wined3d_texture_load(src_surface->container, context, FALSE);
2763 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2764 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2766 if (context->aux_buffers >= 2)
2768 /* Got more than one aux buffer? Use the 2nd aux buffer */
2769 drawBuffer = GL_AUX1;
2771 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
2773 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2774 drawBuffer = GL_AUX0;
2777 if (noBackBufferBackup)
2779 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
2780 checkGLcall("glGenTextures");
2781 context_bind_texture(context, GL_TEXTURE_2D, backup);
2782 texture_target = GL_TEXTURE_2D;
2784 else
2786 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2787 * we are reading from the back buffer, the backup can be used as source texture
2789 texture_target = src_surface->texture_target;
2790 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
2791 gl_info->gl_ops.gl.p_glEnable(texture_target);
2792 checkGLcall("glEnable(texture_target)");
2794 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2795 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
2798 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2799 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2801 if(dst_rect.top > dst_rect.bottom) {
2802 UINT tmp = dst_rect.bottom;
2803 dst_rect.bottom = dst_rect.top;
2804 dst_rect.top = tmp;
2805 upsidedown = TRUE;
2808 if (src_offscreen)
2810 TRACE("Reading from an offscreen target\n");
2811 upsidedown = !upsidedown;
2812 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2814 else
2816 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
2819 /* TODO: Only back up the part that will be overwritten */
2820 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
2822 checkGLcall("glCopyTexSubImage2D");
2824 /* No issue with overriding these - the sampler is dirty due to blit usage */
2825 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
2826 checkGLcall("glTexParameteri");
2827 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2828 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
2829 checkGLcall("glTexParameteri");
2831 if (!src_surface->container->swapchain
2832 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
2834 src = backup ? backup : src_surface->container->texture_rgb.name;
2836 else
2838 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
2839 checkGLcall("glReadBuffer(GL_FRONT)");
2841 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
2842 checkGLcall("glGenTextures(1, &src)");
2843 context_bind_texture(context, GL_TEXTURE_2D, src);
2845 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
2846 * out for power of 2 sizes
2848 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
2849 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2850 checkGLcall("glTexImage2D");
2851 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
2853 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2854 checkGLcall("glTexParameteri");
2855 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2856 checkGLcall("glTexParameteri");
2858 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
2859 checkGLcall("glReadBuffer(GL_BACK)");
2861 if (texture_target != GL_TEXTURE_2D)
2863 gl_info->gl_ops.gl.p_glDisable(texture_target);
2864 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2865 texture_target = GL_TEXTURE_2D;
2868 checkGLcall("glEnd and previous");
2870 left = src_rect->left;
2871 right = src_rect->right;
2873 if (!upsidedown)
2875 top = src_surface->resource.height - src_rect->top;
2876 bottom = src_surface->resource.height - src_rect->bottom;
2878 else
2880 top = src_surface->resource.height - src_rect->bottom;
2881 bottom = src_surface->resource.height - src_rect->top;
2884 if (src_surface->container->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
2886 left /= src_surface->pow2Width;
2887 right /= src_surface->pow2Width;
2888 top /= src_surface->pow2Height;
2889 bottom /= src_surface->pow2Height;
2892 /* draw the source texture stretched and upside down. The correct surface is bound already */
2893 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2894 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2896 context_set_draw_buffer(context, drawBuffer);
2897 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
2899 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2900 /* bottom left */
2901 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
2902 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2904 /* top left */
2905 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
2906 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
2908 /* top right */
2909 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
2910 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2912 /* bottom right */
2913 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
2914 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
2915 gl_info->gl_ops.gl.p_glEnd();
2916 checkGLcall("glEnd and previous");
2918 if (texture_target != dst_surface->texture_target)
2920 gl_info->gl_ops.gl.p_glDisable(texture_target);
2921 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
2922 texture_target = dst_surface->texture_target;
2925 /* Now read the stretched and upside down image into the destination texture */
2926 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
2927 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
2929 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
2930 0, 0, /* We blitted the image to the origin */
2931 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2932 checkGLcall("glCopyTexSubImage2D");
2934 if (drawBuffer == GL_BACK)
2936 /* Write the back buffer backup back. */
2937 if (backup)
2939 if (texture_target != GL_TEXTURE_2D)
2941 gl_info->gl_ops.gl.p_glDisable(texture_target);
2942 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2943 texture_target = GL_TEXTURE_2D;
2945 context_bind_texture(context, GL_TEXTURE_2D, backup);
2947 else
2949 if (texture_target != src_surface->texture_target)
2951 gl_info->gl_ops.gl.p_glDisable(texture_target);
2952 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
2953 texture_target = src_surface->texture_target;
2955 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
2958 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2959 /* top left */
2960 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
2961 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
2963 /* bottom left */
2964 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
2965 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2967 /* bottom right */
2968 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
2969 (float)fbheight / (float)src_surface->pow2Height);
2970 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
2972 /* top right */
2973 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
2974 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
2975 gl_info->gl_ops.gl.p_glEnd();
2977 gl_info->gl_ops.gl.p_glDisable(texture_target);
2978 checkGLcall("glDisable(texture_target)");
2980 /* Cleanup */
2981 if (src != src_surface->container->texture_rgb.name && src != backup)
2983 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
2984 checkGLcall("glDeleteTextures(1, &src)");
2986 if (backup)
2988 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
2989 checkGLcall("glDeleteTextures(1, &backup)");
2992 if (wined3d_settings.strict_draw_ordering)
2993 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2995 context_release(context);
2997 /* The texture is now most up to date - If the surface is a render target
2998 * and has a drawable, this path is never entered. */
2999 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3000 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3003 /* Front buffer coordinates are always full screen coordinates, but our GL
3004 * drawable is limited to the window's client area. The sysmem and texture
3005 * copies do have the full screen size. Note that GL has a bottom-left
3006 * origin, while D3D has a top-left origin. */
3007 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3009 UINT drawable_height;
3011 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3013 POINT offset = {0, 0};
3014 RECT windowsize;
3016 ScreenToClient(window, &offset);
3017 OffsetRect(rect, offset.x, offset.y);
3019 GetClientRect(window, &windowsize);
3020 drawable_height = windowsize.bottom - windowsize.top;
3022 else
3024 drawable_height = surface->resource.height;
3027 rect->top = drawable_height - rect->top;
3028 rect->bottom = drawable_height - rect->bottom;
3031 /* Context activation is done by the caller. */
3032 static void surface_blt_to_drawable(const struct wined3d_device *device,
3033 struct wined3d_context *old_ctx,
3034 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3035 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3036 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3038 const struct wined3d_gl_info *gl_info;
3039 struct wined3d_context *context = old_ctx;
3040 struct wined3d_surface *restore_rt = NULL;
3041 RECT src_rect, dst_rect;
3043 src_rect = *src_rect_in;
3044 dst_rect = *dst_rect_in;
3047 if (old_ctx->current_rt != dst_surface)
3049 restore_rt = old_ctx->current_rt;
3050 context = context_acquire(device, dst_surface);
3053 gl_info = context->gl_info;
3055 /* Make sure the surface is up-to-date. This should probably use
3056 * surface_load_location() and worry about the destination surface too,
3057 * unless we're overwriting it completely. */
3058 wined3d_texture_load(src_surface->container, context, FALSE);
3060 /* Activate the destination context, set it up for blitting */
3061 context_apply_blit_state(context, device);
3063 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3064 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3066 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
3068 if (alpha_test)
3070 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3071 checkGLcall("glEnable(GL_ALPHA_TEST)");
3073 /* For P8 surfaces, the alpha component contains the palette index.
3074 * Which means that the colorkey is one of the palette entries. In
3075 * other cases pixels that should be masked away have alpha set to 0. */
3076 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3077 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3078 (float)src_surface->container->async.src_blt_color_key.color_space_low_value / 255.0f);
3079 else
3080 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3081 checkGLcall("glAlphaFunc");
3083 else
3085 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3086 checkGLcall("glDisable(GL_ALPHA_TEST)");
3089 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3091 if (alpha_test)
3093 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3094 checkGLcall("glDisable(GL_ALPHA_TEST)");
3097 /* Leave the opengl state valid for blitting */
3098 device->blitter->unset_shader(context->gl_info);
3100 if (wined3d_settings.strict_draw_ordering
3101 || (dst_surface->container->swapchain
3102 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3103 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3105 if (restore_rt)
3106 context_restore(context, restore_rt);
3109 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3111 struct wined3d_resource *resource = &s->container->resource;
3112 struct wined3d_device *device = resource->device;
3113 struct wined3d_rendertarget_view_desc view_desc;
3114 struct wined3d_rendertarget_view *view;
3115 const struct blit_shader *blitter;
3116 HRESULT hr;
3118 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
3119 WINED3D_BLIT_OP_COLOR_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
3121 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3122 return WINED3DERR_INVALIDCALL;
3125 view_desc.format_id = resource->format->id;
3126 view_desc.u.texture.level_idx = s->texture_level;
3127 view_desc.u.texture.layer_idx = s->texture_layer;
3128 view_desc.u.texture.layer_count = 1;
3129 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
3130 resource, NULL, &wined3d_null_parent_ops, &view)))
3132 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
3133 return hr;
3136 hr = blitter->color_fill(device, view, rect, color);
3137 wined3d_rendertarget_view_decref(view);
3139 return hr;
3142 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3143 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
3144 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
3146 struct wined3d_device *device = dst_surface->resource.device;
3147 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3148 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3150 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
3151 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3152 flags, fx, debug_d3dtexturefiltertype(filter));
3154 /* Get the swapchain. One of the surfaces has to be a primary surface */
3155 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3157 WARN("Destination is in sysmem, rejecting gl blt\n");
3158 return WINED3DERR_INVALIDCALL;
3161 dst_swapchain = dst_surface->container->swapchain;
3163 if (src_surface)
3165 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3167 WARN("Src is in sysmem, rejecting gl blt\n");
3168 return WINED3DERR_INVALIDCALL;
3171 src_swapchain = src_surface->container->swapchain;
3173 else
3175 src_swapchain = NULL;
3178 /* Early sort out of cases where no render target is used */
3179 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
3181 TRACE("No surface is render target, not using hardware blit.\n");
3182 return WINED3DERR_INVALIDCALL;
3185 /* No destination color keying supported */
3186 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
3188 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3189 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3190 return WINED3DERR_INVALIDCALL;
3193 if (dst_swapchain && dst_swapchain == src_swapchain)
3195 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3196 return WINED3DERR_INVALIDCALL;
3199 if (dst_swapchain && src_swapchain)
3201 FIXME("Implement hardware blit between two different swapchains\n");
3202 return WINED3DERR_INVALIDCALL;
3205 if (dst_swapchain)
3207 /* Handled with regular texture -> swapchain blit */
3208 if (src_surface == rt)
3209 TRACE("Blit from active render target to a swapchain\n");
3211 else if (src_swapchain && dst_surface == rt)
3213 FIXME("Implement blit from a swapchain to the active render target\n");
3214 return WINED3DERR_INVALIDCALL;
3217 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
3219 /* Blit from render target to texture */
3220 BOOL stretchx;
3222 /* P8 read back is not implemented */
3223 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
3224 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
3226 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3227 return WINED3DERR_INVALIDCALL;
3230 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
3232 TRACE("Color keying not supported by frame buffer to texture blit\n");
3233 return WINED3DERR_INVALIDCALL;
3234 /* Destination color key is checked above */
3237 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
3238 stretchx = TRUE;
3239 else
3240 stretchx = FALSE;
3242 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3243 * flip the image nor scale it.
3245 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3246 * -> If the app wants an image width an unscaled width, copy it line per line
3247 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
3248 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3249 * back buffer. This is slower than reading line per line, thus not used for flipping
3250 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3251 * pixel by pixel. */
3252 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
3253 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
3255 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
3256 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
3258 else
3260 TRACE("Using hardware stretching to flip / stretch the texture.\n");
3261 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
3264 surface_evict_sysmem(dst_surface);
3266 return WINED3D_OK;
3269 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3270 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3271 return WINED3DERR_INVALIDCALL;
3274 /* Context activation is done by the caller. */
3275 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
3276 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
3278 struct wined3d_device *device = surface->resource.device;
3279 const struct wined3d_gl_info *gl_info = context->gl_info;
3280 GLint compare_mode = GL_NONE;
3281 struct blt_info info;
3282 GLint old_binding = 0;
3283 RECT rect;
3285 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
3287 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
3288 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
3289 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3290 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
3291 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
3292 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
3293 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
3294 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
3295 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3296 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
3297 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
3299 SetRect(&rect, 0, h, w, 0);
3300 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
3301 context_active_texture(context, context->gl_info, 0);
3302 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
3303 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
3304 if (gl_info->supported[ARB_SHADOW])
3306 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
3307 if (compare_mode != GL_NONE)
3308 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
3311 device->shader_backend->shader_select_depth_blt(device->shader_priv,
3312 gl_info, info.tex_type, &surface->ds_current_size);
3314 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
3315 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
3316 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
3317 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
3318 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
3319 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
3320 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
3321 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
3322 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
3323 gl_info->gl_ops.gl.p_glEnd();
3325 if (compare_mode != GL_NONE)
3326 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
3327 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
3329 gl_info->gl_ops.gl.p_glPopAttrib();
3331 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
3334 void surface_modify_ds_location(struct wined3d_surface *surface,
3335 DWORD location, UINT w, UINT h)
3337 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
3339 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
3340 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
3341 wined3d_texture_set_dirty(surface->container);
3343 surface->ds_current_size.cx = w;
3344 surface->ds_current_size.cy = h;
3345 surface->locations = location;
3348 /* Context activation is done by the caller. */
3349 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3351 const struct wined3d_gl_info *gl_info = context->gl_info;
3352 struct wined3d_device *device = surface->resource.device;
3353 GLsizei w, h;
3355 TRACE("surface %p, context %p, new location %#x.\n", surface, context, location);
3357 /* TODO: Make this work for modes other than FBO */
3358 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3360 if (!(surface->locations & location))
3362 w = surface->ds_current_size.cx;
3363 h = surface->ds_current_size.cy;
3364 surface->ds_current_size.cx = 0;
3365 surface->ds_current_size.cy = 0;
3367 else
3369 w = surface->resource.width;
3370 h = surface->resource.height;
3373 if (surface->ds_current_size.cx == surface->resource.width
3374 && surface->ds_current_size.cy == surface->resource.height)
3376 TRACE("Location (%#x) is already up to date.\n", location);
3377 return;
3380 if (surface->current_renderbuffer)
3382 FIXME("Not supported with fixed up depth stencil.\n");
3383 return;
3386 wined3d_surface_prepare(surface, context, location);
3387 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3389 TRACE("Surface was discarded, no need copy data.\n");
3390 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
3391 surface->locations |= location;
3392 surface->ds_current_size.cx = surface->resource.width;
3393 surface->ds_current_size.cy = surface->resource.height;
3394 return;
3397 if (!surface->locations)
3399 FIXME("No up to date depth stencil location.\n");
3400 surface->locations |= location;
3401 surface->ds_current_size.cx = surface->resource.width;
3402 surface->ds_current_size.cy = surface->resource.height;
3403 return;
3406 if (location == WINED3D_LOCATION_TEXTURE_RGB)
3408 GLint old_binding = 0;
3409 GLenum bind_target;
3411 /* The render target is allowed to be smaller than the depth/stencil
3412 * buffer, so the onscreen depth/stencil buffer is potentially smaller
3413 * than the offscreen surface. Don't overwrite the offscreen surface
3414 * with undefined data. */
3415 w = min(w, context->swapchain->desc.backbuffer_width);
3416 h = min(h, context->swapchain->desc.backbuffer_height);
3418 TRACE("Copying onscreen depth buffer to depth texture.\n");
3420 if (!device->depth_blt_texture)
3421 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
3423 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3424 * directly on the FBO texture. That's because we need to flip. */
3425 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3426 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3427 NULL, WINED3D_LOCATION_DRAWABLE);
3428 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
3430 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
3431 bind_target = GL_TEXTURE_RECTANGLE_ARB;
3433 else
3435 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3436 bind_target = GL_TEXTURE_2D;
3438 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
3439 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
3440 * internal format, because the internal format might include stencil
3441 * data. In principle we should copy stencil data as well, but unless
3442 * the driver supports stencil export it's hard to do, and doesn't
3443 * seem to be needed in practice. If the hardware doesn't support
3444 * writing stencil data, the glCopyTexImage2D() call might trigger
3445 * software fallbacks. */
3446 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
3447 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3448 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3449 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3450 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3451 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
3452 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
3454 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3455 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
3456 context_set_draw_buffer(context, GL_NONE);
3458 /* Do the actual blit */
3459 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
3460 checkGLcall("depth_blt");
3462 context_invalidate_state(context, STATE_FRAMEBUFFER);
3464 if (wined3d_settings.strict_draw_ordering)
3465 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3467 else if (location == WINED3D_LOCATION_DRAWABLE)
3469 TRACE("Copying depth texture to onscreen depth buffer.\n");
3471 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3472 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3473 NULL, WINED3D_LOCATION_DRAWABLE);
3474 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
3475 0, surface->pow2Height - h, w, h, surface->texture_target);
3476 checkGLcall("depth_blt");
3478 context_invalidate_state(context, STATE_FRAMEBUFFER);
3480 if (wined3d_settings.strict_draw_ordering)
3481 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3483 else
3485 ERR("Invalid location (%#x) specified.\n", location);
3488 surface->locations |= location;
3489 surface->ds_current_size.cx = surface->resource.width;
3490 surface->ds_current_size.cy = surface->resource.height;
3493 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
3495 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3497 surface->locations |= location;
3500 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
3502 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3504 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3505 wined3d_texture_set_dirty(surface->container);
3506 surface->locations &= ~location;
3508 if (!surface->locations)
3509 ERR("Surface %p does not have any up to date location.\n", surface);
3512 static DWORD resource_access_from_location(DWORD location)
3514 switch (location)
3516 case WINED3D_LOCATION_SYSMEM:
3517 case WINED3D_LOCATION_USER_MEMORY:
3518 case WINED3D_LOCATION_DIB:
3519 case WINED3D_LOCATION_BUFFER:
3520 return WINED3D_RESOURCE_ACCESS_CPU;
3522 case WINED3D_LOCATION_DRAWABLE:
3523 case WINED3D_LOCATION_TEXTURE_SRGB:
3524 case WINED3D_LOCATION_TEXTURE_RGB:
3525 case WINED3D_LOCATION_RB_MULTISAMPLE:
3526 case WINED3D_LOCATION_RB_RESOLVED:
3527 return WINED3D_RESOURCE_ACCESS_GPU;
3529 default:
3530 FIXME("Unhandled location %#x.\n", location);
3531 return 0;
3535 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
3537 struct wined3d_device *device = surface->resource.device;
3538 struct wined3d_context *context;
3539 const struct wined3d_gl_info *gl_info;
3540 struct wined3d_bo_address dst, src;
3541 UINT size = surface->resource.size;
3543 surface_get_memory(surface, &dst, location);
3544 surface_get_memory(surface, &src, surface->locations);
3546 if (dst.buffer_object)
3548 context = context_acquire(device, NULL);
3549 gl_info = context->gl_info;
3550 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
3551 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr));
3552 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
3553 checkGLcall("Upload PBO");
3554 context_release(context);
3555 return;
3557 if (src.buffer_object)
3559 context = context_acquire(device, NULL);
3560 gl_info = context->gl_info;
3561 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
3562 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr));
3563 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
3564 checkGLcall("Download PBO");
3565 context_release(context);
3566 return;
3568 memcpy(dst.addr, src.addr, size);
3571 /* Context activation is done by the caller. */
3572 static void surface_load_sysmem(struct wined3d_surface *surface,
3573 struct wined3d_context *context, DWORD dst_location)
3575 const struct wined3d_gl_info *gl_info = context->gl_info;
3577 if (surface->locations & surface_simple_locations)
3579 surface_copy_simple_location(surface, dst_location);
3580 return;
3583 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
3584 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3586 /* Download the surface to system memory. */
3587 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3589 wined3d_texture_bind_and_dirtify(surface->container, context,
3590 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
3591 surface_download_data(surface, gl_info, dst_location);
3593 return;
3596 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
3598 read_from_framebuffer(surface, context, dst_location);
3599 return;
3602 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
3603 surface, wined3d_debug_location(surface->locations));
3606 /* Context activation is done by the caller. */
3607 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
3608 struct wined3d_context *context)
3610 RECT r;
3612 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3613 && wined3d_resource_is_offscreen(&surface->container->resource))
3615 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
3616 return WINED3DERR_INVALIDCALL;
3619 surface_get_rect(surface, NULL, &r);
3620 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3621 surface_blt_to_drawable(surface->resource.device, context,
3622 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
3624 return WINED3D_OK;
3627 static HRESULT surface_load_texture(struct wined3d_surface *surface,
3628 struct wined3d_context *context, BOOL srgb)
3630 unsigned int width, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
3631 const RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
3632 const struct wined3d_gl_info *gl_info = context->gl_info;
3633 struct wined3d_device *device = surface->resource.device;
3634 const struct wined3d_color_key_conversion *conversion;
3635 struct wined3d_texture *texture = surface->container;
3636 struct wined3d_bo_address data;
3637 struct wined3d_format format;
3638 POINT dst_point = {0, 0};
3639 BYTE *mem = NULL;
3641 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
3642 && wined3d_resource_is_offscreen(&texture->resource)
3643 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
3645 surface_load_fb_texture(surface, srgb, context);
3647 return WINED3D_OK;
3650 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
3651 && (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
3652 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3653 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3654 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3656 if (srgb)
3657 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
3658 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
3659 else
3660 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
3661 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
3663 return WINED3D_OK;
3666 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
3667 && (!srgb || (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
3668 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3669 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3670 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3672 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
3673 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
3674 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
3675 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3677 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
3678 &rect, surface, dst_location, &rect);
3680 return WINED3D_OK;
3683 /* Upload from system memory */
3685 if (srgb)
3687 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
3688 == WINED3D_LOCATION_TEXTURE_RGB)
3690 /* Performance warning... */
3691 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
3692 surface_prepare_map_memory(surface);
3693 surface_load_location(surface, context, surface->resource.map_binding);
3696 else
3698 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
3699 == WINED3D_LOCATION_TEXTURE_SRGB)
3701 /* Performance warning... */
3702 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
3703 surface_prepare_map_memory(surface);
3704 surface_load_location(surface, context, surface->resource.map_binding);
3708 if (!(surface->locations & surface_simple_locations))
3710 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
3711 /* Lets hope we get it from somewhere... */
3712 surface_prepare_system_memory(surface);
3713 surface_load_location(surface, context, WINED3D_LOCATION_SYSMEM);
3716 wined3d_texture_prepare_texture(texture, context, srgb);
3717 wined3d_texture_bind_and_dirtify(texture, context, srgb);
3718 wined3d_texture_get_pitch(texture, surface->texture_level, &src_row_pitch, &src_slice_pitch);
3720 width = surface->resource.width;
3722 format = *texture->resource.format;
3723 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
3724 format = *wined3d_get_format(gl_info, conversion->dst_format);
3726 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
3727 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
3728 * getting called. */
3729 if ((format.convert || conversion) && surface->pbo)
3731 TRACE("Removing the pbo attached to surface %p.\n", surface);
3733 if (surface->flags & SFLAG_DIBSECTION)
3734 surface->resource.map_binding = WINED3D_LOCATION_DIB;
3735 else
3736 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
3738 surface_prepare_map_memory(surface);
3739 surface_load_location(surface, context, surface->resource.map_binding);
3740 surface_remove_pbo(surface, gl_info);
3743 surface_get_memory(surface, &data, surface->locations);
3744 if (format.convert)
3746 /* This code is entered for texture formats which need a fixup. */
3747 UINT height = surface->resource.height;
3749 format.byte_count = format.conv_byte_count;
3750 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
3752 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
3754 ERR("Out of memory (%u).\n", dst_slice_pitch);
3755 context_release(context);
3756 return E_OUTOFMEMORY;
3758 format.convert(data.addr, mem, src_row_pitch, src_slice_pitch,
3759 dst_row_pitch, dst_slice_pitch, width, height, 1);
3760 src_row_pitch = dst_row_pitch;
3761 data.addr = mem;
3763 else if (conversion)
3765 /* This code is only entered for color keying fixups */
3766 struct wined3d_palette *palette = NULL;
3767 UINT height = surface->resource.height;
3769 wined3d_format_calculate_pitch(&format, device->surface_alignment,
3770 width, height, &dst_row_pitch, &dst_slice_pitch);
3772 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
3774 ERR("Out of memory (%u).\n", dst_slice_pitch);
3775 context_release(context);
3776 return E_OUTOFMEMORY;
3778 if (texture->swapchain && texture->swapchain->palette)
3779 palette = texture->swapchain->palette;
3780 conversion->convert(data.addr, src_row_pitch, mem, dst_row_pitch,
3781 width, height, palette, &texture->async.gl_color_key);
3782 src_row_pitch = dst_row_pitch;
3783 data.addr = mem;
3786 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
3787 src_row_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
3789 HeapFree(GetProcessHeap(), 0, mem);
3791 return WINED3D_OK;
3794 /* Context activation is done by the caller. */
3795 static void surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
3796 DWORD dst_location)
3798 const RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3799 DWORD src_location;
3801 if (surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE)
3802 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
3803 else if (surface->locations & WINED3D_LOCATION_RB_RESOLVED)
3804 src_location = WINED3D_LOCATION_RB_RESOLVED;
3805 else if (surface->locations & WINED3D_LOCATION_TEXTURE_SRGB)
3806 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
3807 else /* surface_blt_fbo will load the source location if necessary. */
3808 src_location = WINED3D_LOCATION_TEXTURE_RGB;
3810 surface_blt_fbo(surface->resource.device, context, WINED3D_TEXF_POINT,
3811 surface, src_location, &rect, surface, dst_location, &rect);
3814 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
3815 HRESULT surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3817 HRESULT hr;
3819 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3821 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3823 if (location == WINED3D_LOCATION_TEXTURE_RGB
3824 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
3826 surface_load_ds_location(surface, context, location);
3827 return WINED3D_OK;
3829 else if (location & surface->locations
3830 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
3832 /* Already up to date, nothing to do. */
3833 return WINED3D_OK;
3835 else
3837 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
3838 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
3839 return WINED3DERR_INVALIDCALL;
3843 if (surface->locations & location)
3845 TRACE("Location already up to date.\n");
3846 return WINED3D_OK;
3849 if (WARN_ON(d3d_surface))
3851 DWORD required_access = resource_access_from_location(location);
3852 if ((surface->resource.access_flags & required_access) != required_access)
3853 WARN("Operation requires %#x access, but surface only has %#x.\n",
3854 required_access, surface->resource.access_flags);
3857 if (!surface->locations)
3859 ERR("Surface %p does not have any up to date location.\n", surface);
3860 return WINED3DERR_INVALIDCALL;
3863 switch (location)
3865 case WINED3D_LOCATION_DIB:
3866 case WINED3D_LOCATION_USER_MEMORY:
3867 case WINED3D_LOCATION_SYSMEM:
3868 case WINED3D_LOCATION_BUFFER:
3869 surface_load_sysmem(surface, context, location);
3870 break;
3872 case WINED3D_LOCATION_DRAWABLE:
3873 if (FAILED(hr = surface_load_drawable(surface, context)))
3874 return hr;
3875 break;
3877 case WINED3D_LOCATION_RB_RESOLVED:
3878 case WINED3D_LOCATION_RB_MULTISAMPLE:
3879 surface_load_renderbuffer(surface, context, location);
3880 break;
3882 case WINED3D_LOCATION_TEXTURE_RGB:
3883 case WINED3D_LOCATION_TEXTURE_SRGB:
3884 if (FAILED(hr = surface_load_texture(surface, context,
3885 location == WINED3D_LOCATION_TEXTURE_SRGB)))
3886 return hr;
3887 break;
3889 default:
3890 ERR("Don't know how to handle location %#x.\n", location);
3891 break;
3894 surface_validate_location(surface, location);
3896 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
3897 surface_evict_sysmem(surface);
3899 return WINED3D_OK;
3902 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
3903 /* Context activation is done by the caller. */
3904 static void ffp_blit_free(struct wined3d_device *device) { }
3906 /* Context activation is done by the caller. */
3907 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
3908 const struct wined3d_color_key *color_key)
3910 const struct wined3d_gl_info *gl_info = context->gl_info;
3912 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
3913 checkGLcall("glEnable(target)");
3915 return WINED3D_OK;
3918 /* Context activation is done by the caller. */
3919 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
3921 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
3922 checkGLcall("glDisable(GL_TEXTURE_2D)");
3923 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
3925 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3926 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3928 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
3930 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
3931 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
3935 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
3936 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
3937 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
3938 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
3940 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
3942 TRACE("Source or destination is in system memory.\n");
3943 return FALSE;
3946 switch (blit_op)
3948 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
3949 if (d3d_info->shader_color_key)
3951 TRACE("Color keying requires converted textures.\n");
3952 return FALSE;
3954 case WINED3D_BLIT_OP_COLOR_BLIT:
3955 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
3956 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
3958 TRACE("Checking support for fixup:\n");
3959 dump_color_fixup_desc(src_format->color_fixup);
3962 /* We only support identity conversions. */
3963 if (!is_identity_fixup(src_format->color_fixup)
3964 || !is_identity_fixup(dst_format->color_fixup))
3966 TRACE("Fixups are not supported.\n");
3967 return FALSE;
3970 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
3972 TRACE("Can only blit to render targets.\n");
3973 return FALSE;
3975 return TRUE;
3977 case WINED3D_BLIT_OP_COLOR_FILL:
3978 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
3980 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
3981 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
3982 return FALSE;
3984 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
3986 TRACE("Color fill not supported\n");
3987 return FALSE;
3990 /* FIXME: We should reject color fills on formats with fixups,
3991 * but this would break P8 color fills for example. */
3993 return TRUE;
3995 case WINED3D_BLIT_OP_DEPTH_FILL:
3996 return TRUE;
3998 default:
3999 TRACE("Unsupported blit_op=%d\n", blit_op);
4000 return FALSE;
4004 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
4005 const RECT *rect, const struct wined3d_color *color)
4007 const RECT draw_rect = {0, 0, view->width, view->height};
4008 struct wined3d_fb_state fb = {&view, NULL};
4010 device_clear_render_targets(device, 1, &fb, 1, rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4012 return WINED3D_OK;
4015 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
4016 struct wined3d_rendertarget_view *view, const RECT *rect, float depth)
4018 const RECT draw_rect = {0, 0, view->width, view->height};
4019 struct wined3d_fb_state fb = {NULL, view};
4021 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4023 return WINED3D_OK;
4026 static void ffp_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
4027 struct wined3d_surface *src_surface, const RECT *src_rect,
4028 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4029 const struct wined3d_color_key *color_key)
4031 struct wined3d_context *context;
4033 /* Blit from offscreen surface to render target */
4034 struct wined3d_color_key old_blt_key = src_surface->container->async.src_blt_color_key;
4035 DWORD old_color_key_flags = src_surface->container->async.color_key_flags;
4037 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4039 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT, color_key);
4041 context = context_acquire(device, dst_surface);
4043 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
4044 glEnable(GL_ALPHA_TEST);
4046 surface_blt_to_drawable(device, context, filter,
4047 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
4049 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
4050 glDisable(GL_ALPHA_TEST);
4052 context_release(context);
4054 /* Restore the color key parameters */
4055 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT,
4056 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
4058 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
4059 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
4062 const struct blit_shader ffp_blit = {
4063 ffp_blit_alloc,
4064 ffp_blit_free,
4065 ffp_blit_set,
4066 ffp_blit_unset,
4067 ffp_blit_supported,
4068 ffp_blit_color_fill,
4069 ffp_blit_depth_fill,
4070 ffp_blit_blit_surface,
4073 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4075 return WINED3D_OK;
4078 /* Context activation is done by the caller. */
4079 static void cpu_blit_free(struct wined3d_device *device)
4083 /* Context activation is done by the caller. */
4084 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4085 const struct wined3d_color_key *color_key)
4087 return WINED3D_OK;
4090 /* Context activation is done by the caller. */
4091 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4095 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
4096 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4097 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4098 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4100 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4102 return TRUE;
4105 return FALSE;
4108 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4109 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4110 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
4112 UINT row_block_count;
4113 const BYTE *src_row;
4114 BYTE *dst_row;
4115 UINT x, y;
4117 src_row = src_data;
4118 dst_row = dst_data;
4120 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4122 if (!flags)
4124 for (y = 0; y < update_h; y += format->block_height)
4126 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4127 src_row += src_pitch;
4128 dst_row += dst_pitch;
4131 return WINED3D_OK;
4134 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
4136 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4138 switch (format->id)
4140 case WINED3DFMT_DXT1:
4141 for (y = 0; y < update_h; y += format->block_height)
4143 struct block
4145 WORD color[2];
4146 BYTE control_row[4];
4149 const struct block *s = (const struct block *)src_row;
4150 struct block *d = (struct block *)dst_row;
4152 for (x = 0; x < row_block_count; ++x)
4154 d[x].color[0] = s[x].color[0];
4155 d[x].color[1] = s[x].color[1];
4156 d[x].control_row[0] = s[x].control_row[3];
4157 d[x].control_row[1] = s[x].control_row[2];
4158 d[x].control_row[2] = s[x].control_row[1];
4159 d[x].control_row[3] = s[x].control_row[0];
4161 src_row -= src_pitch;
4162 dst_row += dst_pitch;
4164 return WINED3D_OK;
4166 case WINED3DFMT_DXT2:
4167 case WINED3DFMT_DXT3:
4168 for (y = 0; y < update_h; y += format->block_height)
4170 struct block
4172 WORD alpha_row[4];
4173 WORD color[2];
4174 BYTE control_row[4];
4177 const struct block *s = (const struct block *)src_row;
4178 struct block *d = (struct block *)dst_row;
4180 for (x = 0; x < row_block_count; ++x)
4182 d[x].alpha_row[0] = s[x].alpha_row[3];
4183 d[x].alpha_row[1] = s[x].alpha_row[2];
4184 d[x].alpha_row[2] = s[x].alpha_row[1];
4185 d[x].alpha_row[3] = s[x].alpha_row[0];
4186 d[x].color[0] = s[x].color[0];
4187 d[x].color[1] = s[x].color[1];
4188 d[x].control_row[0] = s[x].control_row[3];
4189 d[x].control_row[1] = s[x].control_row[2];
4190 d[x].control_row[2] = s[x].control_row[1];
4191 d[x].control_row[3] = s[x].control_row[0];
4193 src_row -= src_pitch;
4194 dst_row += dst_pitch;
4196 return WINED3D_OK;
4198 default:
4199 FIXME("Compressed flip not implemented for format %s.\n",
4200 debug_d3dformat(format->id));
4201 return E_NOTIMPL;
4205 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
4206 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
4208 return E_NOTIMPL;
4211 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4212 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4213 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
4215 const struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
4216 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
4217 const struct wined3d_format *src_format, *dst_format;
4218 unsigned int src_fmt_flags, dst_fmt_flags;
4219 struct wined3d_texture *src_texture = NULL;
4220 struct wined3d_map_desc dst_map, src_map;
4221 const BYTE *sbase = NULL;
4222 HRESULT hr = WINED3D_OK;
4223 const BYTE *sbuf;
4224 BYTE *dbuf;
4225 int x, y;
4227 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4228 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4229 flags, fx, debug_d3dtexturefiltertype(filter));
4231 if (src_surface == dst_surface)
4233 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
4234 src_map = dst_map;
4235 src_format = dst_surface->resource.format;
4236 dst_format = src_format;
4237 dst_fmt_flags = dst_surface->container->resource.format_flags;
4238 src_fmt_flags = dst_fmt_flags;
4240 else
4242 dst_format = dst_surface->resource.format;
4243 dst_fmt_flags = dst_surface->container->resource.format_flags;
4244 if (src_surface)
4246 if (dst_surface->resource.format->id != src_surface->resource.format->id)
4248 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
4250 /* The conv function writes a FIXME */
4251 WARN("Cannot convert source surface format to dest format.\n");
4252 goto release;
4254 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
4256 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
4257 src_format = src_surface->resource.format;
4258 src_fmt_flags = src_surface->container->resource.format_flags;
4260 else
4262 src_format = dst_format;
4263 src_fmt_flags = dst_fmt_flags;
4266 wined3d_surface_map(dst_surface, &dst_map, &dst_box, 0);
4269 bpp = dst_surface->resource.format->byte_count;
4270 srcheight = src_rect->bottom - src_rect->top;
4271 srcwidth = src_rect->right - src_rect->left;
4272 dstheight = dst_rect->bottom - dst_rect->top;
4273 dstwidth = dst_rect->right - dst_rect->left;
4274 width = (dst_rect->right - dst_rect->left) * bpp;
4276 if (src_surface)
4277 sbase = (BYTE *)src_map.data
4278 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
4279 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
4280 if (src_surface != dst_surface)
4281 dbuf = dst_map.data;
4282 else
4283 dbuf = (BYTE *)dst_map.data
4284 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
4285 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
4287 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
4289 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
4291 if (src_surface == dst_surface)
4293 FIXME("Only plain blits supported on compressed surfaces.\n");
4294 hr = E_NOTIMPL;
4295 goto release;
4298 if (srcheight != dstheight || srcwidth != dstwidth)
4300 WARN("Stretching not supported on compressed surfaces.\n");
4301 hr = WINED3DERR_INVALIDCALL;
4302 goto release;
4305 if (!surface_check_block_align_rect(src_surface, src_rect))
4307 WARN("Source rectangle not block-aligned.\n");
4308 hr = WINED3DERR_INVALIDCALL;
4309 goto release;
4312 if (!surface_check_block_align_rect(dst_surface, dst_rect))
4314 WARN("Destination rectangle not block-aligned.\n");
4315 hr = WINED3DERR_INVALIDCALL;
4316 goto release;
4319 hr = surface_cpu_blt_compressed(sbase, dbuf,
4320 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
4321 src_format, flags, fx);
4322 goto release;
4325 /* First, all the 'source-less' blits */
4326 if (flags & WINED3D_BLT_COLOR_FILL)
4328 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->fill_color);
4329 flags &= ~WINED3D_BLT_COLOR_FILL;
4332 if (flags & WINED3D_BLT_DEPTH_FILL)
4333 FIXME("WINED3D_BLT_DEPTH_FILL needs to be implemented!\n");
4335 /* Now the 'with source' blits. */
4336 if (src_surface)
4338 int sx, xinc, sy, yinc;
4340 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
4341 goto release;
4343 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
4344 && (srcwidth != dstwidth || srcheight != dstheight))
4346 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
4347 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
4350 xinc = (srcwidth << 16) / dstwidth;
4351 yinc = (srcheight << 16) / dstheight;
4353 if (!flags)
4355 /* No effects, we can cheat here. */
4356 if (dstwidth == srcwidth)
4358 if (dstheight == srcheight)
4360 /* No stretching in either direction. This needs to be as
4361 * fast as possible. */
4362 sbuf = sbase;
4364 /* Check for overlapping surfaces. */
4365 if (src_surface != dst_surface || dst_rect->top < src_rect->top
4366 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
4368 /* No overlap, or dst above src, so copy from top downwards. */
4369 for (y = 0; y < dstheight; ++y)
4371 memcpy(dbuf, sbuf, width);
4372 sbuf += src_map.row_pitch;
4373 dbuf += dst_map.row_pitch;
4376 else if (dst_rect->top > src_rect->top)
4378 /* Copy from bottom upwards. */
4379 sbuf += src_map.row_pitch * dstheight;
4380 dbuf += dst_map.row_pitch * dstheight;
4381 for (y = 0; y < dstheight; ++y)
4383 sbuf -= src_map.row_pitch;
4384 dbuf -= dst_map.row_pitch;
4385 memcpy(dbuf, sbuf, width);
4388 else
4390 /* Src and dst overlapping on the same line, use memmove. */
4391 for (y = 0; y < dstheight; ++y)
4393 memmove(dbuf, sbuf, width);
4394 sbuf += src_map.row_pitch;
4395 dbuf += dst_map.row_pitch;
4399 else
4401 /* Stretching in y direction only. */
4402 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4404 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4405 memcpy(dbuf, sbuf, width);
4406 dbuf += dst_map.row_pitch;
4410 else
4412 /* Stretching in X direction. */
4413 int last_sy = -1;
4414 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4416 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4418 if ((sy >> 16) == (last_sy >> 16))
4420 /* This source row is the same as last source row -
4421 * Copy the already stretched row. */
4422 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
4424 else
4426 #define STRETCH_ROW(type) \
4427 do { \
4428 const type *s = (const type *)sbuf; \
4429 type *d = (type *)dbuf; \
4430 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4431 d[x] = s[sx >> 16]; \
4432 } while(0)
4434 switch(bpp)
4436 case 1:
4437 STRETCH_ROW(BYTE);
4438 break;
4439 case 2:
4440 STRETCH_ROW(WORD);
4441 break;
4442 case 4:
4443 STRETCH_ROW(DWORD);
4444 break;
4445 case 3:
4447 const BYTE *s;
4448 BYTE *d = dbuf;
4449 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
4451 DWORD pixel;
4453 s = sbuf + 3 * (sx >> 16);
4454 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4455 d[0] = (pixel ) & 0xff;
4456 d[1] = (pixel >> 8) & 0xff;
4457 d[2] = (pixel >> 16) & 0xff;
4458 d += 3;
4460 break;
4462 default:
4463 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
4464 hr = WINED3DERR_NOTAVAILABLE;
4465 goto error;
4467 #undef STRETCH_ROW
4469 dbuf += dst_map.row_pitch;
4470 last_sy = sy;
4474 else
4476 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
4477 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
4478 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
4479 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
4480 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
4482 /* The color keying flags are checked for correctness in ddraw */
4483 if (flags & WINED3D_BLT_SRC_CKEY)
4485 keylow = src_surface->container->async.src_blt_color_key.color_space_low_value;
4486 keyhigh = src_surface->container->async.src_blt_color_key.color_space_high_value;
4488 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
4490 keylow = fx->src_color_key.color_space_low_value;
4491 keyhigh = fx->src_color_key.color_space_high_value;
4494 if (flags & WINED3D_BLT_DST_CKEY)
4496 /* Destination color keys are taken from the source surface! */
4497 destkeylow = src_surface->container->async.dst_blt_color_key.color_space_low_value;
4498 destkeyhigh = src_surface->container->async.dst_blt_color_key.color_space_high_value;
4500 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
4502 destkeylow = fx->dst_color_key.color_space_low_value;
4503 destkeyhigh = fx->dst_color_key.color_space_high_value;
4506 if (bpp == 1)
4508 keymask = 0xff;
4510 else
4512 DWORD masks[3];
4513 get_color_masks(src_format, masks);
4514 keymask = masks[0]
4515 | masks[1]
4516 | masks[2];
4518 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
4519 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
4522 if (flags & WINED3D_BLT_FX)
4524 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
4525 LONG tmpxy;
4526 dTopLeft = dbuf;
4527 dTopRight = dbuf + ((dstwidth - 1) * bpp);
4528 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
4529 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
4531 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
4533 /* I don't think we need to do anything about this flag */
4534 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
4536 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
4538 tmp = dTopRight;
4539 dTopRight = dTopLeft;
4540 dTopLeft = tmp;
4541 tmp = dBottomRight;
4542 dBottomRight = dBottomLeft;
4543 dBottomLeft = tmp;
4544 dstxinc = dstxinc * -1;
4546 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
4548 tmp = dTopLeft;
4549 dTopLeft = dBottomLeft;
4550 dBottomLeft = tmp;
4551 tmp = dTopRight;
4552 dTopRight = dBottomRight;
4553 dBottomRight = tmp;
4554 dstyinc = dstyinc * -1;
4556 if (fx->fx & WINEDDBLTFX_NOTEARING)
4558 /* I don't think we need to do anything about this flag */
4559 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
4561 if (fx->fx & WINEDDBLTFX_ROTATE180)
4563 tmp = dBottomRight;
4564 dBottomRight = dTopLeft;
4565 dTopLeft = tmp;
4566 tmp = dBottomLeft;
4567 dBottomLeft = dTopRight;
4568 dTopRight = tmp;
4569 dstxinc = dstxinc * -1;
4570 dstyinc = dstyinc * -1;
4572 if (fx->fx & WINEDDBLTFX_ROTATE270)
4574 tmp = dTopLeft;
4575 dTopLeft = dBottomLeft;
4576 dBottomLeft = dBottomRight;
4577 dBottomRight = dTopRight;
4578 dTopRight = tmp;
4579 tmpxy = dstxinc;
4580 dstxinc = dstyinc;
4581 dstyinc = tmpxy;
4582 dstxinc = dstxinc * -1;
4584 if (fx->fx & WINEDDBLTFX_ROTATE90)
4586 tmp = dTopLeft;
4587 dTopLeft = dTopRight;
4588 dTopRight = dBottomRight;
4589 dBottomRight = dBottomLeft;
4590 dBottomLeft = tmp;
4591 tmpxy = dstxinc;
4592 dstxinc = dstyinc;
4593 dstyinc = tmpxy;
4594 dstyinc = dstyinc * -1;
4596 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
4598 /* I don't think we need to do anything about this flag */
4599 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
4601 dbuf = dTopLeft;
4602 flags &= ~(WINED3D_BLT_FX);
4605 #define COPY_COLORKEY_FX(type) \
4606 do { \
4607 const type *s; \
4608 type *d = (type *)dbuf, *dx, tmp; \
4609 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
4611 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
4612 dx = d; \
4613 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4615 tmp = s[sx >> 16]; \
4616 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
4617 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
4619 dx[0] = tmp; \
4621 dx = (type *)(((BYTE *)dx) + dstxinc); \
4623 d = (type *)(((BYTE *)d) + dstyinc); \
4625 } while(0)
4627 switch (bpp)
4629 case 1:
4630 COPY_COLORKEY_FX(BYTE);
4631 break;
4632 case 2:
4633 COPY_COLORKEY_FX(WORD);
4634 break;
4635 case 4:
4636 COPY_COLORKEY_FX(DWORD);
4637 break;
4638 case 3:
4640 const BYTE *s;
4641 BYTE *d = dbuf, *dx;
4642 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4644 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4645 dx = d;
4646 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
4648 DWORD pixel, dpixel = 0;
4649 s = sbuf + 3 * (sx>>16);
4650 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4651 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
4652 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
4653 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
4655 dx[0] = (pixel ) & 0xff;
4656 dx[1] = (pixel >> 8) & 0xff;
4657 dx[2] = (pixel >> 16) & 0xff;
4659 dx += dstxinc;
4661 d += dstyinc;
4663 break;
4665 default:
4666 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
4667 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
4668 hr = WINED3DERR_NOTAVAILABLE;
4669 goto error;
4670 #undef COPY_COLORKEY_FX
4675 error:
4676 if (flags && FIXME_ON(d3d_surface))
4678 FIXME("\tUnsupported flags: %#x.\n", flags);
4681 release:
4682 wined3d_surface_unmap(dst_surface);
4683 if (src_surface && src_surface != dst_surface)
4684 wined3d_surface_unmap(src_surface);
4685 /* Release the converted surface, if any. */
4686 if (src_texture)
4687 wined3d_texture_decref(src_texture);
4689 return hr;
4692 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
4693 const RECT *rect, const struct wined3d_color *color)
4695 struct wined3d_surface *surface = wined3d_rendertarget_view_get_surface(view);
4696 static const RECT src_rect;
4697 struct wined3d_blt_fx fx;
4699 fx.fill_color = wined3d_format_convert_from_float(surface, color);
4700 return surface_cpu_blt(surface, rect, NULL, &src_rect,
4701 WINED3D_BLT_COLOR_FILL, &fx, WINED3D_TEXF_POINT);
4704 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
4705 struct wined3d_rendertarget_view *view, const RECT *rect, float depth)
4707 FIXME("Depth filling not implemented by cpu_blit.\n");
4708 return WINED3DERR_INVALIDCALL;
4711 static void cpu_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
4712 struct wined3d_surface *src_surface, const RECT *src_rect,
4713 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4714 const struct wined3d_color_key *color_key)
4716 /* FIXME: Remove error returns from surface_blt_cpu. */
4717 ERR("Blit method not implemented by cpu_blit.\n");
4720 const struct blit_shader cpu_blit = {
4721 cpu_blit_alloc,
4722 cpu_blit_free,
4723 cpu_blit_set,
4724 cpu_blit_unset,
4725 cpu_blit_supported,
4726 cpu_blit_color_fill,
4727 cpu_blit_depth_fill,
4728 cpu_blit_blit_surface,
4731 HRESULT wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4732 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4733 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
4735 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
4736 struct wined3d_device *device = dst_surface->resource.device;
4737 DWORD src_ds_flags, dst_ds_flags;
4738 BOOL scale, convert;
4740 static const DWORD simple_blit = WINED3D_BLT_ASYNC
4741 | WINED3D_BLT_COLOR_FILL
4742 | WINED3D_BLT_SRC_CKEY
4743 | WINED3D_BLT_SRC_CKEY_OVERRIDE
4744 | WINED3D_BLT_WAIT
4745 | WINED3D_BLT_DEPTH_FILL
4746 | WINED3D_BLT_DO_NOT_WAIT
4747 | WINED3D_BLT_ALPHA_TEST;
4749 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4750 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4751 flags, fx, debug_d3dtexturefiltertype(filter));
4752 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
4754 if (fx)
4756 TRACE("fx %#x.\n", fx->fx);
4757 TRACE("fill_color 0x%08x.\n", fx->fill_color);
4758 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
4759 fx->dst_color_key.color_space_low_value,
4760 fx->dst_color_key.color_space_high_value);
4761 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
4762 fx->src_color_key.color_space_low_value,
4763 fx->src_color_key.color_space_high_value);
4766 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
4768 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
4769 return WINEDDERR_SURFACEBUSY;
4772 if (dst_rect->left >= dst_rect->right || dst_rect->top >= dst_rect->bottom
4773 || dst_rect->left > dst_surface->resource.width || dst_rect->left < 0
4774 || dst_rect->top > dst_surface->resource.height || dst_rect->top < 0
4775 || dst_rect->right > dst_surface->resource.width || dst_rect->right < 0
4776 || dst_rect->bottom > dst_surface->resource.height || dst_rect->bottom < 0)
4778 WARN("The application gave us a bad destination rectangle.\n");
4779 return WINEDDERR_INVALIDRECT;
4782 if (src_surface)
4784 if (src_rect->left >= src_rect->right || src_rect->top >= src_rect->bottom
4785 || src_rect->left > src_surface->resource.width || src_rect->left < 0
4786 || src_rect->top > src_surface->resource.height || src_rect->top < 0
4787 || src_rect->right > src_surface->resource.width || src_rect->right < 0
4788 || src_rect->bottom > src_surface->resource.height || src_rect->bottom < 0)
4790 WARN("The application gave us a bad source rectangle.\n");
4791 return WINEDDERR_INVALIDRECT;
4795 if (!fx || !(fx->fx))
4796 flags &= ~WINED3D_BLT_FX;
4798 if (flags & WINED3D_BLT_WAIT)
4799 flags &= ~WINED3D_BLT_WAIT;
4801 if (flags & WINED3D_BLT_ASYNC)
4803 static unsigned int once;
4805 if (!once++)
4806 FIXME("Can't handle WINED3D_BLT_ASYNC flag.\n");
4807 flags &= ~WINED3D_BLT_ASYNC;
4810 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
4811 if (flags & WINED3D_BLT_DO_NOT_WAIT)
4813 static unsigned int once;
4815 if (!once++)
4816 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
4817 flags &= ~WINED3D_BLT_DO_NOT_WAIT;
4820 if (!device->d3d_initialized)
4822 WARN("D3D not initialized, using fallback.\n");
4823 goto cpu;
4826 /* We want to avoid invalidating the sysmem location for converted
4827 * surfaces, since otherwise we'd have to convert the data back when
4828 * locking them. */
4829 if (dst_surface->container->flags & WINED3D_TEXTURE_CONVERTED
4830 || dst_surface->container->resource.format->convert
4831 || wined3d_format_get_color_key_conversion(dst_surface->container, TRUE))
4833 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
4834 goto cpu;
4837 if (flags & ~simple_blit)
4839 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
4840 goto fallback;
4843 if (src_surface)
4844 src_swapchain = src_surface->container->swapchain;
4845 else
4846 src_swapchain = NULL;
4848 dst_swapchain = dst_surface->container->swapchain;
4850 /* This isn't strictly needed. FBO blits for example could deal with
4851 * cross-swapchain blits by first downloading the source to a texture
4852 * before switching to the destination context. We just have this here to
4853 * not have to deal with the issue, since cross-swapchain blits should be
4854 * rare. */
4855 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
4857 FIXME("Using fallback for cross-swapchain blit.\n");
4858 goto fallback;
4861 scale = src_surface
4862 && (src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
4863 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top);
4864 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
4866 dst_ds_flags = dst_surface->container->resource.format_flags
4867 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4868 if (src_surface)
4869 src_ds_flags = src_surface->container->resource.format_flags
4870 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4871 else
4872 src_ds_flags = 0;
4874 if (src_ds_flags || dst_ds_flags)
4876 if (flags & WINED3D_BLT_DEPTH_FILL)
4878 float depth;
4880 TRACE("Depth fill.\n");
4882 if (!surface_convert_depth_to_float(dst_surface, fx->fill_color, &depth))
4883 return WINED3DERR_INVALIDCALL;
4885 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, dst_rect, depth)))
4886 return WINED3D_OK;
4888 else
4890 if (src_ds_flags != dst_ds_flags)
4892 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
4893 return WINED3DERR_INVALIDCALL;
4896 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
4897 src_rect, dst_surface, dst_surface->container->resource.draw_binding, dst_rect)))
4898 return WINED3D_OK;
4901 else
4903 const struct blit_shader *blitter;
4905 /* In principle this would apply to depth blits as well, but we don't
4906 * implement those in the CPU blitter at the moment. */
4907 if ((dst_surface->locations & dst_surface->resource.map_binding)
4908 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
4910 if (scale)
4911 TRACE("Not doing sysmem blit because of scaling.\n");
4912 else if (convert)
4913 TRACE("Not doing sysmem blit because of format conversion.\n");
4914 else
4915 goto cpu;
4918 if (flags & WINED3D_BLT_COLOR_FILL)
4920 struct wined3d_color color;
4921 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
4923 TRACE("Color fill.\n");
4925 if (!wined3d_format_convert_color_to_float(dst_surface->resource.format,
4926 palette, fx->fill_color, &color))
4927 goto fallback;
4929 if (SUCCEEDED(surface_color_fill(dst_surface, dst_rect, &color)))
4930 return WINED3D_OK;
4932 else
4934 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
4935 const struct wined3d_color_key *color_key = NULL;
4937 TRACE("Color blit.\n");
4938 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
4940 color_key = &fx->src_color_key;
4941 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
4943 else if (flags & WINED3D_BLT_SRC_CKEY)
4945 color_key = &src_surface->container->async.src_blt_color_key;
4946 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
4948 else if (flags & WINED3D_BLT_ALPHA_TEST)
4950 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
4952 else if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
4953 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
4955 /* Upload */
4956 if (scale)
4957 TRACE("Not doing upload because of scaling.\n");
4958 else if (convert)
4959 TRACE("Not doing upload because of format conversion.\n");
4960 else
4962 POINT dst_point = {dst_rect->left, dst_rect->top};
4964 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
4966 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
4968 struct wined3d_context *context = context_acquire(device, dst_surface);
4969 surface_load_location(dst_surface, context, dst_surface->container->resource.draw_binding);
4970 context_release(context);
4972 return WINED3D_OK;
4976 else if (dst_swapchain && dst_swapchain->back_buffers
4977 && dst_surface->container == dst_swapchain->front_buffer
4978 && src_surface->container == dst_swapchain->back_buffers[0])
4980 /* Use present for back -> front blits. The idea behind this is
4981 * that present is potentially faster than a blit, in particular
4982 * when FBO blits aren't available. Some ddraw applications like
4983 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
4984 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
4985 * applications can't blit directly to the frontbuffer. */
4986 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
4988 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
4990 /* Set the swap effect to COPY, we don't want the backbuffer
4991 * to become undefined. */
4992 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
4993 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
4994 dst_swapchain->desc.swap_effect = swap_effect;
4996 return WINED3D_OK;
4999 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
5000 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5001 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5003 struct wined3d_context *context;
5004 TRACE("Using FBO blit.\n");
5006 context = context_acquire(device, NULL);
5007 surface_blt_fbo(device, context, filter,
5008 src_surface, src_surface->container->resource.draw_binding, src_rect,
5009 dst_surface, dst_surface->container->resource.draw_binding, dst_rect);
5010 context_release(context);
5012 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5013 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5015 return WINED3D_OK;
5018 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
5019 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5020 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format);
5021 if (blitter)
5023 blitter->blit_surface(device, blit_op, filter, src_surface,
5024 src_rect, dst_surface, dst_rect, color_key);
5025 return WINED3D_OK;
5030 fallback:
5031 /* Special cases for render targets. */
5032 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
5033 return WINED3D_OK;
5035 cpu:
5036 return surface_cpu_blt(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter);
5039 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5040 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5042 struct wined3d_device *device = container->resource.device;
5043 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5044 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5045 BOOL lockable = flags & WINED3D_TEXTURE_CREATE_MAPPABLE;
5046 UINT multisample_quality = desc->multisample_quality;
5047 unsigned int resource_size;
5048 HRESULT hr;
5050 /* Quick lockable sanity check.
5051 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5052 * this function is too deep to need to care about things like this.
5053 * Levels need to be checked too, since they all affect what can be done. */
5054 switch (desc->pool)
5056 case WINED3D_POOL_MANAGED:
5057 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5058 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5059 break;
5061 case WINED3D_POOL_DEFAULT:
5062 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5063 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5064 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5065 break;
5067 case WINED3D_POOL_SCRATCH:
5068 case WINED3D_POOL_SYSTEM_MEM:
5069 break;
5071 default:
5072 FIXME("Unknown pool %#x.\n", desc->pool);
5073 break;
5076 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5077 FIXME("Trying to create a render target that isn't in the default pool.\n");
5079 /* FIXME: Check that the format is supported by the device. */
5081 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5082 if (!resource_size)
5083 return WINED3DERR_INVALIDCALL;
5085 if (device->wined3d->flags & WINED3D_NO3D)
5086 surface->surface_ops = &gdi_surface_ops;
5087 else
5088 surface->surface_ops = &surface_ops;
5090 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE,
5091 format, desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height,
5092 1, resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5094 WARN("Failed to initialize resource, returning %#x.\n", hr);
5095 return hr;
5098 surface->container = container;
5099 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5100 list_init(&surface->renderbuffers);
5101 list_init(&surface->overlays);
5103 /* Flags */
5104 if (flags & WINED3D_TEXTURE_CREATE_DISCARD)
5105 surface->flags |= SFLAG_DISCARD;
5106 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5107 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5109 surface->texture_target = target;
5110 surface->texture_level = level;
5111 surface->texture_layer = layer;
5113 /* Call the private setup routine */
5114 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5116 ERR("Private setup failed, hr %#x.\n", hr);
5117 surface_cleanup(surface);
5118 return hr;
5121 /* Similar to lockable rendertargets above, creating the DIB section
5122 * during surface initialization prevents the sysmem pointer from changing
5123 * after a wined3d_texture_get_dc() call. */
5124 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5125 && SUCCEEDED(surface_create_dib_section(surface)))
5126 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5128 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5130 wined3d_resource_free_sysmem(&surface->resource);
5131 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5132 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5135 return hr;
5138 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5139 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
5141 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
5142 const struct wined3d_parent_ops *parent_ops;
5143 struct wined3d_surface *object;
5144 void *parent;
5145 HRESULT hr;
5147 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
5148 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
5149 container, desc->width, desc->height, debug_d3dformat(desc->format),
5150 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
5151 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
5153 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
5154 return E_OUTOFMEMORY;
5156 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
5158 WARN("Failed to initialize surface, returning %#x.\n", hr);
5159 HeapFree(GetProcessHeap(), 0, object);
5160 return hr;
5163 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
5164 container, layer * container->level_count + level, &parent, &parent_ops)))
5166 WARN("Failed to create surface parent, hr %#x.\n", hr);
5167 wined3d_surface_destroy(object);
5168 return hr;
5171 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
5173 object->resource.parent = parent;
5174 object->resource.parent_ops = parent_ops;
5175 *surface = object;
5177 return hr;
5180 /* Context activation is done by the caller. */
5181 void wined3d_surface_prepare(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5183 switch (location)
5185 case WINED3D_LOCATION_TEXTURE_RGB:
5186 wined3d_texture_prepare_texture(surface->container, context, FALSE);
5187 break;
5189 case WINED3D_LOCATION_TEXTURE_SRGB:
5190 wined3d_texture_prepare_texture(surface->container, context, TRUE);
5191 break;
5193 case WINED3D_LOCATION_RB_MULTISAMPLE:
5194 surface_prepare_rb(surface, context->gl_info, TRUE);
5195 break;
5197 case WINED3D_LOCATION_RB_RESOLVED:
5198 surface_prepare_rb(surface, context->gl_info, FALSE);
5199 break;