ddraw: Use wined3d_texture_blt() in ddraw_surface_update_frontbuffer().
[wine.git] / dlls / wined3d / surface.c
blob86e529007e7d598f5ecaa1179cf44a98d254d2dc
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2011, 2013-2014 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d);
37 #define MAXLOCKCOUNT 50 /* After this amount of locks do not free the sysmem copy. */
39 static const DWORD surface_simple_locations =
40 WINED3D_LOCATION_SYSMEM | WINED3D_LOCATION_USER_MEMORY
41 | WINED3D_LOCATION_DIB | WINED3D_LOCATION_BUFFER;
43 static void surface_cleanup(struct wined3d_surface *surface)
45 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->pbo || surface->rb_multisample
50 || surface->rb_resolved || !list_empty(&surface->renderbuffers))
52 struct wined3d_renderbuffer_entry *entry, *entry2;
53 const struct wined3d_gl_info *gl_info;
54 struct wined3d_context *context;
56 context = context_acquire(surface->resource.device, NULL);
57 gl_info = context->gl_info;
59 if (surface->pbo)
61 TRACE("Deleting PBO %u.\n", surface->pbo);
62 GL_EXTCALL(glDeleteBuffers(1, &surface->pbo));
65 if (surface->rb_multisample)
67 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
68 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
71 if (surface->rb_resolved)
73 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
74 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
77 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
79 TRACE("Deleting renderbuffer %u.\n", entry->id);
80 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
81 HeapFree(GetProcessHeap(), 0, entry);
84 context_release(context);
87 if (surface->flags & SFLAG_DIBSECTION)
89 DeleteDC(surface->hDC);
90 DeleteObject(surface->dib.DIBsection);
91 surface->dib.bitmap_data = NULL;
94 if (surface->overlay_dest)
95 list_remove(&surface->overlay_entry);
97 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
99 list_remove(&overlay->overlay_entry);
100 overlay->overlay_dest = NULL;
103 resource_cleanup(&surface->resource);
106 void wined3d_surface_destroy(struct wined3d_surface *surface)
108 TRACE("surface %p.\n", surface);
110 surface_cleanup(surface);
111 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
112 HeapFree(GetProcessHeap(), 0, surface);
115 void surface_get_drawable_size(const struct wined3d_surface *surface, const struct wined3d_context *context,
116 unsigned int *width, unsigned int *height)
118 if (surface->container->swapchain)
120 /* The drawable size of an onscreen drawable is the surface size.
121 * (Actually: The window size, but the surface is created in window
122 * size.) */
123 *width = context->current_rt->resource.width;
124 *height = context->current_rt->resource.height;
126 else if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER)
128 const struct wined3d_swapchain *swapchain = context->swapchain;
130 /* The drawable size of a backbuffer / aux buffer offscreen target is
131 * the size of the current context's drawable, which is the size of
132 * the back buffer of the swapchain the active context belongs to. */
133 *width = swapchain->desc.backbuffer_width;
134 *height = swapchain->desc.backbuffer_height;
136 else
138 /* The drawable size of an FBO target is the OpenGL texture size,
139 * which is the power of two size. */
140 *width = context->current_rt->pow2Width;
141 *height = context->current_rt->pow2Height;
145 struct blt_info
147 GLenum binding;
148 GLenum bind_target;
149 enum wined3d_gl_resource_type tex_type;
150 GLfloat coords[4][3];
153 struct float_rect
155 float l;
156 float t;
157 float r;
158 float b;
161 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
163 f->l = ((r->left * 2.0f) / w) - 1.0f;
164 f->t = ((r->top * 2.0f) / h) - 1.0f;
165 f->r = ((r->right * 2.0f) / w) - 1.0f;
166 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
169 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
171 GLfloat (*coords)[3] = info->coords;
172 struct float_rect f;
174 switch (target)
176 default:
177 FIXME("Unsupported texture target %#x\n", target);
178 /* Fall back to GL_TEXTURE_2D */
179 case GL_TEXTURE_2D:
180 info->binding = GL_TEXTURE_BINDING_2D;
181 info->bind_target = GL_TEXTURE_2D;
182 info->tex_type = WINED3D_GL_RES_TYPE_TEX_2D;
183 coords[0][0] = (float)rect->left / w;
184 coords[0][1] = (float)rect->top / h;
185 coords[0][2] = 0.0f;
187 coords[1][0] = (float)rect->right / w;
188 coords[1][1] = (float)rect->top / h;
189 coords[1][2] = 0.0f;
191 coords[2][0] = (float)rect->left / w;
192 coords[2][1] = (float)rect->bottom / h;
193 coords[2][2] = 0.0f;
195 coords[3][0] = (float)rect->right / w;
196 coords[3][1] = (float)rect->bottom / h;
197 coords[3][2] = 0.0f;
198 break;
200 case GL_TEXTURE_RECTANGLE_ARB:
201 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
202 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
203 info->tex_type = WINED3D_GL_RES_TYPE_TEX_RECT;
204 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
205 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
206 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
207 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
208 break;
210 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
211 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
212 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
213 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
214 cube_coords_float(rect, w, h, &f);
216 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
217 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
218 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
219 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
220 break;
222 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
223 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
224 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
225 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
226 cube_coords_float(rect, w, h, &f);
228 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
229 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
230 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
231 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
232 break;
234 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
235 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
236 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
237 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
238 cube_coords_float(rect, w, h, &f);
240 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
241 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
242 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
243 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
244 break;
246 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
247 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
248 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
249 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
250 cube_coords_float(rect, w, h, &f);
252 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
253 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
254 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
255 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
256 break;
258 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
259 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
260 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
261 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
262 cube_coords_float(rect, w, h, &f);
264 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
265 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
266 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
267 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
268 break;
270 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
271 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
272 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
273 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
274 cube_coords_float(rect, w, h, &f);
276 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
277 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
278 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
279 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
280 break;
284 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
286 if (rect_in)
287 *rect_out = *rect_in;
288 else
290 rect_out->left = 0;
291 rect_out->top = 0;
292 rect_out->right = surface->resource.width;
293 rect_out->bottom = surface->resource.height;
297 /* Context activation is done by the caller. */
298 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
299 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
301 const struct wined3d_gl_info *gl_info = context->gl_info;
302 struct wined3d_texture *texture = src_surface->container;
303 struct blt_info info;
305 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
307 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
308 checkGLcall("glEnable(bind_target)");
310 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
312 /* Filtering for StretchRect */
313 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
314 checkGLcall("glTexParameteri");
315 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
316 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
317 checkGLcall("glTexParameteri");
318 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
319 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
320 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
321 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
322 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
323 checkGLcall("glTexEnvi");
325 /* Draw a quad */
326 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
327 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
328 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
330 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
331 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
333 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
334 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
336 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
337 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
338 gl_info->gl_ops.gl.p_glEnd();
340 /* Unbind the texture */
341 context_bind_texture(context, info.bind_target, 0);
343 /* We changed the filtering settings on the texture. Inform the
344 * container about this to get the filters reset properly next draw. */
345 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
346 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
347 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
348 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
351 /* Works correctly only for <= 4 bpp formats. */
352 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
354 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
355 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
356 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
359 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
361 const struct wined3d_format *format = surface->resource.format;
362 unsigned int format_flags = surface->container->resource.format_flags;
363 SYSTEM_INFO sysInfo;
364 BITMAPINFO *b_info;
365 int extraline = 0;
366 DWORD *masks;
368 TRACE("surface %p.\n", surface);
370 if (!(format_flags & WINED3DFMT_FLAG_GETDC))
372 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
373 return WINED3DERR_INVALIDCALL;
376 switch (format->byte_count)
378 case 2:
379 case 4:
380 /* Allocate extra space to store the RGB bit masks. */
381 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
382 break;
384 case 3:
385 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
386 break;
388 default:
389 /* Allocate extra space for a palette. */
390 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
391 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1u << (format->byte_count * 8)));
392 break;
395 if (!b_info)
396 return E_OUTOFMEMORY;
398 /* Some applications access the surface in via DWORDs, and do not take
399 * the necessary care at the end of the surface. So we need at least
400 * 4 extra bytes at the end of the surface. Check against the page size,
401 * if the last page used for the surface has at least 4 spare bytes we're
402 * safe, otherwise add an extra line to the DIB section. */
403 GetSystemInfo(&sysInfo);
404 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
406 extraline = 1;
407 TRACE("Adding an extra line to the DIB section.\n");
410 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
411 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
412 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
413 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
414 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
415 * wined3d_surface_get_pitch(surface);
416 b_info->bmiHeader.biPlanes = 1;
417 b_info->bmiHeader.biBitCount = format->byte_count * 8;
419 b_info->bmiHeader.biXPelsPerMeter = 0;
420 b_info->bmiHeader.biYPelsPerMeter = 0;
421 b_info->bmiHeader.biClrUsed = 0;
422 b_info->bmiHeader.biClrImportant = 0;
424 /* Get the bit masks */
425 masks = (DWORD *)b_info->bmiColors;
426 switch (surface->resource.format->id)
428 case WINED3DFMT_B8G8R8_UNORM:
429 b_info->bmiHeader.biCompression = BI_RGB;
430 break;
432 case WINED3DFMT_B5G5R5X1_UNORM:
433 case WINED3DFMT_B5G5R5A1_UNORM:
434 case WINED3DFMT_B4G4R4A4_UNORM:
435 case WINED3DFMT_B4G4R4X4_UNORM:
436 case WINED3DFMT_B2G3R3_UNORM:
437 case WINED3DFMT_B2G3R3A8_UNORM:
438 case WINED3DFMT_R10G10B10A2_UNORM:
439 case WINED3DFMT_R8G8B8A8_UNORM:
440 case WINED3DFMT_R8G8B8X8_UNORM:
441 case WINED3DFMT_B10G10R10A2_UNORM:
442 case WINED3DFMT_B5G6R5_UNORM:
443 case WINED3DFMT_R16G16B16A16_UNORM:
444 b_info->bmiHeader.biCompression = BI_BITFIELDS;
445 get_color_masks(format, masks);
446 break;
448 default:
449 /* Don't know palette */
450 b_info->bmiHeader.biCompression = BI_RGB;
451 break;
454 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
455 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
456 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
457 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
459 if (!surface->dib.DIBsection)
461 ERR("Failed to create DIB section.\n");
462 HeapFree(GetProcessHeap(), 0, b_info);
463 return HRESULT_FROM_WIN32(GetLastError());
466 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
467 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
469 HeapFree(GetProcessHeap(), 0, b_info);
471 /* Now allocate a DC. */
472 surface->hDC = CreateCompatibleDC(0);
473 SelectObject(surface->hDC, surface->dib.DIBsection);
475 surface->flags |= SFLAG_DIBSECTION;
477 return WINED3D_OK;
480 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
481 DWORD location)
483 if (location & WINED3D_LOCATION_BUFFER)
485 data->addr = NULL;
486 data->buffer_object = surface->pbo;
487 return;
489 if (location & WINED3D_LOCATION_USER_MEMORY)
491 data->addr = surface->user_memory;
492 data->buffer_object = 0;
493 return;
495 if (location & WINED3D_LOCATION_DIB)
497 data->addr = surface->dib.bitmap_data;
498 data->buffer_object = 0;
499 return;
501 if (location & WINED3D_LOCATION_SYSMEM)
503 data->addr = surface->resource.heap_memory;
504 data->buffer_object = 0;
505 return;
508 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
509 data->addr = NULL;
510 data->buffer_object = 0;
513 static void surface_prepare_buffer(struct wined3d_surface *surface)
515 struct wined3d_context *context;
516 GLenum error;
517 const struct wined3d_gl_info *gl_info;
519 if (surface->pbo)
520 return;
522 context = context_acquire(surface->resource.device, NULL);
523 gl_info = context->gl_info;
525 GL_EXTCALL(glGenBuffers(1, &surface->pbo));
526 error = gl_info->gl_ops.gl.p_glGetError();
527 if (!surface->pbo || error != GL_NO_ERROR)
528 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
530 TRACE("Binding PBO %u.\n", surface->pbo);
532 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
533 checkGLcall("glBindBuffer");
535 GL_EXTCALL(glBufferData(GL_PIXEL_UNPACK_BUFFER, surface->resource.size + 4,
536 NULL, GL_STREAM_DRAW));
537 checkGLcall("glBufferData");
539 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
540 checkGLcall("glBindBuffer");
542 context_release(context);
545 static void surface_prepare_system_memory(struct wined3d_surface *surface)
547 TRACE("surface %p.\n", surface);
549 if (surface->resource.heap_memory)
550 return;
552 /* Whatever surface we have, make sure that there is memory allocated
553 * for the downloaded copy, or a PBO to map. */
554 if (!wined3d_resource_allocate_sysmem(&surface->resource))
555 ERR("Failed to allocate system memory.\n");
557 if (surface->locations & WINED3D_LOCATION_SYSMEM)
558 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
561 void surface_prepare_map_memory(struct wined3d_surface *surface)
563 switch (surface->resource.map_binding)
565 case WINED3D_LOCATION_SYSMEM:
566 surface_prepare_system_memory(surface);
567 break;
569 case WINED3D_LOCATION_USER_MEMORY:
570 if (!surface->user_memory)
571 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
572 break;
574 case WINED3D_LOCATION_DIB:
575 if (!surface->dib.bitmap_data)
576 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
577 break;
579 case WINED3D_LOCATION_BUFFER:
580 surface_prepare_buffer(surface);
581 break;
583 default:
584 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
588 static void surface_evict_sysmem(struct wined3d_surface *surface)
590 /* In some conditions the surface memory must not be freed:
591 * WINED3D_TEXTURE_CONVERTED: Converting the data back would take too long
592 * WINED3D_TEXTURE_DYNAMIC_MAP: Avoid freeing the data for performance */
593 if (surface->resource.map_count || surface->container->flags & (WINED3D_TEXTURE_CONVERTED
594 | WINED3D_TEXTURE_PIN_SYSMEM | WINED3D_TEXTURE_DYNAMIC_MAP))
595 return;
597 wined3d_resource_free_sysmem(&surface->resource);
598 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
601 static BOOL surface_use_pbo(const struct wined3d_surface *surface)
603 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
604 struct wined3d_texture *texture = surface->container;
606 return texture->resource.pool == WINED3D_POOL_DEFAULT
607 && surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU
608 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
609 && !texture->resource.format->convert
610 && !(texture->flags & WINED3D_TEXTURE_PIN_SYSMEM)
611 && !(surface->flags & SFLAG_NONPOW2);
614 static HRESULT surface_private_setup(struct wined3d_surface *surface)
616 /* TODO: Check against the maximum texture sizes supported by the video card. */
617 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
618 unsigned int pow2Width, pow2Height;
620 TRACE("surface %p.\n", surface);
622 /* Non-power2 support */
623 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
624 || gl_info->supported[ARB_TEXTURE_RECTANGLE])
626 pow2Width = surface->resource.width;
627 pow2Height = surface->resource.height;
629 else
631 /* Find the nearest pow2 match */
632 pow2Width = pow2Height = 1;
633 while (pow2Width < surface->resource.width)
634 pow2Width <<= 1;
635 while (pow2Height < surface->resource.height)
636 pow2Height <<= 1;
638 surface->pow2Width = pow2Width;
639 surface->pow2Height = pow2Height;
641 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
643 /* TODO: Add support for non power two compressed textures. */
644 if (surface->container->resource.format_flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE))
646 FIXME("(%p) Compressed or height scaled non-power-two textures are not supported w(%d) h(%d)\n",
647 surface, surface->resource.width, surface->resource.height);
648 return WINED3DERR_NOTAVAILABLE;
652 if (pow2Width != surface->resource.width
653 || pow2Height != surface->resource.height)
655 surface->flags |= SFLAG_NONPOW2;
658 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
659 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
661 /* One of three options:
662 * 1: Do the same as we do with NPOT and scale the texture, (any
663 * texture ops would require the texture to be scaled which is
664 * potentially slow)
665 * 2: Set the texture to the maximum size (bad idea).
666 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
667 * 4: Create the surface, but allow it to be used only for DirectDraw
668 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
669 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
670 * the render target. */
671 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
673 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
674 return WINED3DERR_NOTAVAILABLE;
677 /* We should never use this surface in combination with OpenGL! */
678 TRACE("Creating an oversized surface: %ux%u.\n",
679 surface->pow2Width, surface->pow2Height);
682 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
683 surface->locations = WINED3D_LOCATION_DISCARDED;
685 if (surface_use_pbo(surface))
686 surface->resource.map_binding = WINED3D_LOCATION_BUFFER;
688 return WINED3D_OK;
691 static void surface_unmap(struct wined3d_surface *surface)
693 struct wined3d_device *device = surface->resource.device;
694 const struct wined3d_gl_info *gl_info;
695 struct wined3d_context *context;
697 TRACE("surface %p.\n", surface);
699 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
701 switch (surface->resource.map_binding)
703 case WINED3D_LOCATION_SYSMEM:
704 case WINED3D_LOCATION_USER_MEMORY:
705 case WINED3D_LOCATION_DIB:
706 break;
708 case WINED3D_LOCATION_BUFFER:
709 context = context_acquire(device, NULL);
710 gl_info = context->gl_info;
712 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
713 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
714 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
715 checkGLcall("glUnmapBuffer");
716 context_release(context);
717 break;
719 default:
720 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
723 if (surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_TEXTURE_RGB))
725 TRACE("Not dirtified, nothing to do.\n");
726 return;
729 if (surface->container->swapchain && surface->container->swapchain->front_buffer == surface->container)
731 context = context_acquire(device, surface);
732 surface_load_location(surface, context, surface->container->resource.draw_binding);
733 context_release(context);
735 else if (surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
736 FIXME("Depth / stencil buffer locking is not implemented.\n");
739 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
741 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
742 return FALSE;
743 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
744 return FALSE;
745 return TRUE;
748 static void surface_depth_blt_fbo(const struct wined3d_device *device,
749 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
750 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
752 const struct wined3d_gl_info *gl_info;
753 struct wined3d_context *context;
754 DWORD src_mask, dst_mask;
755 GLbitfield gl_mask;
757 TRACE("device %p\n", device);
758 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
759 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
760 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
761 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
763 src_mask = src_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
764 dst_mask = dst_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
766 if (src_mask != dst_mask)
768 ERR("Incompatible formats %s and %s.\n",
769 debug_d3dformat(src_surface->resource.format->id),
770 debug_d3dformat(dst_surface->resource.format->id));
771 return;
774 if (!src_mask)
776 ERR("Not a depth / stencil format: %s.\n",
777 debug_d3dformat(src_surface->resource.format->id));
778 return;
781 gl_mask = 0;
782 if (src_mask & WINED3DFMT_FLAG_DEPTH)
783 gl_mask |= GL_DEPTH_BUFFER_BIT;
784 if (src_mask & WINED3DFMT_FLAG_STENCIL)
785 gl_mask |= GL_STENCIL_BUFFER_BIT;
787 context = context_acquire(device, NULL);
788 if (!context->valid)
790 context_release(context);
791 WARN("Invalid context, skipping blit.\n");
792 return;
795 /* Make sure the locations are up-to-date. Loading the destination
796 * surface isn't required if the entire surface is overwritten. */
797 surface_load_location(src_surface, context, src_location);
798 if (!surface_is_full_rect(dst_surface, dst_rect))
799 surface_load_location(dst_surface, context, dst_location);
800 else
801 wined3d_surface_prepare(dst_surface, context, dst_location);
803 gl_info = context->gl_info;
805 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
806 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
808 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
809 context_set_draw_buffer(context, GL_NONE);
810 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
811 context_invalidate_state(context, STATE_FRAMEBUFFER);
813 if (gl_mask & GL_DEPTH_BUFFER_BIT)
815 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
816 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
818 if (gl_mask & GL_STENCIL_BUFFER_BIT)
820 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
822 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
823 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
825 gl_info->gl_ops.gl.p_glStencilMask(~0U);
826 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
829 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
830 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
832 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
833 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
834 checkGLcall("glBlitFramebuffer()");
836 if (wined3d_settings.strict_draw_ordering)
837 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
839 context_release(context);
842 /* Blit between surface locations. Onscreen on different swapchains is not supported.
843 * Depth / stencil is not supported. Context activation is done by the caller. */
844 static void surface_blt_fbo(const struct wined3d_device *device,
845 struct wined3d_context *old_ctx, enum wined3d_texture_filter_type filter,
846 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
847 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
849 const struct wined3d_gl_info *gl_info;
850 struct wined3d_context *context = old_ctx;
851 struct wined3d_surface *required_rt, *restore_rt = NULL;
852 RECT src_rect, dst_rect;
853 GLenum gl_filter;
854 GLenum buffer;
856 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
857 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
858 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
859 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
860 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
862 src_rect = *src_rect_in;
863 dst_rect = *dst_rect_in;
865 switch (filter)
867 case WINED3D_TEXF_LINEAR:
868 gl_filter = GL_LINEAR;
869 break;
871 default:
872 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
873 case WINED3D_TEXF_NONE:
874 case WINED3D_TEXF_POINT:
875 gl_filter = GL_NEAREST;
876 break;
879 /* Resolve the source surface first if needed. */
880 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
881 && (src_surface->resource.format->id != dst_surface->resource.format->id
882 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
883 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
884 src_location = WINED3D_LOCATION_RB_RESOLVED;
886 /* Make sure the locations are up-to-date. Loading the destination
887 * surface isn't required if the entire surface is overwritten. (And is
888 * in fact harmful if we're being called by surface_load_location() with
889 * the purpose of loading the destination surface.) */
890 surface_load_location(src_surface, old_ctx, src_location);
891 if (!surface_is_full_rect(dst_surface, &dst_rect))
892 surface_load_location(dst_surface, old_ctx, dst_location);
893 else
894 wined3d_surface_prepare(dst_surface, old_ctx, dst_location);
897 if (src_location == WINED3D_LOCATION_DRAWABLE) required_rt = src_surface;
898 else if (dst_location == WINED3D_LOCATION_DRAWABLE) required_rt = dst_surface;
899 else required_rt = NULL;
901 if (required_rt && required_rt != old_ctx->current_rt)
903 restore_rt = old_ctx->current_rt;
904 context = context_acquire(device, required_rt);
907 if (!context->valid)
909 context_release(context);
910 WARN("Invalid context, skipping blit.\n");
911 return;
914 gl_info = context->gl_info;
916 if (src_location == WINED3D_LOCATION_DRAWABLE)
918 TRACE("Source surface %p is onscreen.\n", src_surface);
919 buffer = surface_get_gl_buffer(src_surface);
920 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
922 else
924 TRACE("Source surface %p is offscreen.\n", src_surface);
925 buffer = GL_COLOR_ATTACHMENT0;
928 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
929 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
930 checkGLcall("glReadBuffer()");
931 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
933 if (dst_location == WINED3D_LOCATION_DRAWABLE)
935 TRACE("Destination surface %p is onscreen.\n", dst_surface);
936 buffer = surface_get_gl_buffer(dst_surface);
937 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
939 else
941 TRACE("Destination surface %p is offscreen.\n", dst_surface);
942 buffer = GL_COLOR_ATTACHMENT0;
945 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
946 context_set_draw_buffer(context, buffer);
947 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
948 context_invalidate_state(context, STATE_FRAMEBUFFER);
950 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
951 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
952 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
953 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
954 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
956 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
957 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
959 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
960 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
961 checkGLcall("glBlitFramebuffer()");
963 if (wined3d_settings.strict_draw_ordering
964 || (dst_location == WINED3D_LOCATION_DRAWABLE
965 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
966 gl_info->gl_ops.gl.p_glFlush();
968 if (restore_rt)
969 context_restore(context, restore_rt);
972 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
973 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
974 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
976 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
977 return FALSE;
979 /* Source and/or destination need to be on the GL side */
980 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
981 return FALSE;
983 switch (blit_op)
985 case WINED3D_BLIT_OP_COLOR_BLIT:
986 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
987 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
988 return FALSE;
989 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
990 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
991 return FALSE;
992 if (!(src_format->id == dst_format->id
993 || (is_identity_fixup(src_format->color_fixup)
994 && is_identity_fixup(dst_format->color_fixup))))
995 return FALSE;
996 break;
998 case WINED3D_BLIT_OP_DEPTH_BLIT:
999 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1000 return FALSE;
1001 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1002 return FALSE;
1003 /* Accept pure swizzle fixups for depth formats. In general we
1004 * ignore the stencil component (if present) at the moment and the
1005 * swizzle is not relevant with just the depth component. */
1006 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
1007 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
1008 return FALSE;
1009 break;
1011 default:
1012 return FALSE;
1015 return TRUE;
1018 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1020 const struct wined3d_format *format = surface->resource.format;
1022 switch (format->id)
1024 case WINED3DFMT_S1_UINT_D15_UNORM:
1025 *float_depth = depth / (float)0x00007fff;
1026 break;
1028 case WINED3DFMT_D16_UNORM:
1029 *float_depth = depth / (float)0x0000ffff;
1030 break;
1032 case WINED3DFMT_D24_UNORM_S8_UINT:
1033 case WINED3DFMT_X8D24_UNORM:
1034 *float_depth = depth / (float)0x00ffffff;
1035 break;
1037 case WINED3DFMT_D32_UNORM:
1038 *float_depth = depth / (float)0xffffffff;
1039 break;
1041 default:
1042 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1043 return FALSE;
1046 return TRUE;
1049 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1051 const struct wined3d_resource *resource = &surface->container->resource;
1052 struct wined3d_device *device = resource->device;
1053 const struct blit_shader *blitter;
1055 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, WINED3D_BLIT_OP_DEPTH_FILL,
1056 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1057 if (!blitter)
1059 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1060 return WINED3DERR_INVALIDCALL;
1063 return blitter->depth_fill(device, surface, rect, depth);
1066 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1067 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1069 struct wined3d_device *device = src_surface->resource.device;
1071 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1072 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1073 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1074 return WINED3DERR_INVALIDCALL;
1076 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1078 surface_modify_ds_location(dst_surface, dst_location,
1079 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1081 return WINED3D_OK;
1084 /* Context activation is done by the caller. */
1085 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1087 GL_EXTCALL(glDeleteBuffers(1, &surface->pbo));
1088 checkGLcall("glDeleteBuffers(1, &surface->pbo)");
1090 surface->pbo = 0;
1091 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1094 static ULONG surface_resource_incref(struct wined3d_resource *resource)
1096 struct wined3d_surface *surface = surface_from_resource(resource);
1098 TRACE("surface %p, container %p.\n", surface, surface->container);
1100 return wined3d_texture_incref(surface->container);
1103 static ULONG surface_resource_decref(struct wined3d_resource *resource)
1105 struct wined3d_surface *surface = surface_from_resource(resource);
1107 TRACE("surface %p, container %p.\n", surface, surface->container);
1109 return wined3d_texture_decref(surface->container);
1112 static void surface_unload(struct wined3d_resource *resource)
1114 struct wined3d_surface *surface = surface_from_resource(resource);
1115 struct wined3d_renderbuffer_entry *entry, *entry2;
1116 struct wined3d_device *device = resource->device;
1117 const struct wined3d_gl_info *gl_info;
1118 struct wined3d_context *context;
1120 TRACE("surface %p.\n", surface);
1122 context = context_acquire(device, NULL);
1123 gl_info = context->gl_info;
1125 if (resource->pool == WINED3D_POOL_DEFAULT)
1127 /* Default pool resources are supposed to be destroyed before Reset is called.
1128 * Implicit resources stay however. So this means we have an implicit render target
1129 * or depth stencil. The content may be destroyed, but we still have to tear down
1130 * opengl resources, so we cannot leave early.
1132 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1133 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1134 * or the depth stencil into an FBO the texture or render buffer will be removed
1135 * and all flags get lost */
1136 if (resource->usage & WINED3DUSAGE_DEPTHSTENCIL)
1138 surface_validate_location(surface, WINED3D_LOCATION_DISCARDED);
1139 surface_invalidate_location(surface, ~WINED3D_LOCATION_DISCARDED);
1141 else
1143 surface_prepare_system_memory(surface);
1144 memset(surface->resource.heap_memory, 0, surface->resource.size);
1145 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1146 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1149 else
1151 surface_prepare_map_memory(surface);
1152 surface_load_location(surface, context, surface->resource.map_binding);
1153 surface_invalidate_location(surface, ~surface->resource.map_binding);
1156 /* Destroy PBOs, but load them into real sysmem before */
1157 if (surface->pbo)
1158 surface_remove_pbo(surface, gl_info);
1160 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1161 * all application-created targets the application has to release the surface
1162 * before calling _Reset
1164 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1166 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1167 list_remove(&entry->entry);
1168 HeapFree(GetProcessHeap(), 0, entry);
1170 list_init(&surface->renderbuffers);
1171 surface->current_renderbuffer = NULL;
1173 if (surface->rb_multisample)
1175 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1176 surface->rb_multisample = 0;
1178 if (surface->rb_resolved)
1180 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1181 surface->rb_resolved = 0;
1184 context_release(context);
1186 resource_unload(resource);
1189 static HRESULT surface_resource_sub_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx,
1190 struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
1192 ERR("Not supported on sub-resources.\n");
1193 return WINED3DERR_INVALIDCALL;
1196 static HRESULT surface_resource_sub_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx)
1198 ERR("Not supported on sub-resources.\n");
1199 return WINED3DERR_INVALIDCALL;
1202 static const struct wined3d_resource_ops surface_resource_ops =
1204 surface_resource_incref,
1205 surface_resource_decref,
1206 surface_unload,
1207 surface_resource_sub_resource_map,
1208 surface_resource_sub_resource_unmap,
1211 static const struct wined3d_surface_ops surface_ops =
1213 surface_private_setup,
1214 surface_unmap,
1217 /*****************************************************************************
1218 * Initializes the GDI surface, aka creates the DIB section we render to
1219 * The DIB section creation is done by calling GetDC, which will create the
1220 * section and releasing the dc to allow the app to use it. The dib section
1221 * will stay until the surface is released
1223 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1224 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1225 * avoid confusion in the shared surface code.
1227 * Returns:
1228 * WINED3D_OK on success
1229 * The return values of called methods on failure
1231 *****************************************************************************/
1232 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1234 HRESULT hr;
1236 TRACE("surface %p.\n", surface);
1238 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1240 ERR("Overlays not yet supported by GDI surfaces.\n");
1241 return WINED3DERR_INVALIDCALL;
1244 /* Sysmem textures have memory already allocated - release it,
1245 * this avoids an unnecessary memcpy. */
1246 hr = surface_create_dib_section(surface);
1247 if (FAILED(hr))
1248 return hr;
1249 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1251 /* We don't mind the nonpow2 stuff in GDI. */
1252 surface->pow2Width = surface->resource.width;
1253 surface->pow2Height = surface->resource.height;
1255 return WINED3D_OK;
1258 static void gdi_surface_unmap(struct wined3d_surface *surface)
1260 TRACE("surface %p.\n", surface);
1262 /* Tell the swapchain to update the screen. */
1263 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
1264 x11_copy_to_screen(surface->container->swapchain, &surface->lockedRect);
1266 memset(&surface->lockedRect, 0, sizeof(RECT));
1269 static const struct wined3d_surface_ops gdi_surface_ops =
1271 gdi_surface_private_setup,
1272 gdi_surface_unmap,
1275 /* This call just downloads data, the caller is responsible for binding the
1276 * correct texture. */
1277 /* Context activation is done by the caller. */
1278 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1279 DWORD dst_location)
1281 const struct wined3d_format *format = surface->resource.format;
1282 struct wined3d_bo_address data;
1284 /* Only support read back of converted P8 surfaces. */
1285 if (surface->container->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1287 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1288 return;
1291 surface_get_memory(surface, &data, dst_location);
1293 if (surface->container->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
1295 TRACE("(%p) : Calling glGetCompressedTexImage level %d, format %#x, type %#x, data %p.\n",
1296 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1298 if (data.buffer_object)
1300 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1301 checkGLcall("glBindBuffer");
1302 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, NULL));
1303 checkGLcall("glGetCompressedTexImage");
1304 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1305 checkGLcall("glBindBuffer");
1307 else
1309 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target,
1310 surface->texture_level, data.addr));
1311 checkGLcall("glGetCompressedTexImage");
1314 else
1316 void *mem;
1317 GLenum gl_format = format->glFormat;
1318 GLenum gl_type = format->glType;
1319 int src_pitch = 0;
1320 int dst_pitch = 0;
1322 if (surface->flags & SFLAG_NONPOW2)
1324 unsigned char alignment = surface->resource.device->surface_alignment;
1325 src_pitch = format->byte_count * surface->pow2Width;
1326 dst_pitch = wined3d_surface_get_pitch(surface);
1327 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1328 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1330 else
1332 mem = data.addr;
1335 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1336 surface, surface->texture_level, gl_format, gl_type, mem);
1338 if (data.buffer_object)
1340 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1341 checkGLcall("glBindBuffer");
1343 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1344 gl_format, gl_type, NULL);
1345 checkGLcall("glGetTexImage");
1347 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1348 checkGLcall("glBindBuffer");
1350 else
1352 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1353 gl_format, gl_type, mem);
1354 checkGLcall("glGetTexImage");
1357 if (surface->flags & SFLAG_NONPOW2)
1359 const BYTE *src_data;
1360 BYTE *dst_data;
1361 UINT y;
1363 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1364 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1365 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1367 * We're doing this...
1369 * instead of boxing the texture :
1370 * |<-texture width ->| -->pow2width| /\
1371 * |111111111111111111| | |
1372 * |222 Texture 222222| boxed empty | texture height
1373 * |3333 Data 33333333| | |
1374 * |444444444444444444| | \/
1375 * ----------------------------------- |
1376 * | boxed empty | boxed empty | pow2height
1377 * | | | \/
1378 * -----------------------------------
1381 * we're repacking the data to the expected texture width
1383 * |<-texture width ->| -->pow2width| /\
1384 * |111111111111111111222222222222222| |
1385 * |222333333333333333333444444444444| texture height
1386 * |444444 | |
1387 * | | \/
1388 * | | |
1389 * | empty | pow2height
1390 * | | \/
1391 * -----------------------------------
1393 * == is the same as
1395 * |<-texture width ->| /\
1396 * |111111111111111111|
1397 * |222222222222222222|texture height
1398 * |333333333333333333|
1399 * |444444444444444444| \/
1400 * --------------------
1402 * This also means that any references to surface memory should work with the data as if it were a
1403 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1405 * internally the texture is still stored in a boxed format so any references to textureName will
1406 * get a boxed texture with width pow2width and not a texture of width resource.width.
1408 * Performance should not be an issue, because applications normally do not lock the surfaces when
1409 * rendering. If an app does, the WINED3D_TEXTURE_DYNAMIC_MAP flag will kick in and the memory copy
1410 * won't be released, and doesn't have to be re-read. */
1411 src_data = mem;
1412 dst_data = data.addr;
1413 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1414 for (y = 0; y < surface->resource.height; ++y)
1416 memcpy(dst_data, src_data, dst_pitch);
1417 src_data += src_pitch;
1418 dst_data += dst_pitch;
1421 HeapFree(GetProcessHeap(), 0, mem);
1426 /* This call just uploads data, the caller is responsible for binding the
1427 * correct texture. */
1428 /* Context activation is done by the caller. */
1429 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1430 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1431 BOOL srgb, const struct wined3d_const_bo_address *data)
1433 UINT update_w = src_rect->right - src_rect->left;
1434 UINT update_h = src_rect->bottom - src_rect->top;
1436 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1437 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1438 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1440 if (surface->resource.map_count)
1442 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
1443 surface->container->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
1446 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
1448 update_h *= format->height_scale.numerator;
1449 update_h /= format->height_scale.denominator;
1452 if (data->buffer_object)
1454 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
1455 checkGLcall("glBindBuffer");
1458 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
1460 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1461 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1462 const BYTE *addr = data->addr;
1463 GLenum internal;
1465 addr += (src_rect->top / format->block_height) * src_pitch;
1466 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1468 if (srgb)
1469 internal = format->glGammaInternal;
1470 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1471 && wined3d_resource_is_offscreen(&surface->container->resource))
1472 internal = format->rtInternal;
1473 else
1474 internal = format->glInternal;
1476 TRACE("glCompressedTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, "
1477 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1478 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1480 if (row_length == src_pitch)
1482 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1483 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1485 else
1487 UINT row, y;
1489 /* glCompressedTexSubImage2D() ignores pixel store state, so we
1490 * can't use the unpack row length like for glTexSubImage2D. */
1491 for (row = 0, y = dst_point->y; row < row_count; ++row)
1493 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1494 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1495 y += format->block_height;
1496 addr += src_pitch;
1499 checkGLcall("glCompressedTexSubImage2D");
1501 else
1503 const BYTE *addr = data->addr;
1505 addr += src_rect->top * src_pitch;
1506 addr += src_rect->left * format->byte_count;
1508 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1509 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1510 update_w, update_h, format->glFormat, format->glType, addr);
1512 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1513 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1514 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1515 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1516 checkGLcall("glTexSubImage2D");
1519 if (data->buffer_object)
1521 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1522 checkGLcall("glBindBuffer");
1525 if (wined3d_settings.strict_draw_ordering)
1526 gl_info->gl_ops.gl.p_glFlush();
1528 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1530 struct wined3d_device *device = surface->resource.device;
1531 unsigned int i;
1533 for (i = 0; i < device->context_count; ++i)
1535 context_surface_update(device->contexts[i], surface);
1540 static BOOL surface_check_block_align(struct wined3d_surface *surface, const struct wined3d_box *box)
1542 UINT width_mask, height_mask;
1544 if (!box->left && !box->top
1545 && box->right == surface->resource.width
1546 && box->bottom == surface->resource.height)
1547 return TRUE;
1549 if ((box->left >= box->right)
1550 || (box->top >= box->bottom)
1551 || (box->right > surface->resource.width)
1552 || (box->bottom > surface->resource.height))
1553 return FALSE;
1555 /* This assumes power of two block sizes, but NPOT block sizes would be
1556 * silly anyway. */
1557 width_mask = surface->resource.format->block_width - 1;
1558 height_mask = surface->resource.format->block_height - 1;
1560 if (!(box->left & width_mask) && !(box->top & height_mask)
1561 && !(box->right & width_mask) && !(box->bottom & height_mask))
1562 return TRUE;
1564 return FALSE;
1567 static BOOL surface_check_block_align_rect(struct wined3d_surface *surface, const RECT *rect)
1569 struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
1571 return surface_check_block_align(surface, &box);
1574 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1575 struct wined3d_surface *src_surface, const RECT *src_rect)
1577 const struct wined3d_format *src_format;
1578 const struct wined3d_format *dst_format;
1579 unsigned int src_fmt_flags, dst_fmt_flags;
1580 const struct wined3d_gl_info *gl_info;
1581 struct wined3d_context *context;
1582 struct wined3d_bo_address data;
1583 UINT update_w, update_h;
1584 UINT dst_w, dst_h;
1585 RECT r, dst_rect;
1586 UINT src_pitch;
1587 POINT p;
1589 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1590 dst_surface, wine_dbgstr_point(dst_point),
1591 src_surface, wine_dbgstr_rect(src_rect));
1593 src_format = src_surface->resource.format;
1594 dst_format = dst_surface->resource.format;
1595 src_fmt_flags = src_surface->container->resource.format_flags;
1596 dst_fmt_flags = dst_surface->container->resource.format_flags;
1598 if (src_format->id != dst_format->id)
1600 WARN("Source and destination surfaces should have the same format.\n");
1601 return WINED3DERR_INVALIDCALL;
1604 if (!dst_point)
1606 p.x = 0;
1607 p.y = 0;
1608 dst_point = &p;
1610 else if (dst_point->x < 0 || dst_point->y < 0)
1612 WARN("Invalid destination point.\n");
1613 return WINED3DERR_INVALIDCALL;
1616 if (!src_rect)
1618 r.left = 0;
1619 r.top = 0;
1620 r.right = src_surface->resource.width;
1621 r.bottom = src_surface->resource.height;
1622 src_rect = &r;
1624 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1625 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1627 WARN("Invalid source rectangle.\n");
1628 return WINED3DERR_INVALIDCALL;
1631 dst_w = dst_surface->resource.width;
1632 dst_h = dst_surface->resource.height;
1634 update_w = src_rect->right - src_rect->left;
1635 update_h = src_rect->bottom - src_rect->top;
1637 if (update_w > dst_w || dst_point->x > dst_w - update_w
1638 || update_h > dst_h || dst_point->y > dst_h - update_h)
1640 WARN("Destination out of bounds.\n");
1641 return WINED3DERR_INVALIDCALL;
1644 if ((src_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(src_surface, src_rect))
1646 WARN("Source rectangle not block-aligned.\n");
1647 return WINED3DERR_INVALIDCALL;
1650 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1651 if ((dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(dst_surface, &dst_rect))
1653 WARN("Destination rectangle not block-aligned.\n");
1654 return WINED3DERR_INVALIDCALL;
1657 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1658 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_surface->container, FALSE))
1659 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1661 context = context_acquire(dst_surface->resource.device, NULL);
1662 gl_info = context->gl_info;
1664 /* Only load the surface for partial updates. For newly allocated texture
1665 * the texture wouldn't be the current location, and we'd upload zeroes
1666 * just to overwrite them again. */
1667 if (update_w == dst_w && update_h == dst_h)
1668 wined3d_texture_prepare_texture(dst_surface->container, context, FALSE);
1669 else
1670 surface_load_location(dst_surface, context, WINED3D_LOCATION_TEXTURE_RGB);
1671 wined3d_texture_bind_and_dirtify(dst_surface->container, context, FALSE);
1673 surface_get_memory(src_surface, &data, src_surface->locations);
1674 src_pitch = wined3d_surface_get_pitch(src_surface);
1676 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1677 src_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1679 context_release(context);
1681 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1682 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1684 return WINED3D_OK;
1687 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1688 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1689 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1690 /* Context activation is done by the caller. */
1691 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1693 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1694 struct wined3d_renderbuffer_entry *entry;
1695 GLuint renderbuffer = 0;
1696 unsigned int src_width, src_height;
1697 unsigned int width, height;
1699 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1701 width = rt->pow2Width;
1702 height = rt->pow2Height;
1704 else
1706 width = surface->pow2Width;
1707 height = surface->pow2Height;
1710 src_width = surface->pow2Width;
1711 src_height = surface->pow2Height;
1713 /* A depth stencil smaller than the render target is not valid */
1714 if (width > src_width || height > src_height) return;
1716 /* Remove any renderbuffer set if the sizes match */
1717 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1718 || (width == src_width && height == src_height))
1720 surface->current_renderbuffer = NULL;
1721 return;
1724 /* Look if we've already got a renderbuffer of the correct dimensions */
1725 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1727 if (entry->width == width && entry->height == height)
1729 renderbuffer = entry->id;
1730 surface->current_renderbuffer = entry;
1731 break;
1735 if (!renderbuffer)
1737 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1738 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1739 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1740 surface->resource.format->glInternal, width, height);
1742 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1743 entry->width = width;
1744 entry->height = height;
1745 entry->id = renderbuffer;
1746 list_add_head(&surface->renderbuffers, &entry->entry);
1748 surface->current_renderbuffer = entry;
1751 checkGLcall("set_compatible_renderbuffer");
1754 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1756 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1758 TRACE("surface %p.\n", surface);
1760 if (!swapchain)
1762 ERR("Surface %p is not on a swapchain.\n", surface);
1763 return GL_NONE;
1766 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
1768 if (swapchain->render_to_fbo)
1770 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1771 return GL_COLOR_ATTACHMENT0;
1773 TRACE("Returning GL_BACK\n");
1774 return GL_BACK;
1776 else if (surface->container == swapchain->front_buffer)
1778 TRACE("Returning GL_FRONT\n");
1779 return GL_FRONT;
1782 FIXME("Higher back buffer, returning GL_BACK\n");
1783 return GL_BACK;
1786 /* Context activation is done by the caller. */
1787 void surface_load(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
1789 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1791 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1793 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
1794 ERR("Not supported on scratch surfaces.\n");
1796 if (surface->locations & location)
1798 TRACE("surface is already in texture\n");
1799 return;
1801 TRACE("Reloading because surface is dirty.\n");
1803 surface_load_location(surface, context, location);
1804 surface_evict_sysmem(surface);
1807 /* See also float_16_to_32() in wined3d_private.h */
1808 static inline unsigned short float_32_to_16(const float *in)
1810 int exp = 0;
1811 float tmp = fabsf(*in);
1812 unsigned int mantissa;
1813 unsigned short ret;
1815 /* Deal with special numbers */
1816 if (*in == 0.0f)
1817 return 0x0000;
1818 if (isnan(*in))
1819 return 0x7c01;
1820 if (isinf(*in))
1821 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1823 if (tmp < (float)(1u << 10))
1827 tmp = tmp * 2.0f;
1828 exp--;
1829 } while (tmp < (float)(1u << 10));
1831 else if (tmp >= (float)(1u << 11))
1835 tmp /= 2.0f;
1836 exp++;
1837 } while (tmp >= (float)(1u << 11));
1840 mantissa = (unsigned int)tmp;
1841 if (tmp - mantissa >= 0.5f)
1842 ++mantissa; /* Round to nearest, away from zero. */
1844 exp += 10; /* Normalize the mantissa. */
1845 exp += 15; /* Exponent is encoded with excess 15. */
1847 if (exp > 30) /* too big */
1849 ret = 0x7c00; /* INF */
1851 else if (exp <= 0)
1853 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1854 while (exp <= 0)
1856 mantissa = mantissa >> 1;
1857 ++exp;
1859 ret = mantissa & 0x3ff;
1861 else
1863 ret = (exp << 10) | (mantissa & 0x3ff);
1866 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1867 return ret;
1870 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
1872 TRACE("surface %p.\n", surface);
1874 if (!surface->resource.device->d3d_initialized)
1876 ERR("D3D not initialized.\n");
1877 return;
1880 wined3d_texture_preload(surface->container);
1883 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
1885 TRACE("surface %p.\n", surface);
1887 return surface->resource.parent;
1890 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
1892 TRACE("surface %p.\n", surface);
1894 return &surface->resource;
1897 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
1899 unsigned int alignment;
1900 DWORD pitch;
1902 TRACE("surface %p.\n", surface);
1904 if (surface->pitch)
1905 return surface->pitch;
1907 alignment = surface->resource.device->surface_alignment;
1908 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
1909 pitch = (pitch + alignment - 1) & ~(alignment - 1);
1911 TRACE("Returning %u.\n", pitch);
1913 return pitch;
1916 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
1918 LONG w, h;
1920 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
1922 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1924 WARN("Not an overlay surface.\n");
1925 return WINEDDERR_NOTAOVERLAYSURFACE;
1928 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
1929 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
1930 surface->overlay_destrect.left = x;
1931 surface->overlay_destrect.top = y;
1932 surface->overlay_destrect.right = x + w;
1933 surface->overlay_destrect.bottom = y + h;
1935 return WINED3D_OK;
1938 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
1940 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
1942 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1944 TRACE("Not an overlay surface.\n");
1945 return WINEDDERR_NOTAOVERLAYSURFACE;
1948 if (!surface->overlay_dest)
1950 TRACE("Overlay not visible.\n");
1951 *x = 0;
1952 *y = 0;
1953 return WINEDDERR_OVERLAYNOTVISIBLE;
1956 *x = surface->overlay_destrect.left;
1957 *y = surface->overlay_destrect.top;
1959 TRACE("Returning position %d, %d.\n", *x, *y);
1961 return WINED3D_OK;
1964 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
1965 DWORD flags, struct wined3d_surface *ref)
1967 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
1969 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1971 TRACE("Not an overlay surface.\n");
1972 return WINEDDERR_NOTAOVERLAYSURFACE;
1975 return WINED3D_OK;
1978 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
1979 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
1981 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
1982 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
1984 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1986 WARN("Not an overlay surface.\n");
1987 return WINEDDERR_NOTAOVERLAYSURFACE;
1989 else if (!dst_surface)
1991 WARN("Dest surface is NULL.\n");
1992 return WINED3DERR_INVALIDCALL;
1995 surface_get_rect(surface, src_rect, &surface->overlay_srcrect);
1996 surface_get_rect(dst_surface, dst_rect, &surface->overlay_destrect);
1998 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2000 surface->overlay_dest = NULL;
2001 list_remove(&surface->overlay_entry);
2004 if (flags & WINEDDOVER_SHOW)
2006 if (surface->overlay_dest != dst_surface)
2008 surface->overlay_dest = dst_surface;
2009 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2012 else if (flags & WINEDDOVER_HIDE)
2014 /* tests show that the rectangles are erased on hide */
2015 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2016 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2017 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2018 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2019 surface->overlay_dest = NULL;
2022 return WINED3D_OK;
2025 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface,
2026 const struct wined3d_gl_info *gl_info, void *mem, unsigned int pitch)
2028 struct wined3d_resource *texture_resource = &surface->container->resource;
2029 unsigned int width, height;
2030 BOOL create_dib = FALSE;
2031 DWORD valid_location = 0;
2032 HRESULT hr;
2034 if (surface->flags & SFLAG_DIBSECTION)
2036 DeleteDC(surface->hDC);
2037 DeleteObject(surface->dib.DIBsection);
2038 surface->dib.bitmap_data = NULL;
2039 surface->flags &= ~SFLAG_DIBSECTION;
2040 create_dib = TRUE;
2043 surface->locations = 0;
2044 wined3d_resource_free_sysmem(&surface->resource);
2046 width = texture_resource->width;
2047 height = texture_resource->height;
2048 surface->resource.width = width;
2049 surface->resource.height = height;
2050 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2051 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2053 surface->pow2Width = width;
2054 surface->pow2Height = height;
2056 else
2058 surface->pow2Width = surface->pow2Height = 1;
2059 while (surface->pow2Width < width)
2060 surface->pow2Width <<= 1;
2061 while (surface->pow2Height < height)
2062 surface->pow2Height <<= 1;
2065 if (surface->pow2Width != width || surface->pow2Height != height)
2066 surface->flags |= SFLAG_NONPOW2;
2067 else
2068 surface->flags &= ~SFLAG_NONPOW2;
2070 if ((surface->user_memory = mem))
2072 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
2073 valid_location = WINED3D_LOCATION_USER_MEMORY;
2075 surface->pitch = pitch;
2076 surface->resource.format = texture_resource->format;
2077 surface->resource.multisample_type = texture_resource->multisample_type;
2078 surface->resource.multisample_quality = texture_resource->multisample_quality;
2079 if (surface->pitch)
2081 surface->resource.size = height * surface->pitch;
2083 else
2085 /* User memory surfaces don't have the regular surface alignment. */
2086 surface->resource.size = wined3d_format_calculate_size(texture_resource->format,
2087 1, width, height, 1);
2088 surface->pitch = wined3d_format_calculate_pitch(texture_resource->format, width);
2091 /* The format might be changed to a format that needs conversion.
2092 * If the surface didn't use PBOs previously but could now, don't
2093 * change it - whatever made us not use PBOs might come back, e.g.
2094 * color keys. */
2095 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2096 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2098 if (create_dib)
2100 if (FAILED(hr = surface_create_dib_section(surface)))
2102 ERR("Failed to create dib section, hr %#x.\n", hr);
2103 return hr;
2105 if (!valid_location)
2106 valid_location = WINED3D_LOCATION_DIB;
2109 if (!valid_location)
2111 surface_prepare_system_memory(surface);
2112 valid_location = WINED3D_LOCATION_SYSMEM;
2115 surface_validate_location(surface, valid_location);
2117 return WINED3D_OK;
2120 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2121 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2123 unsigned short *dst_s;
2124 const float *src_f;
2125 unsigned int x, y;
2127 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2129 for (y = 0; y < h; ++y)
2131 src_f = (const float *)(src + y * pitch_in);
2132 dst_s = (unsigned short *) (dst + y * pitch_out);
2133 for (x = 0; x < w; ++x)
2135 dst_s[x] = float_32_to_16(src_f + x);
2140 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2141 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2143 static const unsigned char convert_5to8[] =
2145 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2146 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2147 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2148 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2150 static const unsigned char convert_6to8[] =
2152 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2153 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2154 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2155 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2156 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2157 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2158 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2159 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2161 unsigned int x, y;
2163 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2165 for (y = 0; y < h; ++y)
2167 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2168 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2169 for (x = 0; x < w; ++x)
2171 WORD pixel = src_line[x];
2172 dst_line[x] = 0xff000000u
2173 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
2174 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
2175 | convert_5to8[(pixel & 0x001fu)];
2180 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2181 * in both cases we're just setting the X / Alpha channel to 0xff. */
2182 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2183 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2185 unsigned int x, y;
2187 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2189 for (y = 0; y < h; ++y)
2191 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2192 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2194 for (x = 0; x < w; ++x)
2196 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2201 static inline BYTE cliptobyte(int x)
2203 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2206 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2207 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2209 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2210 unsigned int x, y;
2212 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2214 for (y = 0; y < h; ++y)
2216 const BYTE *src_line = src + y * pitch_in;
2217 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2218 for (x = 0; x < w; ++x)
2220 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2221 * C = Y - 16; D = U - 128; E = V - 128;
2222 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2223 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2224 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2225 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2226 * U and V are shared between the pixels. */
2227 if (!(x & 1)) /* For every even pixel, read new U and V. */
2229 d = (int) src_line[1] - 128;
2230 e = (int) src_line[3] - 128;
2231 r2 = 409 * e + 128;
2232 g2 = - 100 * d - 208 * e + 128;
2233 b2 = 516 * d + 128;
2235 c2 = 298 * ((int) src_line[0] - 16);
2236 dst_line[x] = 0xff000000
2237 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2238 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2239 | cliptobyte((c2 + b2) >> 8); /* blue */
2240 /* Scale RGB values to 0..255 range,
2241 * then clip them if still not in range (may be negative),
2242 * then shift them within DWORD if necessary. */
2243 src_line += 2;
2248 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2249 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2251 unsigned int x, y;
2252 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2254 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2256 for (y = 0; y < h; ++y)
2258 const BYTE *src_line = src + y * pitch_in;
2259 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2260 for (x = 0; x < w; ++x)
2262 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2263 * C = Y - 16; D = U - 128; E = V - 128;
2264 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2265 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2266 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2267 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2268 * U and V are shared between the pixels. */
2269 if (!(x & 1)) /* For every even pixel, read new U and V. */
2271 d = (int) src_line[1] - 128;
2272 e = (int) src_line[3] - 128;
2273 r2 = 409 * e + 128;
2274 g2 = - 100 * d - 208 * e + 128;
2275 b2 = 516 * d + 128;
2277 c2 = 298 * ((int) src_line[0] - 16);
2278 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2279 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2280 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2281 /* Scale RGB values to 0..255 range,
2282 * then clip them if still not in range (may be negative),
2283 * then shift them within DWORD if necessary. */
2284 src_line += 2;
2289 struct d3dfmt_converter_desc
2291 enum wined3d_format_id from, to;
2292 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2295 static const struct d3dfmt_converter_desc converters[] =
2297 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2298 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2299 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2300 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2301 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2302 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2305 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2306 enum wined3d_format_id to)
2308 unsigned int i;
2310 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2312 if (converters[i].from == from && converters[i].to == to)
2313 return &converters[i];
2316 return NULL;
2319 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2321 struct wined3d_map_desc src_map, dst_map;
2322 const struct d3dfmt_converter_desc *conv;
2323 struct wined3d_texture *ret = NULL;
2324 struct wined3d_resource_desc desc;
2325 struct wined3d_surface *dst;
2327 conv = find_converter(source->resource.format->id, to_fmt);
2328 if (!conv)
2330 FIXME("Cannot find a conversion function from format %s to %s.\n",
2331 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2332 return NULL;
2335 /* FIXME: Multisampled conversion? */
2336 wined3d_resource_get_desc(&source->resource, &desc);
2337 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
2338 desc.format = to_fmt;
2339 desc.usage = 0;
2340 desc.pool = WINED3D_POOL_SCRATCH;
2341 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2342 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
2343 NULL, NULL, &wined3d_null_parent_ops, &ret)))
2345 ERR("Failed to create a destination surface for conversion.\n");
2346 return NULL;
2348 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2350 memset(&src_map, 0, sizeof(src_map));
2351 memset(&dst_map, 0, sizeof(dst_map));
2353 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2355 ERR("Failed to lock the source surface.\n");
2356 wined3d_texture_decref(ret);
2357 return NULL;
2359 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2361 ERR("Failed to lock the destination surface.\n");
2362 wined3d_surface_unmap(source);
2363 wined3d_texture_decref(ret);
2364 return NULL;
2367 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2368 source->resource.width, source->resource.height);
2370 wined3d_surface_unmap(dst);
2371 wined3d_surface_unmap(source);
2373 return ret;
2376 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2377 unsigned int bpp, UINT pitch, DWORD color)
2379 BYTE *first;
2380 unsigned int x, y;
2382 /* Do first row */
2384 #define COLORFILL_ROW(type) \
2385 do { \
2386 type *d = (type *)buf; \
2387 for (x = 0; x < width; ++x) \
2388 d[x] = (type)color; \
2389 } while(0)
2391 switch (bpp)
2393 case 1:
2394 COLORFILL_ROW(BYTE);
2395 break;
2397 case 2:
2398 COLORFILL_ROW(WORD);
2399 break;
2401 case 3:
2403 BYTE *d = buf;
2404 for (x = 0; x < width; ++x, d += 3)
2406 d[0] = (color ) & 0xff;
2407 d[1] = (color >> 8) & 0xff;
2408 d[2] = (color >> 16) & 0xff;
2410 break;
2412 case 4:
2413 COLORFILL_ROW(DWORD);
2414 break;
2416 default:
2417 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2418 return WINED3DERR_NOTAVAILABLE;
2421 #undef COLORFILL_ROW
2423 /* Now copy first row. */
2424 first = buf;
2425 for (y = 1; y < height; ++y)
2427 buf += pitch;
2428 memcpy(buf, first, width * bpp);
2431 return WINED3D_OK;
2434 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2436 TRACE("surface %p.\n", surface);
2438 if (!surface->resource.map_count)
2440 WARN("Trying to unmap unmapped surface.\n");
2441 return WINEDDERR_NOTLOCKED;
2443 --surface->resource.map_count;
2445 surface->surface_ops->surface_unmap(surface);
2447 return WINED3D_OK;
2450 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2451 struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
2453 const struct wined3d_format *format = surface->resource.format;
2454 unsigned int fmt_flags = surface->container->resource.format_flags;
2455 struct wined3d_device *device = surface->resource.device;
2456 struct wined3d_context *context;
2457 const struct wined3d_gl_info *gl_info;
2458 BYTE *base_memory;
2460 TRACE("surface %p, map_desc %p, box %p, flags %#x.\n",
2461 surface, map_desc, box, flags);
2463 if (surface->resource.map_count)
2465 WARN("Surface is already mapped.\n");
2466 return WINED3DERR_INVALIDCALL;
2469 if ((fmt_flags & WINED3DFMT_FLAG_BLOCKS) && box
2470 && !surface_check_block_align(surface, box))
2472 WARN("Map rect %p is misaligned for %ux%u blocks.\n",
2473 box, format->block_width, format->block_height);
2475 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2476 return WINED3DERR_INVALIDCALL;
2479 ++surface->resource.map_count;
2481 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2482 WARN("Trying to lock unlockable surface.\n");
2484 /* Performance optimization: Count how often a surface is mapped, if it is
2485 * mapped regularly do not throw away the system memory copy. This avoids
2486 * the need to download the surface from OpenGL all the time. The surface
2487 * is still downloaded if the OpenGL texture is changed. Note that this
2488 * only really makes sense for managed textures.*/
2489 if (!(surface->container->flags & WINED3D_TEXTURE_DYNAMIC_MAP)
2490 && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2492 if (++surface->lockCount > MAXLOCKCOUNT)
2494 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2495 surface->container->flags |= WINED3D_TEXTURE_DYNAMIC_MAP;
2499 surface_prepare_map_memory(surface);
2500 if (flags & WINED3D_MAP_DISCARD)
2502 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2503 wined3d_debug_location(surface->resource.map_binding));
2504 surface_validate_location(surface, surface->resource.map_binding);
2506 else
2508 struct wined3d_context *context = NULL;
2510 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2511 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2513 if (surface->resource.device->d3d_initialized)
2514 context = context_acquire(surface->resource.device, NULL);
2515 surface_load_location(surface, context, surface->resource.map_binding);
2516 if (context)
2517 context_release(context);
2520 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2521 surface_invalidate_location(surface, ~surface->resource.map_binding);
2523 switch (surface->resource.map_binding)
2525 case WINED3D_LOCATION_SYSMEM:
2526 base_memory = surface->resource.heap_memory;
2527 break;
2529 case WINED3D_LOCATION_USER_MEMORY:
2530 base_memory = surface->user_memory;
2531 break;
2533 case WINED3D_LOCATION_DIB:
2534 base_memory = surface->dib.bitmap_data;
2535 break;
2537 case WINED3D_LOCATION_BUFFER:
2538 context = context_acquire(device, NULL);
2539 gl_info = context->gl_info;
2541 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
2542 base_memory = GL_EXTCALL(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE));
2543 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
2544 checkGLcall("map PBO");
2546 context_release(context);
2547 break;
2549 default:
2550 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2551 base_memory = NULL;
2554 if (fmt_flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2555 map_desc->row_pitch = surface->resource.width * format->byte_count;
2556 else
2557 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2558 map_desc->slice_pitch = surface->resource.height * map_desc->row_pitch;
2560 if (!box)
2562 map_desc->data = base_memory;
2563 surface->lockedRect.left = 0;
2564 surface->lockedRect.top = 0;
2565 surface->lockedRect.right = surface->resource.width;
2566 surface->lockedRect.bottom = surface->resource.height;
2568 else
2570 if ((fmt_flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2572 /* Compressed textures are block based, so calculate the offset of
2573 * the block that contains the top-left pixel of the locked rectangle. */
2574 map_desc->data = base_memory
2575 + ((box->top / format->block_height) * map_desc->row_pitch)
2576 + ((box->left / format->block_width) * format->block_byte_count);
2578 else
2580 map_desc->data = base_memory
2581 + (map_desc->row_pitch * box->top)
2582 + (box->left * format->byte_count);
2584 surface->lockedRect.left = box->left;
2585 surface->lockedRect.top = box->top;
2586 surface->lockedRect.right = box->right;
2587 surface->lockedRect.bottom = box->bottom;
2590 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2591 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2593 return WINED3D_OK;
2596 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
2598 HRESULT hr;
2599 struct wined3d_device *device = surface->resource.device;
2600 struct wined3d_context *context = NULL;
2602 TRACE("surface %p, dc %p.\n", surface, dc);
2604 /* Give more detailed info for ddraw. */
2605 if (surface->flags & SFLAG_DCINUSE)
2606 return WINEDDERR_DCALREADYCREATED;
2608 /* Can't GetDC if the surface is locked. */
2609 if (surface->resource.map_count)
2610 return WINED3DERR_INVALIDCALL;
2612 if (device->d3d_initialized)
2613 context = context_acquire(surface->resource.device, NULL);
2615 /* Create a DIB section if there isn't a dc yet. */
2616 if (!surface->hDC)
2618 if (FAILED(hr = surface_create_dib_section(surface)))
2620 if (context)
2621 context_release(context);
2622 return WINED3DERR_INVALIDCALL;
2624 if (!(surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2625 || surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2626 || surface->pbo))
2627 surface->resource.map_binding = WINED3D_LOCATION_DIB;
2630 surface_load_location(surface, context, WINED3D_LOCATION_DIB);
2631 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
2633 if (context)
2634 context_release(context);
2636 surface->flags |= SFLAG_DCINUSE;
2637 surface->resource.map_count++;
2639 *dc = surface->hDC;
2640 TRACE("Returning dc %p.\n", *dc);
2642 return WINED3D_OK;
2645 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
2647 TRACE("surface %p, dc %p.\n", surface, dc);
2649 if (!(surface->flags & SFLAG_DCINUSE))
2650 return WINEDDERR_NODC;
2652 if (surface->hDC != dc)
2654 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
2655 dc, surface->hDC);
2656 return WINEDDERR_NODC;
2659 surface->resource.map_count--;
2660 surface->flags &= ~SFLAG_DCINUSE;
2662 if (surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2663 || (surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2664 && surface->resource.map_binding != WINED3D_LOCATION_DIB))
2666 /* The game Salammbo modifies the surface contents without mapping the surface between
2667 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
2668 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
2669 * Do not only copy the DIB to the map location, but also make sure the map location is
2670 * copied back to the DIB in the next getdc call.
2672 * The same consideration applies to user memory surfaces. */
2673 struct wined3d_device *device = surface->resource.device;
2674 struct wined3d_context *context = NULL;
2676 if (device->d3d_initialized)
2677 context = context_acquire(device, NULL);
2679 surface_load_location(surface, context, surface->resource.map_binding);
2680 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
2681 if (context)
2682 context_release(context);
2685 return WINED3D_OK;
2688 static void read_from_framebuffer(struct wined3d_surface *surface,
2689 struct wined3d_context *old_ctx, DWORD dst_location)
2691 struct wined3d_device *device = surface->resource.device;
2692 const struct wined3d_gl_info *gl_info;
2693 struct wined3d_context *context = old_ctx;
2694 struct wined3d_surface *restore_rt = NULL;
2695 BYTE *mem;
2696 BYTE *row, *top, *bottom;
2697 int i;
2698 BOOL srcIsUpsideDown;
2699 struct wined3d_bo_address data;
2701 surface_get_memory(surface, &data, dst_location);
2703 if (surface != old_ctx->current_rt)
2705 restore_rt = old_ctx->current_rt;
2706 context = context_acquire(device, surface);
2709 context_apply_blit_state(context, device);
2710 gl_info = context->gl_info;
2712 /* Select the correct read buffer, and give some debug output.
2713 * There is no need to keep track of the current read buffer or reset it, every part of the code
2714 * that reads sets the read buffer as desired.
2716 if (wined3d_resource_is_offscreen(&surface->container->resource))
2718 /* Mapping the primary render target which is not on a swapchain.
2719 * Read from the back buffer. */
2720 TRACE("Mapping offscreen render target.\n");
2721 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2722 srcIsUpsideDown = TRUE;
2724 else
2726 /* Onscreen surfaces are always part of a swapchain */
2727 GLenum buffer = surface_get_gl_buffer(surface);
2728 TRACE("Mapping %#x buffer.\n", buffer);
2729 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2730 checkGLcall("glReadBuffer");
2731 srcIsUpsideDown = FALSE;
2734 if (data.buffer_object)
2736 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
2737 checkGLcall("glBindBuffer");
2740 /* Setup pixel store pack state -- to glReadPixels into the correct place */
2741 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH,
2742 wined3d_surface_get_pitch(surface) / surface->resource.format->byte_count);
2743 checkGLcall("glPixelStorei");
2745 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
2746 surface->resource.width, surface->resource.height,
2747 surface->resource.format->glFormat,
2748 surface->resource.format->glType, data.addr);
2749 checkGLcall("glReadPixels");
2751 /* Reset previous pixel store pack state */
2752 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
2753 checkGLcall("glPixelStorei");
2755 if (!srcIsUpsideDown)
2757 /* glReadPixels returns the image upside down, and there is no way to prevent this.
2758 * Flip the lines in software. */
2759 UINT pitch = wined3d_surface_get_pitch(surface);
2761 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
2762 goto error;
2764 if (data.buffer_object)
2766 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
2767 checkGLcall("glMapBuffer");
2769 else
2770 mem = data.addr;
2772 top = mem;
2773 bottom = mem + pitch * (surface->resource.height - 1);
2774 for (i = 0; i < surface->resource.height / 2; i++)
2776 memcpy(row, top, pitch);
2777 memcpy(top, bottom, pitch);
2778 memcpy(bottom, row, pitch);
2779 top += pitch;
2780 bottom -= pitch;
2782 HeapFree(GetProcessHeap(), 0, row);
2784 if (data.buffer_object)
2785 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
2788 error:
2789 if (data.buffer_object)
2791 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2792 checkGLcall("glBindBuffer");
2795 if (restore_rt)
2796 context_restore(context, restore_rt);
2799 /* Read the framebuffer contents into a texture. Note that this function
2800 * doesn't do any kind of flipping. Using this on an onscreen surface will
2801 * result in a flipped D3D texture.
2803 * Context activation is done by the caller. This function may temporarily
2804 * switch to a different context and restore the original one before return. */
2805 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
2807 struct wined3d_device *device = surface->resource.device;
2808 const struct wined3d_gl_info *gl_info;
2809 struct wined3d_context *context = old_ctx;
2810 struct wined3d_surface *restore_rt = NULL;
2812 if (old_ctx->current_rt != surface)
2814 restore_rt = old_ctx->current_rt;
2815 context = context_acquire(device, surface);
2818 gl_info = context->gl_info;
2819 device_invalidate_state(device, STATE_FRAMEBUFFER);
2821 wined3d_texture_prepare_texture(surface->container, context, srgb);
2822 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
2824 TRACE("Reading back offscreen render target %p.\n", surface);
2826 if (wined3d_resource_is_offscreen(&surface->container->resource))
2827 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2828 else
2829 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
2830 checkGLcall("glReadBuffer");
2832 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
2833 0, 0, 0, 0, surface->resource.width, surface->resource.height);
2834 checkGLcall("glCopyTexSubImage2D");
2836 if (restore_rt)
2837 context_restore(context, restore_rt);
2840 static void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2842 if (multisample)
2844 DWORD samples;
2846 if (surface->rb_multisample)
2847 return;
2849 /* TODO: Nvidia exposes their Coverage Sample Anti-Aliasing (CSAA) feature
2850 * through type == MULTISAMPLE_XX and quality != 0. This could be mapped
2851 * to GL_NV_framebuffer_multisample_coverage.
2853 * AMD has a similar feature called Enhanced Quality Anti-Aliasing (EQAA),
2854 * but it does not have an equivalent OpenGL extension. */
2856 /* We advertise as many WINED3D_MULTISAMPLE_NON_MASKABLE quality levels
2857 * as the count of advertised multisample types for the surface format. */
2858 if (surface->resource.multisample_type == WINED3D_MULTISAMPLE_NON_MASKABLE)
2860 const struct wined3d_format *format = surface->resource.format;
2861 unsigned int i, count = 0;
2863 for (i = 0; i < sizeof(format->multisample_types) * 8; ++i)
2865 if (format->multisample_types & 1u << i)
2867 if (surface->resource.multisample_quality == count++)
2868 break;
2871 samples = i + 1;
2873 else
2875 samples = surface->resource.multisample_type;
2878 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2879 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2880 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
2881 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
2882 checkGLcall("glRenderbufferStorageMultisample()");
2883 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2885 else
2887 if (surface->rb_resolved)
2888 return;
2890 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2891 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2892 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
2893 surface->pow2Width, surface->pow2Height);
2894 checkGLcall("glRenderbufferStorage()");
2895 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2899 /* Does a direct frame buffer -> texture copy. Stretching is done with single
2900 * pixel copy calls. */
2901 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2902 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2904 struct wined3d_device *device = dst_surface->resource.device;
2905 const struct wined3d_gl_info *gl_info;
2906 float xrel, yrel;
2907 struct wined3d_context *context;
2908 BOOL upsidedown = FALSE;
2909 RECT dst_rect = *dst_rect_in;
2911 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2912 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2914 if(dst_rect.top > dst_rect.bottom) {
2915 UINT tmp = dst_rect.bottom;
2916 dst_rect.bottom = dst_rect.top;
2917 dst_rect.top = tmp;
2918 upsidedown = TRUE;
2921 context = context_acquire(device, src_surface);
2922 gl_info = context->gl_info;
2923 context_apply_blit_state(context, device);
2924 wined3d_texture_load(dst_surface->container, context, FALSE);
2926 /* Bind the target texture */
2927 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
2928 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
2930 TRACE("Reading from an offscreen target\n");
2931 upsidedown = !upsidedown;
2932 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2934 else
2936 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
2938 checkGLcall("glReadBuffer");
2940 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
2941 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
2943 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2945 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2947 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2948 ERR("Texture filtering not supported in direct blit.\n");
2950 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2951 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2953 ERR("Texture filtering not supported in direct blit\n");
2956 if (upsidedown
2957 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2958 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2960 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
2961 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2962 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
2963 src_rect->left, src_surface->resource.height - src_rect->bottom,
2964 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2966 else
2968 LONG row;
2969 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
2970 /* I have to process this row by row to swap the image,
2971 * otherwise it would be upside down, so stretching in y direction
2972 * doesn't cost extra time
2974 * However, stretching in x direction can be avoided if not necessary
2976 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
2977 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2979 /* Well, that stuff works, but it's very slow.
2980 * find a better way instead
2982 LONG col;
2984 for (col = dst_rect.left; col < dst_rect.right; ++col)
2986 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2987 dst_rect.left + col /* x offset */, row /* y offset */,
2988 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
2991 else
2993 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2994 dst_rect.left /* x offset */, row /* y offset */,
2995 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
2999 checkGLcall("glCopyTexSubImage2D");
3001 context_release(context);
3003 /* The texture is now most up to date - If the surface is a render target
3004 * and has a drawable, this path is never entered. */
3005 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3006 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3009 /* Uses the hardware to stretch and flip the image */
3010 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3011 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3013 struct wined3d_device *device = dst_surface->resource.device;
3014 GLuint src, backup = 0;
3015 float left, right, top, bottom; /* Texture coordinates */
3016 UINT fbwidth = src_surface->resource.width;
3017 UINT fbheight = src_surface->resource.height;
3018 const struct wined3d_gl_info *gl_info;
3019 struct wined3d_context *context;
3020 GLenum drawBuffer = GL_BACK;
3021 GLenum texture_target;
3022 BOOL noBackBufferBackup;
3023 BOOL src_offscreen;
3024 BOOL upsidedown = FALSE;
3025 RECT dst_rect = *dst_rect_in;
3027 TRACE("Using hwstretch blit\n");
3028 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3029 context = context_acquire(device, src_surface);
3030 gl_info = context->gl_info;
3031 context_apply_blit_state(context, device);
3032 wined3d_texture_load(dst_surface->container, context, FALSE);
3034 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
3035 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3036 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3038 /* Get it a description */
3039 wined3d_texture_load(src_surface->container, context, FALSE);
3042 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3043 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3045 if (context->aux_buffers >= 2)
3047 /* Got more than one aux buffer? Use the 2nd aux buffer */
3048 drawBuffer = GL_AUX1;
3050 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3052 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3053 drawBuffer = GL_AUX0;
3056 if (noBackBufferBackup)
3058 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3059 checkGLcall("glGenTextures");
3060 context_bind_texture(context, GL_TEXTURE_2D, backup);
3061 texture_target = GL_TEXTURE_2D;
3063 else
3065 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3066 * we are reading from the back buffer, the backup can be used as source texture
3068 texture_target = src_surface->texture_target;
3069 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3070 gl_info->gl_ops.gl.p_glEnable(texture_target);
3071 checkGLcall("glEnable(texture_target)");
3073 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3074 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3077 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3078 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3080 if(dst_rect.top > dst_rect.bottom) {
3081 UINT tmp = dst_rect.bottom;
3082 dst_rect.bottom = dst_rect.top;
3083 dst_rect.top = tmp;
3084 upsidedown = TRUE;
3087 if (src_offscreen)
3089 TRACE("Reading from an offscreen target\n");
3090 upsidedown = !upsidedown;
3091 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3093 else
3095 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3098 /* TODO: Only back up the part that will be overwritten */
3099 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3101 checkGLcall("glCopyTexSubImage2D");
3103 /* No issue with overriding these - the sampler is dirty due to blit usage */
3104 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
3105 checkGLcall("glTexParameteri");
3106 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3107 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
3108 checkGLcall("glTexParameteri");
3110 if (!src_surface->container->swapchain
3111 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
3113 src = backup ? backup : src_surface->container->texture_rgb.name;
3115 else
3117 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3118 checkGLcall("glReadBuffer(GL_FRONT)");
3120 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3121 checkGLcall("glGenTextures(1, &src)");
3122 context_bind_texture(context, GL_TEXTURE_2D, src);
3124 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3125 * out for power of 2 sizes
3127 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3128 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3129 checkGLcall("glTexImage2D");
3130 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3132 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3133 checkGLcall("glTexParameteri");
3134 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3135 checkGLcall("glTexParameteri");
3137 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3138 checkGLcall("glReadBuffer(GL_BACK)");
3140 if (texture_target != GL_TEXTURE_2D)
3142 gl_info->gl_ops.gl.p_glDisable(texture_target);
3143 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3144 texture_target = GL_TEXTURE_2D;
3147 checkGLcall("glEnd and previous");
3149 left = src_rect->left;
3150 right = src_rect->right;
3152 if (!upsidedown)
3154 top = src_surface->resource.height - src_rect->top;
3155 bottom = src_surface->resource.height - src_rect->bottom;
3157 else
3159 top = src_surface->resource.height - src_rect->bottom;
3160 bottom = src_surface->resource.height - src_rect->top;
3163 if (src_surface->container->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
3165 left /= src_surface->pow2Width;
3166 right /= src_surface->pow2Width;
3167 top /= src_surface->pow2Height;
3168 bottom /= src_surface->pow2Height;
3171 /* draw the source texture stretched and upside down. The correct surface is bound already */
3172 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3173 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3175 context_set_draw_buffer(context, drawBuffer);
3176 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3178 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3179 /* bottom left */
3180 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3181 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3183 /* top left */
3184 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3185 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3187 /* top right */
3188 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3189 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3191 /* bottom right */
3192 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3193 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3194 gl_info->gl_ops.gl.p_glEnd();
3195 checkGLcall("glEnd and previous");
3197 if (texture_target != dst_surface->texture_target)
3199 gl_info->gl_ops.gl.p_glDisable(texture_target);
3200 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3201 texture_target = dst_surface->texture_target;
3204 /* Now read the stretched and upside down image into the destination texture */
3205 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
3206 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
3208 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3209 0, 0, /* We blitted the image to the origin */
3210 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3211 checkGLcall("glCopyTexSubImage2D");
3213 if (drawBuffer == GL_BACK)
3215 /* Write the back buffer backup back. */
3216 if (backup)
3218 if (texture_target != GL_TEXTURE_2D)
3220 gl_info->gl_ops.gl.p_glDisable(texture_target);
3221 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3222 texture_target = GL_TEXTURE_2D;
3224 context_bind_texture(context, GL_TEXTURE_2D, backup);
3226 else
3228 if (texture_target != src_surface->texture_target)
3230 gl_info->gl_ops.gl.p_glDisable(texture_target);
3231 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3232 texture_target = src_surface->texture_target;
3234 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3237 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3238 /* top left */
3239 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3240 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3242 /* bottom left */
3243 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3244 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3246 /* bottom right */
3247 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3248 (float)fbheight / (float)src_surface->pow2Height);
3249 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3251 /* top right */
3252 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3253 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3254 gl_info->gl_ops.gl.p_glEnd();
3256 gl_info->gl_ops.gl.p_glDisable(texture_target);
3257 checkGLcall("glDisable(texture_target)");
3259 /* Cleanup */
3260 if (src != src_surface->container->texture_rgb.name && src != backup)
3262 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3263 checkGLcall("glDeleteTextures(1, &src)");
3265 if (backup)
3267 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3268 checkGLcall("glDeleteTextures(1, &backup)");
3271 if (wined3d_settings.strict_draw_ordering)
3272 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3274 context_release(context);
3276 /* The texture is now most up to date - If the surface is a render target
3277 * and has a drawable, this path is never entered. */
3278 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3279 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3282 /* Front buffer coordinates are always full screen coordinates, but our GL
3283 * drawable is limited to the window's client area. The sysmem and texture
3284 * copies do have the full screen size. Note that GL has a bottom-left
3285 * origin, while D3D has a top-left origin. */
3286 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3288 UINT drawable_height;
3290 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3292 POINT offset = {0, 0};
3293 RECT windowsize;
3295 ScreenToClient(window, &offset);
3296 OffsetRect(rect, offset.x, offset.y);
3298 GetClientRect(window, &windowsize);
3299 drawable_height = windowsize.bottom - windowsize.top;
3301 else
3303 drawable_height = surface->resource.height;
3306 rect->top = drawable_height - rect->top;
3307 rect->bottom = drawable_height - rect->bottom;
3310 /* Context activation is done by the caller. */
3311 static void surface_blt_to_drawable(const struct wined3d_device *device,
3312 struct wined3d_context *old_ctx,
3313 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3314 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3315 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3317 const struct wined3d_gl_info *gl_info;
3318 struct wined3d_context *context = old_ctx;
3319 struct wined3d_surface *restore_rt = NULL;
3320 RECT src_rect, dst_rect;
3322 src_rect = *src_rect_in;
3323 dst_rect = *dst_rect_in;
3326 if (old_ctx->current_rt != dst_surface)
3328 restore_rt = old_ctx->current_rt;
3329 context = context_acquire(device, dst_surface);
3332 gl_info = context->gl_info;
3334 /* Make sure the surface is up-to-date. This should probably use
3335 * surface_load_location() and worry about the destination surface too,
3336 * unless we're overwriting it completely. */
3337 wined3d_texture_load(src_surface->container, context, FALSE);
3339 /* Activate the destination context, set it up for blitting */
3340 context_apply_blit_state(context, device);
3342 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3343 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3345 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
3347 if (alpha_test)
3349 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3350 checkGLcall("glEnable(GL_ALPHA_TEST)");
3352 /* For P8 surfaces, the alpha component contains the palette index.
3353 * Which means that the colorkey is one of the palette entries. In
3354 * other cases pixels that should be masked away have alpha set to 0. */
3355 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3356 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3357 (float)src_surface->container->async.src_blt_color_key.color_space_low_value / 255.0f);
3358 else
3359 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3360 checkGLcall("glAlphaFunc");
3362 else
3364 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3365 checkGLcall("glDisable(GL_ALPHA_TEST)");
3368 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3370 if (alpha_test)
3372 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3373 checkGLcall("glDisable(GL_ALPHA_TEST)");
3376 /* Leave the opengl state valid for blitting */
3377 device->blitter->unset_shader(context->gl_info);
3379 if (wined3d_settings.strict_draw_ordering
3380 || (dst_surface->container->swapchain
3381 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3382 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3384 if (restore_rt)
3385 context_restore(context, restore_rt);
3388 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3390 struct wined3d_device *device = s->resource.device;
3391 const struct blit_shader *blitter;
3393 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, WINED3D_BLIT_OP_COLOR_FILL,
3394 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
3395 if (!blitter)
3397 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3398 return WINED3DERR_INVALIDCALL;
3401 return blitter->color_fill(device, s, rect, color);
3404 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3405 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3406 enum wined3d_texture_filter_type filter)
3408 struct wined3d_device *device = dst_surface->resource.device;
3409 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3410 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3412 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3413 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3414 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3416 /* Get the swapchain. One of the surfaces has to be a primary surface */
3417 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3419 WARN("Destination is in sysmem, rejecting gl blt\n");
3420 return WINED3DERR_INVALIDCALL;
3423 dst_swapchain = dst_surface->container->swapchain;
3425 if (src_surface)
3427 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3429 WARN("Src is in sysmem, rejecting gl blt\n");
3430 return WINED3DERR_INVALIDCALL;
3433 src_swapchain = src_surface->container->swapchain;
3435 else
3437 src_swapchain = NULL;
3440 /* Early sort out of cases where no render target is used */
3441 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
3443 TRACE("No surface is render target, not using hardware blit.\n");
3444 return WINED3DERR_INVALIDCALL;
3447 /* No destination color keying supported */
3448 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
3450 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3451 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3452 return WINED3DERR_INVALIDCALL;
3455 if (dst_swapchain && dst_swapchain == src_swapchain)
3457 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3458 return WINED3DERR_INVALIDCALL;
3461 if (dst_swapchain && src_swapchain)
3463 FIXME("Implement hardware blit between two different swapchains\n");
3464 return WINED3DERR_INVALIDCALL;
3467 if (dst_swapchain)
3469 /* Handled with regular texture -> swapchain blit */
3470 if (src_surface == rt)
3471 TRACE("Blit from active render target to a swapchain\n");
3473 else if (src_swapchain && dst_surface == rt)
3475 FIXME("Implement blit from a swapchain to the active render target\n");
3476 return WINED3DERR_INVALIDCALL;
3479 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
3481 /* Blit from render target to texture */
3482 BOOL stretchx;
3484 /* P8 read back is not implemented */
3485 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
3486 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
3488 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3489 return WINED3DERR_INVALIDCALL;
3492 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3494 TRACE("Color keying not supported by frame buffer to texture blit\n");
3495 return WINED3DERR_INVALIDCALL;
3496 /* Destination color key is checked above */
3499 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
3500 stretchx = TRUE;
3501 else
3502 stretchx = FALSE;
3504 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3505 * flip the image nor scale it.
3507 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3508 * -> If the app wants an image width an unscaled width, copy it line per line
3509 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
3510 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3511 * back buffer. This is slower than reading line per line, thus not used for flipping
3512 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3513 * pixel by pixel. */
3514 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
3515 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
3517 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
3518 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
3520 else
3522 TRACE("Using hardware stretching to flip / stretch the texture.\n");
3523 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
3526 surface_evict_sysmem(dst_surface);
3528 return WINED3D_OK;
3531 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3532 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3533 return WINED3DERR_INVALIDCALL;
3536 /* Context activation is done by the caller. */
3537 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
3538 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
3540 struct wined3d_device *device = surface->resource.device;
3541 const struct wined3d_gl_info *gl_info = context->gl_info;
3542 GLint compare_mode = GL_NONE;
3543 struct blt_info info;
3544 GLint old_binding = 0;
3545 RECT rect;
3547 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
3549 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
3550 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
3551 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3552 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
3553 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
3554 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
3555 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
3556 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
3557 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3558 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
3559 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
3561 SetRect(&rect, 0, h, w, 0);
3562 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
3563 context_active_texture(context, context->gl_info, 0);
3564 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
3565 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
3566 if (gl_info->supported[ARB_SHADOW])
3568 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
3569 if (compare_mode != GL_NONE)
3570 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
3573 device->shader_backend->shader_select_depth_blt(device->shader_priv,
3574 gl_info, info.tex_type, &surface->ds_current_size);
3576 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
3577 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
3578 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
3579 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
3580 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
3581 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
3582 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
3583 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
3584 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
3585 gl_info->gl_ops.gl.p_glEnd();
3587 if (compare_mode != GL_NONE)
3588 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
3589 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
3591 gl_info->gl_ops.gl.p_glPopAttrib();
3593 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
3596 void surface_modify_ds_location(struct wined3d_surface *surface,
3597 DWORD location, UINT w, UINT h)
3599 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
3601 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
3602 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
3603 wined3d_texture_set_dirty(surface->container);
3605 surface->ds_current_size.cx = w;
3606 surface->ds_current_size.cy = h;
3607 surface->locations = location;
3610 /* Context activation is done by the caller. */
3611 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3613 const struct wined3d_gl_info *gl_info = context->gl_info;
3614 struct wined3d_device *device = surface->resource.device;
3615 GLsizei w, h;
3617 TRACE("surface %p, context %p, new location %#x.\n", surface, context, location);
3619 /* TODO: Make this work for modes other than FBO */
3620 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3622 if (!(surface->locations & location))
3624 w = surface->ds_current_size.cx;
3625 h = surface->ds_current_size.cy;
3626 surface->ds_current_size.cx = 0;
3627 surface->ds_current_size.cy = 0;
3629 else
3631 w = surface->resource.width;
3632 h = surface->resource.height;
3635 if (surface->ds_current_size.cx == surface->resource.width
3636 && surface->ds_current_size.cy == surface->resource.height)
3638 TRACE("Location (%#x) is already up to date.\n", location);
3639 return;
3642 if (surface->current_renderbuffer)
3644 FIXME("Not supported with fixed up depth stencil.\n");
3645 return;
3648 wined3d_surface_prepare(surface, context, location);
3649 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3651 TRACE("Surface was discarded, no need copy data.\n");
3652 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
3653 surface->locations |= location;
3654 surface->ds_current_size.cx = surface->resource.width;
3655 surface->ds_current_size.cy = surface->resource.height;
3656 return;
3659 if (!surface->locations)
3661 FIXME("No up to date depth stencil location.\n");
3662 surface->locations |= location;
3663 surface->ds_current_size.cx = surface->resource.width;
3664 surface->ds_current_size.cy = surface->resource.height;
3665 return;
3668 if (location == WINED3D_LOCATION_TEXTURE_RGB)
3670 GLint old_binding = 0;
3671 GLenum bind_target;
3673 /* The render target is allowed to be smaller than the depth/stencil
3674 * buffer, so the onscreen depth/stencil buffer is potentially smaller
3675 * than the offscreen surface. Don't overwrite the offscreen surface
3676 * with undefined data. */
3677 w = min(w, context->swapchain->desc.backbuffer_width);
3678 h = min(h, context->swapchain->desc.backbuffer_height);
3680 TRACE("Copying onscreen depth buffer to depth texture.\n");
3682 if (!device->depth_blt_texture)
3683 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
3685 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3686 * directly on the FBO texture. That's because we need to flip. */
3687 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3688 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3689 NULL, WINED3D_LOCATION_DRAWABLE);
3690 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
3692 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
3693 bind_target = GL_TEXTURE_RECTANGLE_ARB;
3695 else
3697 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3698 bind_target = GL_TEXTURE_2D;
3700 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
3701 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
3702 * internal format, because the internal format might include stencil
3703 * data. In principle we should copy stencil data as well, but unless
3704 * the driver supports stencil export it's hard to do, and doesn't
3705 * seem to be needed in practice. If the hardware doesn't support
3706 * writing stencil data, the glCopyTexImage2D() call might trigger
3707 * software fallbacks. */
3708 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
3709 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3710 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3711 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3712 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3713 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
3714 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
3716 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3717 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
3718 context_set_draw_buffer(context, GL_NONE);
3720 /* Do the actual blit */
3721 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
3722 checkGLcall("depth_blt");
3724 context_invalidate_state(context, STATE_FRAMEBUFFER);
3726 if (wined3d_settings.strict_draw_ordering)
3727 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3729 else if (location == WINED3D_LOCATION_DRAWABLE)
3731 TRACE("Copying depth texture to onscreen depth buffer.\n");
3733 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3734 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3735 NULL, WINED3D_LOCATION_DRAWABLE);
3736 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
3737 0, surface->pow2Height - h, w, h, surface->texture_target);
3738 checkGLcall("depth_blt");
3740 context_invalidate_state(context, STATE_FRAMEBUFFER);
3742 if (wined3d_settings.strict_draw_ordering)
3743 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3745 else
3747 ERR("Invalid location (%#x) specified.\n", location);
3750 surface->locations |= location;
3751 surface->ds_current_size.cx = surface->resource.width;
3752 surface->ds_current_size.cy = surface->resource.height;
3755 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
3757 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3759 surface->locations |= location;
3762 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
3764 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3766 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3767 wined3d_texture_set_dirty(surface->container);
3768 surface->locations &= ~location;
3770 if (!surface->locations)
3771 ERR("Surface %p does not have any up to date location.\n", surface);
3774 static DWORD resource_access_from_location(DWORD location)
3776 switch (location)
3778 case WINED3D_LOCATION_SYSMEM:
3779 case WINED3D_LOCATION_USER_MEMORY:
3780 case WINED3D_LOCATION_DIB:
3781 case WINED3D_LOCATION_BUFFER:
3782 return WINED3D_RESOURCE_ACCESS_CPU;
3784 case WINED3D_LOCATION_DRAWABLE:
3785 case WINED3D_LOCATION_TEXTURE_SRGB:
3786 case WINED3D_LOCATION_TEXTURE_RGB:
3787 case WINED3D_LOCATION_RB_MULTISAMPLE:
3788 case WINED3D_LOCATION_RB_RESOLVED:
3789 return WINED3D_RESOURCE_ACCESS_GPU;
3791 default:
3792 FIXME("Unhandled location %#x.\n", location);
3793 return 0;
3797 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
3799 struct wined3d_device *device = surface->resource.device;
3800 struct wined3d_context *context;
3801 const struct wined3d_gl_info *gl_info;
3802 struct wined3d_bo_address dst, src;
3803 UINT size = surface->resource.size;
3805 surface_get_memory(surface, &dst, location);
3806 surface_get_memory(surface, &src, surface->locations);
3808 if (dst.buffer_object)
3810 context = context_acquire(device, NULL);
3811 gl_info = context->gl_info;
3812 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
3813 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr));
3814 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
3815 checkGLcall("Upload PBO");
3816 context_release(context);
3817 return;
3819 if (src.buffer_object)
3821 context = context_acquire(device, NULL);
3822 gl_info = context->gl_info;
3823 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
3824 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr));
3825 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
3826 checkGLcall("Download PBO");
3827 context_release(context);
3828 return;
3830 memcpy(dst.addr, src.addr, size);
3833 /* Context activation is done by the caller. */
3834 static void surface_load_sysmem(struct wined3d_surface *surface,
3835 struct wined3d_context *context, DWORD dst_location)
3837 const struct wined3d_gl_info *gl_info = context->gl_info;
3839 if (surface->locations & surface_simple_locations)
3841 surface_copy_simple_location(surface, dst_location);
3842 return;
3845 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
3846 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3848 /* Download the surface to system memory. */
3849 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3851 wined3d_texture_bind_and_dirtify(surface->container, context,
3852 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
3853 surface_download_data(surface, gl_info, dst_location);
3855 return;
3858 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
3860 read_from_framebuffer(surface, context, dst_location);
3861 return;
3864 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
3865 surface, wined3d_debug_location(surface->locations));
3868 /* Context activation is done by the caller. */
3869 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
3870 struct wined3d_context *context)
3872 RECT r;
3874 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3875 && wined3d_resource_is_offscreen(&surface->container->resource))
3877 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
3878 return WINED3DERR_INVALIDCALL;
3881 surface_get_rect(surface, NULL, &r);
3882 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3883 surface_blt_to_drawable(surface->resource.device, context,
3884 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
3886 return WINED3D_OK;
3889 static HRESULT surface_load_texture(struct wined3d_surface *surface,
3890 struct wined3d_context *context, BOOL srgb)
3892 const struct wined3d_gl_info *gl_info = context->gl_info;
3893 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
3894 struct wined3d_device *device = surface->resource.device;
3895 const struct wined3d_color_key_conversion *conversion;
3896 struct wined3d_texture *texture = surface->container;
3897 UINT width, src_pitch, dst_pitch;
3898 struct wined3d_bo_address data;
3899 struct wined3d_format format;
3900 POINT dst_point = {0, 0};
3901 BYTE *mem = NULL;
3903 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
3904 && wined3d_resource_is_offscreen(&texture->resource)
3905 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
3907 surface_load_fb_texture(surface, srgb, context);
3909 return WINED3D_OK;
3912 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
3913 && (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
3914 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3915 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3916 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3918 if (srgb)
3919 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
3920 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
3921 else
3922 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
3923 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
3925 return WINED3D_OK;
3928 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
3929 && (!srgb || (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
3930 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3931 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3932 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3934 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
3935 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
3936 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
3937 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3939 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
3940 &rect, surface, dst_location, &rect);
3942 return WINED3D_OK;
3945 /* Upload from system memory */
3947 if (srgb)
3949 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
3950 == WINED3D_LOCATION_TEXTURE_RGB)
3952 /* Performance warning... */
3953 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
3954 surface_prepare_map_memory(surface);
3955 surface_load_location(surface, context, surface->resource.map_binding);
3958 else
3960 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
3961 == WINED3D_LOCATION_TEXTURE_SRGB)
3963 /* Performance warning... */
3964 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
3965 surface_prepare_map_memory(surface);
3966 surface_load_location(surface, context, surface->resource.map_binding);
3970 if (!(surface->locations & surface_simple_locations))
3972 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
3973 /* Lets hope we get it from somewhere... */
3974 surface_prepare_system_memory(surface);
3975 surface_load_location(surface, context, WINED3D_LOCATION_SYSMEM);
3978 wined3d_texture_prepare_texture(texture, context, srgb);
3979 wined3d_texture_bind_and_dirtify(texture, context, srgb);
3981 width = surface->resource.width;
3982 src_pitch = wined3d_surface_get_pitch(surface);
3984 format = *texture->resource.format;
3985 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
3986 format = *wined3d_get_format(gl_info, conversion->dst_format);
3988 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
3989 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
3990 * getting called. */
3991 if ((format.convert || conversion) && surface->pbo)
3993 TRACE("Removing the pbo attached to surface %p.\n", surface);
3995 if (surface->flags & SFLAG_DIBSECTION)
3996 surface->resource.map_binding = WINED3D_LOCATION_DIB;
3997 else
3998 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
4000 surface_prepare_map_memory(surface);
4001 surface_load_location(surface, context, surface->resource.map_binding);
4002 surface_remove_pbo(surface, gl_info);
4005 surface_get_memory(surface, &data, surface->locations);
4006 if (format.convert)
4008 /* This code is entered for texture formats which need a fixup. */
4009 UINT height = surface->resource.height;
4011 format.byte_count = format.conv_byte_count;
4012 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4014 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4016 ERR("Out of memory (%u).\n", dst_pitch * height);
4017 context_release(context);
4018 return E_OUTOFMEMORY;
4020 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4021 dst_pitch, dst_pitch * height, width, height, 1);
4022 src_pitch = dst_pitch;
4023 data.addr = mem;
4025 else if (conversion)
4027 /* This code is only entered for color keying fixups */
4028 struct wined3d_palette *palette = NULL;
4029 UINT height = surface->resource.height;
4031 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4032 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4034 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4036 ERR("Out of memory (%u).\n", dst_pitch * height);
4037 context_release(context);
4038 return E_OUTOFMEMORY;
4040 if (texture->swapchain && texture->swapchain->palette)
4041 palette = texture->swapchain->palette;
4042 conversion->convert(data.addr, src_pitch, mem, dst_pitch,
4043 width, height, palette, &texture->async.gl_color_key);
4044 src_pitch = dst_pitch;
4045 data.addr = mem;
4048 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
4049 src_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
4051 HeapFree(GetProcessHeap(), 0, mem);
4053 return WINED3D_OK;
4056 /* Context activation is done by the caller. */
4057 static void surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
4058 DWORD dst_location)
4060 const RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4061 DWORD src_location;
4063 if (surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE)
4064 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
4065 else if (surface->locations & WINED3D_LOCATION_RB_RESOLVED)
4066 src_location = WINED3D_LOCATION_RB_RESOLVED;
4067 else if (surface->locations & WINED3D_LOCATION_TEXTURE_SRGB)
4068 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
4069 else /* surface_blt_fbo will load the source location if necessary. */
4070 src_location = WINED3D_LOCATION_TEXTURE_RGB;
4072 surface_blt_fbo(surface->resource.device, context, WINED3D_TEXF_POINT,
4073 surface, src_location, &rect, surface, dst_location, &rect);
4076 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
4077 HRESULT surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4079 HRESULT hr;
4081 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4083 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4085 if (location == WINED3D_LOCATION_TEXTURE_RGB
4086 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4088 surface_load_ds_location(surface, context, location);
4089 return WINED3D_OK;
4091 else if (location & surface->locations
4092 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
4094 /* Already up to date, nothing to do. */
4095 return WINED3D_OK;
4097 else
4099 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4100 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4101 return WINED3DERR_INVALIDCALL;
4105 if (surface->locations & location)
4107 TRACE("Location already up to date.\n");
4108 return WINED3D_OK;
4111 if (WARN_ON(d3d_surface))
4113 DWORD required_access = resource_access_from_location(location);
4114 if ((surface->resource.access_flags & required_access) != required_access)
4115 WARN("Operation requires %#x access, but surface only has %#x.\n",
4116 required_access, surface->resource.access_flags);
4119 if (!surface->locations)
4121 ERR("Surface %p does not have any up to date location.\n", surface);
4122 return WINED3DERR_INVALIDCALL;
4125 switch (location)
4127 case WINED3D_LOCATION_DIB:
4128 case WINED3D_LOCATION_USER_MEMORY:
4129 case WINED3D_LOCATION_SYSMEM:
4130 case WINED3D_LOCATION_BUFFER:
4131 surface_load_sysmem(surface, context, location);
4132 break;
4134 case WINED3D_LOCATION_DRAWABLE:
4135 if (FAILED(hr = surface_load_drawable(surface, context)))
4136 return hr;
4137 break;
4139 case WINED3D_LOCATION_RB_RESOLVED:
4140 case WINED3D_LOCATION_RB_MULTISAMPLE:
4141 surface_load_renderbuffer(surface, context, location);
4142 break;
4144 case WINED3D_LOCATION_TEXTURE_RGB:
4145 case WINED3D_LOCATION_TEXTURE_SRGB:
4146 if (FAILED(hr = surface_load_texture(surface, context,
4147 location == WINED3D_LOCATION_TEXTURE_SRGB)))
4148 return hr;
4149 break;
4151 default:
4152 ERR("Don't know how to handle location %#x.\n", location);
4153 break;
4156 surface_validate_location(surface, location);
4158 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
4159 surface_evict_sysmem(surface);
4161 return WINED3D_OK;
4164 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
4165 /* Context activation is done by the caller. */
4166 static void ffp_blit_free(struct wined3d_device *device) { }
4168 /* Context activation is done by the caller. */
4169 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4170 const struct wined3d_color_key *color_key)
4172 const struct wined3d_gl_info *gl_info = context->gl_info;
4174 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
4175 checkGLcall("glEnable(target)");
4177 return WINED3D_OK;
4180 /* Context activation is done by the caller. */
4181 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
4183 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
4184 checkGLcall("glDisable(GL_TEXTURE_2D)");
4185 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4187 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4188 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4190 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4192 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
4193 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4197 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
4198 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4199 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4200 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4202 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4204 TRACE("Source or destination is in system memory.\n");
4205 return FALSE;
4208 switch (blit_op)
4210 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
4211 if (d3d_info->shader_color_key)
4213 TRACE("Color keying requires converted textures.\n");
4214 return FALSE;
4216 case WINED3D_BLIT_OP_COLOR_BLIT:
4217 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
4218 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4220 TRACE("Checking support for fixup:\n");
4221 dump_color_fixup_desc(src_format->color_fixup);
4224 /* We only support identity conversions. */
4225 if (!is_identity_fixup(src_format->color_fixup)
4226 || !is_identity_fixup(dst_format->color_fixup))
4228 TRACE("Fixups are not supported.\n");
4229 return FALSE;
4232 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4234 TRACE("Can only blit to render targets.\n");
4235 return FALSE;
4237 return TRUE;
4239 case WINED3D_BLIT_OP_COLOR_FILL:
4240 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4242 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
4243 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4244 return FALSE;
4246 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4248 TRACE("Color fill not supported\n");
4249 return FALSE;
4252 /* FIXME: We should reject color fills on formats with fixups,
4253 * but this would break P8 color fills for example. */
4255 return TRUE;
4257 case WINED3D_BLIT_OP_DEPTH_FILL:
4258 return TRUE;
4260 default:
4261 TRACE("Unsupported blit_op=%d\n", blit_op);
4262 return FALSE;
4266 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4267 const RECT *dst_rect, const struct wined3d_color *color)
4269 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4270 struct wined3d_rendertarget_view *view;
4271 struct wined3d_fb_state fb = {&view, NULL};
4272 HRESULT hr;
4274 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4275 NULL, &wined3d_null_parent_ops, &view)))
4277 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4278 return hr;
4281 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4282 wined3d_rendertarget_view_decref(view);
4284 return WINED3D_OK;
4287 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4288 const RECT *dst_rect, float depth)
4290 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4291 struct wined3d_fb_state fb = {NULL, NULL};
4292 HRESULT hr;
4294 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4295 NULL, &wined3d_null_parent_ops, &fb.depth_stencil)))
4297 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4298 return hr;
4301 device_clear_render_targets(device, 0, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4302 wined3d_rendertarget_view_decref(fb.depth_stencil);
4304 return WINED3D_OK;
4307 static void ffp_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
4308 struct wined3d_surface *src_surface, const RECT *src_rect,
4309 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4310 const struct wined3d_color_key *color_key)
4312 struct wined3d_context *context;
4314 /* Blit from offscreen surface to render target */
4315 struct wined3d_color_key old_blt_key = src_surface->container->async.src_blt_color_key;
4316 DWORD old_color_key_flags = src_surface->container->async.color_key_flags;
4318 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4320 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT, color_key);
4322 context = context_acquire(device, dst_surface);
4324 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
4325 glEnable(GL_ALPHA_TEST);
4327 surface_blt_to_drawable(device, context, filter,
4328 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
4330 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
4331 glDisable(GL_ALPHA_TEST);
4333 context_release(context);
4335 /* Restore the color key parameters */
4336 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT,
4337 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
4339 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
4340 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
4343 const struct blit_shader ffp_blit = {
4344 ffp_blit_alloc,
4345 ffp_blit_free,
4346 ffp_blit_set,
4347 ffp_blit_unset,
4348 ffp_blit_supported,
4349 ffp_blit_color_fill,
4350 ffp_blit_depth_fill,
4351 ffp_blit_blit_surface,
4354 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4356 return WINED3D_OK;
4359 /* Context activation is done by the caller. */
4360 static void cpu_blit_free(struct wined3d_device *device)
4364 /* Context activation is done by the caller. */
4365 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4366 const struct wined3d_color_key *color_key)
4368 return WINED3D_OK;
4371 /* Context activation is done by the caller. */
4372 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4376 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
4377 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4378 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4379 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4381 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4383 return TRUE;
4386 return FALSE;
4389 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4390 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4391 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4393 UINT row_block_count;
4394 const BYTE *src_row;
4395 BYTE *dst_row;
4396 UINT x, y;
4398 src_row = src_data;
4399 dst_row = dst_data;
4401 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4403 if (!flags)
4405 for (y = 0; y < update_h; y += format->block_height)
4407 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4408 src_row += src_pitch;
4409 dst_row += dst_pitch;
4412 return WINED3D_OK;
4415 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4417 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4419 switch (format->id)
4421 case WINED3DFMT_DXT1:
4422 for (y = 0; y < update_h; y += format->block_height)
4424 struct block
4426 WORD color[2];
4427 BYTE control_row[4];
4430 const struct block *s = (const struct block *)src_row;
4431 struct block *d = (struct block *)dst_row;
4433 for (x = 0; x < row_block_count; ++x)
4435 d[x].color[0] = s[x].color[0];
4436 d[x].color[1] = s[x].color[1];
4437 d[x].control_row[0] = s[x].control_row[3];
4438 d[x].control_row[1] = s[x].control_row[2];
4439 d[x].control_row[2] = s[x].control_row[1];
4440 d[x].control_row[3] = s[x].control_row[0];
4442 src_row -= src_pitch;
4443 dst_row += dst_pitch;
4445 return WINED3D_OK;
4447 case WINED3DFMT_DXT2:
4448 case WINED3DFMT_DXT3:
4449 for (y = 0; y < update_h; y += format->block_height)
4451 struct block
4453 WORD alpha_row[4];
4454 WORD color[2];
4455 BYTE control_row[4];
4458 const struct block *s = (const struct block *)src_row;
4459 struct block *d = (struct block *)dst_row;
4461 for (x = 0; x < row_block_count; ++x)
4463 d[x].alpha_row[0] = s[x].alpha_row[3];
4464 d[x].alpha_row[1] = s[x].alpha_row[2];
4465 d[x].alpha_row[2] = s[x].alpha_row[1];
4466 d[x].alpha_row[3] = s[x].alpha_row[0];
4467 d[x].color[0] = s[x].color[0];
4468 d[x].color[1] = s[x].color[1];
4469 d[x].control_row[0] = s[x].control_row[3];
4470 d[x].control_row[1] = s[x].control_row[2];
4471 d[x].control_row[2] = s[x].control_row[1];
4472 d[x].control_row[3] = s[x].control_row[0];
4474 src_row -= src_pitch;
4475 dst_row += dst_pitch;
4477 return WINED3D_OK;
4479 default:
4480 FIXME("Compressed flip not implemented for format %s.\n",
4481 debug_d3dformat(format->id));
4482 return E_NOTIMPL;
4486 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
4487 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
4489 return E_NOTIMPL;
4492 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4493 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4494 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4496 const struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
4497 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
4498 const struct wined3d_format *src_format, *dst_format;
4499 unsigned int src_fmt_flags, dst_fmt_flags;
4500 struct wined3d_texture *src_texture = NULL;
4501 struct wined3d_map_desc dst_map, src_map;
4502 const BYTE *sbase = NULL;
4503 HRESULT hr = WINED3D_OK;
4504 const BYTE *sbuf;
4505 BYTE *dbuf;
4506 int x, y;
4508 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4509 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4510 flags, fx, debug_d3dtexturefiltertype(filter));
4512 if (src_surface == dst_surface)
4514 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
4515 src_map = dst_map;
4516 src_format = dst_surface->resource.format;
4517 dst_format = src_format;
4518 dst_fmt_flags = dst_surface->container->resource.format_flags;
4519 src_fmt_flags = dst_fmt_flags;
4521 else
4523 dst_format = dst_surface->resource.format;
4524 dst_fmt_flags = dst_surface->container->resource.format_flags;
4525 if (src_surface)
4527 if (dst_surface->resource.format->id != src_surface->resource.format->id)
4529 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
4531 /* The conv function writes a FIXME */
4532 WARN("Cannot convert source surface format to dest format.\n");
4533 goto release;
4535 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
4537 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
4538 src_format = src_surface->resource.format;
4539 src_fmt_flags = src_surface->container->resource.format_flags;
4541 else
4543 src_format = dst_format;
4544 src_fmt_flags = dst_fmt_flags;
4547 wined3d_surface_map(dst_surface, &dst_map, &dst_box, 0);
4550 bpp = dst_surface->resource.format->byte_count;
4551 srcheight = src_rect->bottom - src_rect->top;
4552 srcwidth = src_rect->right - src_rect->left;
4553 dstheight = dst_rect->bottom - dst_rect->top;
4554 dstwidth = dst_rect->right - dst_rect->left;
4555 width = (dst_rect->right - dst_rect->left) * bpp;
4557 if (src_surface)
4558 sbase = (BYTE *)src_map.data
4559 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
4560 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
4561 if (src_surface != dst_surface)
4562 dbuf = dst_map.data;
4563 else
4564 dbuf = (BYTE *)dst_map.data
4565 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
4566 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
4568 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
4570 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
4572 if (src_surface == dst_surface)
4574 FIXME("Only plain blits supported on compressed surfaces.\n");
4575 hr = E_NOTIMPL;
4576 goto release;
4579 if (srcheight != dstheight || srcwidth != dstwidth)
4581 WARN("Stretching not supported on compressed surfaces.\n");
4582 hr = WINED3DERR_INVALIDCALL;
4583 goto release;
4586 if (!surface_check_block_align_rect(src_surface, src_rect))
4588 WARN("Source rectangle not block-aligned.\n");
4589 hr = WINED3DERR_INVALIDCALL;
4590 goto release;
4593 if (!surface_check_block_align_rect(dst_surface, dst_rect))
4595 WARN("Destination rectangle not block-aligned.\n");
4596 hr = WINED3DERR_INVALIDCALL;
4597 goto release;
4600 hr = surface_cpu_blt_compressed(sbase, dbuf,
4601 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
4602 src_format, flags, fx);
4603 goto release;
4606 /* First, all the 'source-less' blits */
4607 if (flags & WINEDDBLT_COLORFILL)
4609 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
4610 flags &= ~WINEDDBLT_COLORFILL;
4613 if (flags & WINEDDBLT_DEPTHFILL)
4615 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
4617 if (flags & WINEDDBLT_DDROPS)
4619 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
4621 /* Now the 'with source' blits. */
4622 if (src_surface)
4624 int sx, xinc, sy, yinc;
4626 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
4627 goto release;
4629 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
4630 && (srcwidth != dstwidth || srcheight != dstheight))
4632 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
4633 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
4636 xinc = (srcwidth << 16) / dstwidth;
4637 yinc = (srcheight << 16) / dstheight;
4639 if (!flags)
4641 /* No effects, we can cheat here. */
4642 if (dstwidth == srcwidth)
4644 if (dstheight == srcheight)
4646 /* No stretching in either direction. This needs to be as
4647 * fast as possible. */
4648 sbuf = sbase;
4650 /* Check for overlapping surfaces. */
4651 if (src_surface != dst_surface || dst_rect->top < src_rect->top
4652 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
4654 /* No overlap, or dst above src, so copy from top downwards. */
4655 for (y = 0; y < dstheight; ++y)
4657 memcpy(dbuf, sbuf, width);
4658 sbuf += src_map.row_pitch;
4659 dbuf += dst_map.row_pitch;
4662 else if (dst_rect->top > src_rect->top)
4664 /* Copy from bottom upwards. */
4665 sbuf += src_map.row_pitch * dstheight;
4666 dbuf += dst_map.row_pitch * dstheight;
4667 for (y = 0; y < dstheight; ++y)
4669 sbuf -= src_map.row_pitch;
4670 dbuf -= dst_map.row_pitch;
4671 memcpy(dbuf, sbuf, width);
4674 else
4676 /* Src and dst overlapping on the same line, use memmove. */
4677 for (y = 0; y < dstheight; ++y)
4679 memmove(dbuf, sbuf, width);
4680 sbuf += src_map.row_pitch;
4681 dbuf += dst_map.row_pitch;
4685 else
4687 /* Stretching in y direction only. */
4688 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4690 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4691 memcpy(dbuf, sbuf, width);
4692 dbuf += dst_map.row_pitch;
4696 else
4698 /* Stretching in X direction. */
4699 int last_sy = -1;
4700 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4702 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4704 if ((sy >> 16) == (last_sy >> 16))
4706 /* This source row is the same as last source row -
4707 * Copy the already stretched row. */
4708 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
4710 else
4712 #define STRETCH_ROW(type) \
4713 do { \
4714 const type *s = (const type *)sbuf; \
4715 type *d = (type *)dbuf; \
4716 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4717 d[x] = s[sx >> 16]; \
4718 } while(0)
4720 switch(bpp)
4722 case 1:
4723 STRETCH_ROW(BYTE);
4724 break;
4725 case 2:
4726 STRETCH_ROW(WORD);
4727 break;
4728 case 4:
4729 STRETCH_ROW(DWORD);
4730 break;
4731 case 3:
4733 const BYTE *s;
4734 BYTE *d = dbuf;
4735 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
4737 DWORD pixel;
4739 s = sbuf + 3 * (sx >> 16);
4740 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4741 d[0] = (pixel ) & 0xff;
4742 d[1] = (pixel >> 8) & 0xff;
4743 d[2] = (pixel >> 16) & 0xff;
4744 d += 3;
4746 break;
4748 default:
4749 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
4750 hr = WINED3DERR_NOTAVAILABLE;
4751 goto error;
4753 #undef STRETCH_ROW
4755 dbuf += dst_map.row_pitch;
4756 last_sy = sy;
4760 else
4762 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
4763 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
4764 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
4765 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
4767 /* The color keying flags are checked for correctness in ddraw */
4768 if (flags & WINEDDBLT_KEYSRC)
4770 keylow = src_surface->container->async.src_blt_color_key.color_space_low_value;
4771 keyhigh = src_surface->container->async.src_blt_color_key.color_space_high_value;
4773 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4775 keylow = fx->ddckSrcColorkey.color_space_low_value;
4776 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
4779 if (flags & WINEDDBLT_KEYDEST)
4781 /* Destination color keys are taken from the source surface! */
4782 destkeylow = src_surface->container->async.dst_blt_color_key.color_space_low_value;
4783 destkeyhigh = src_surface->container->async.dst_blt_color_key.color_space_high_value;
4785 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
4787 destkeylow = fx->ddckDestColorkey.color_space_low_value;
4788 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
4791 if (bpp == 1)
4793 keymask = 0xff;
4795 else
4797 DWORD masks[3];
4798 get_color_masks(src_format, masks);
4799 keymask = masks[0]
4800 | masks[1]
4801 | masks[2];
4803 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
4806 if (flags & WINEDDBLT_DDFX)
4808 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
4809 LONG tmpxy;
4810 dTopLeft = dbuf;
4811 dTopRight = dbuf + ((dstwidth - 1) * bpp);
4812 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
4813 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
4815 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
4817 /* I don't think we need to do anything about this flag */
4818 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
4820 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
4822 tmp = dTopRight;
4823 dTopRight = dTopLeft;
4824 dTopLeft = tmp;
4825 tmp = dBottomRight;
4826 dBottomRight = dBottomLeft;
4827 dBottomLeft = tmp;
4828 dstxinc = dstxinc * -1;
4830 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
4832 tmp = dTopLeft;
4833 dTopLeft = dBottomLeft;
4834 dBottomLeft = tmp;
4835 tmp = dTopRight;
4836 dTopRight = dBottomRight;
4837 dBottomRight = tmp;
4838 dstyinc = dstyinc * -1;
4840 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
4842 /* I don't think we need to do anything about this flag */
4843 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
4845 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
4847 tmp = dBottomRight;
4848 dBottomRight = dTopLeft;
4849 dTopLeft = tmp;
4850 tmp = dBottomLeft;
4851 dBottomLeft = dTopRight;
4852 dTopRight = tmp;
4853 dstxinc = dstxinc * -1;
4854 dstyinc = dstyinc * -1;
4856 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
4858 tmp = dTopLeft;
4859 dTopLeft = dBottomLeft;
4860 dBottomLeft = dBottomRight;
4861 dBottomRight = dTopRight;
4862 dTopRight = tmp;
4863 tmpxy = dstxinc;
4864 dstxinc = dstyinc;
4865 dstyinc = tmpxy;
4866 dstxinc = dstxinc * -1;
4868 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
4870 tmp = dTopLeft;
4871 dTopLeft = dTopRight;
4872 dTopRight = dBottomRight;
4873 dBottomRight = dBottomLeft;
4874 dBottomLeft = tmp;
4875 tmpxy = dstxinc;
4876 dstxinc = dstyinc;
4877 dstyinc = tmpxy;
4878 dstyinc = dstyinc * -1;
4880 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
4882 /* I don't think we need to do anything about this flag */
4883 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
4885 dbuf = dTopLeft;
4886 flags &= ~(WINEDDBLT_DDFX);
4889 #define COPY_COLORKEY_FX(type) \
4890 do { \
4891 const type *s; \
4892 type *d = (type *)dbuf, *dx, tmp; \
4893 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
4895 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
4896 dx = d; \
4897 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4899 tmp = s[sx >> 16]; \
4900 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
4901 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
4903 dx[0] = tmp; \
4905 dx = (type *)(((BYTE *)dx) + dstxinc); \
4907 d = (type *)(((BYTE *)d) + dstyinc); \
4909 } while(0)
4911 switch (bpp)
4913 case 1:
4914 COPY_COLORKEY_FX(BYTE);
4915 break;
4916 case 2:
4917 COPY_COLORKEY_FX(WORD);
4918 break;
4919 case 4:
4920 COPY_COLORKEY_FX(DWORD);
4921 break;
4922 case 3:
4924 const BYTE *s;
4925 BYTE *d = dbuf, *dx;
4926 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4928 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4929 dx = d;
4930 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
4932 DWORD pixel, dpixel = 0;
4933 s = sbuf + 3 * (sx>>16);
4934 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4935 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
4936 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
4937 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
4939 dx[0] = (pixel ) & 0xff;
4940 dx[1] = (pixel >> 8) & 0xff;
4941 dx[2] = (pixel >> 16) & 0xff;
4943 dx += dstxinc;
4945 d += dstyinc;
4947 break;
4949 default:
4950 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
4951 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
4952 hr = WINED3DERR_NOTAVAILABLE;
4953 goto error;
4954 #undef COPY_COLORKEY_FX
4959 error:
4960 if (flags && FIXME_ON(d3d_surface))
4962 FIXME("\tUnsupported flags: %#x.\n", flags);
4965 release:
4966 wined3d_surface_unmap(dst_surface);
4967 if (src_surface && src_surface != dst_surface)
4968 wined3d_surface_unmap(src_surface);
4969 /* Release the converted surface, if any. */
4970 if (src_texture)
4971 wined3d_texture_decref(src_texture);
4973 return hr;
4976 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4977 const RECT *dst_rect, const struct wined3d_color *color)
4979 static const RECT src_rect;
4980 WINEDDBLTFX BltFx;
4982 memset(&BltFx, 0, sizeof(BltFx));
4983 BltFx.dwSize = sizeof(BltFx);
4984 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
4985 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
4986 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
4989 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
4990 struct wined3d_surface *surface, const RECT *rect, float depth)
4992 FIXME("Depth filling not implemented by cpu_blit.\n");
4993 return WINED3DERR_INVALIDCALL;
4996 static void cpu_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
4997 struct wined3d_surface *src_surface, const RECT *src_rect,
4998 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4999 const struct wined3d_color_key *color_key)
5001 /* FIXME: Remove error returns from surface_blt_cpu. */
5002 ERR("Blit method not implemented by cpu_blit.\n");
5005 const struct blit_shader cpu_blit = {
5006 cpu_blit_alloc,
5007 cpu_blit_free,
5008 cpu_blit_set,
5009 cpu_blit_unset,
5010 cpu_blit_supported,
5011 cpu_blit_color_fill,
5012 cpu_blit_depth_fill,
5013 cpu_blit_blit_surface,
5016 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5017 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
5018 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5020 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5021 struct wined3d_device *device = dst_surface->resource.device;
5022 DWORD src_ds_flags, dst_ds_flags;
5023 BOOL scale, convert;
5025 static const DWORD simple_blit = WINEDDBLT_ASYNC
5026 | WINEDDBLT_COLORFILL
5027 | WINEDDBLT_KEYSRC
5028 | WINEDDBLT_KEYSRCOVERRIDE
5029 | WINEDDBLT_WAIT
5030 | WINEDDBLT_DEPTHFILL
5031 | WINEDDBLT_DONOTWAIT
5032 | WINEDDBLT_ALPHATEST;
5034 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5035 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5036 flags, fx, debug_d3dtexturefiltertype(filter));
5037 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5039 if (fx)
5041 TRACE("dwSize %#x.\n", fx->dwSize);
5042 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5043 TRACE("dwROP %#x.\n", fx->dwROP);
5044 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5045 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5046 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5047 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5048 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5049 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5050 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5051 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5052 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5053 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5054 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5055 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5056 TRACE("dwReserved %#x.\n", fx->dwReserved);
5057 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5058 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5059 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5060 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5061 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5062 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5063 fx->ddckDestColorkey.color_space_low_value,
5064 fx->ddckDestColorkey.color_space_high_value);
5065 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5066 fx->ddckSrcColorkey.color_space_low_value,
5067 fx->ddckSrcColorkey.color_space_high_value);
5070 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5072 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5073 return WINEDDERR_SURFACEBUSY;
5076 if (dst_rect->left >= dst_rect->right || dst_rect->top >= dst_rect->bottom
5077 || dst_rect->left > dst_surface->resource.width || dst_rect->left < 0
5078 || dst_rect->top > dst_surface->resource.height || dst_rect->top < 0
5079 || dst_rect->right > dst_surface->resource.width || dst_rect->right < 0
5080 || dst_rect->bottom > dst_surface->resource.height || dst_rect->bottom < 0)
5082 WARN("The application gave us a bad destination rectangle.\n");
5083 return WINEDDERR_INVALIDRECT;
5086 if (src_surface)
5088 if (src_rect->left >= src_rect->right || src_rect->top >= src_rect->bottom
5089 || src_rect->left > src_surface->resource.width || src_rect->left < 0
5090 || src_rect->top > src_surface->resource.height || src_rect->top < 0
5091 || src_rect->right > src_surface->resource.width || src_rect->right < 0
5092 || src_rect->bottom > src_surface->resource.height || src_rect->bottom < 0)
5094 WARN("The application gave us a bad source rectangle.\n");
5095 return WINEDDERR_INVALIDRECT;
5099 if (!fx || !(fx->dwDDFX))
5100 flags &= ~WINEDDBLT_DDFX;
5102 if (flags & WINEDDBLT_WAIT)
5103 flags &= ~WINEDDBLT_WAIT;
5105 if (flags & WINEDDBLT_ASYNC)
5107 static unsigned int once;
5109 if (!once++)
5110 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5111 flags &= ~WINEDDBLT_ASYNC;
5114 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5115 if (flags & WINEDDBLT_DONOTWAIT)
5117 static unsigned int once;
5119 if (!once++)
5120 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5121 flags &= ~WINEDDBLT_DONOTWAIT;
5124 if (!device->d3d_initialized)
5126 WARN("D3D not initialized, using fallback.\n");
5127 goto cpu;
5130 /* We want to avoid invalidating the sysmem location for converted
5131 * surfaces, since otherwise we'd have to convert the data back when
5132 * locking them. */
5133 if (dst_surface->container->flags & WINED3D_TEXTURE_CONVERTED
5134 || dst_surface->container->resource.format->convert
5135 || wined3d_format_get_color_key_conversion(dst_surface->container, TRUE))
5137 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5138 goto cpu;
5141 if (flags & ~simple_blit)
5143 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5144 goto fallback;
5147 if (src_surface)
5148 src_swapchain = src_surface->container->swapchain;
5149 else
5150 src_swapchain = NULL;
5152 dst_swapchain = dst_surface->container->swapchain;
5154 /* This isn't strictly needed. FBO blits for example could deal with
5155 * cross-swapchain blits by first downloading the source to a texture
5156 * before switching to the destination context. We just have this here to
5157 * not have to deal with the issue, since cross-swapchain blits should be
5158 * rare. */
5159 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5161 FIXME("Using fallback for cross-swapchain blit.\n");
5162 goto fallback;
5165 scale = src_surface
5166 && (src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
5167 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top);
5168 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5170 dst_ds_flags = dst_surface->container->resource.format_flags
5171 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5172 if (src_surface)
5173 src_ds_flags = src_surface->container->resource.format_flags
5174 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5175 else
5176 src_ds_flags = 0;
5178 if (src_ds_flags || dst_ds_flags)
5180 if (flags & WINEDDBLT_DEPTHFILL)
5182 float depth;
5184 TRACE("Depth fill.\n");
5186 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5187 return WINED3DERR_INVALIDCALL;
5189 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, dst_rect, depth)))
5190 return WINED3D_OK;
5192 else
5194 if (src_ds_flags != dst_ds_flags)
5196 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
5197 return WINED3DERR_INVALIDCALL;
5200 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
5201 src_rect, dst_surface, dst_surface->container->resource.draw_binding, dst_rect)))
5202 return WINED3D_OK;
5205 else
5207 const struct blit_shader *blitter;
5209 /* In principle this would apply to depth blits as well, but we don't
5210 * implement those in the CPU blitter at the moment. */
5211 if ((dst_surface->locations & dst_surface->resource.map_binding)
5212 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
5214 if (scale)
5215 TRACE("Not doing sysmem blit because of scaling.\n");
5216 else if (convert)
5217 TRACE("Not doing sysmem blit because of format conversion.\n");
5218 else
5219 goto cpu;
5222 if (flags & WINEDDBLT_COLORFILL)
5224 struct wined3d_color color;
5225 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
5227 TRACE("Color fill.\n");
5229 if (!wined3d_format_convert_color_to_float(dst_surface->resource.format,
5230 palette, fx->u5.dwFillColor, &color))
5231 goto fallback;
5233 if (SUCCEEDED(surface_color_fill(dst_surface, dst_rect, &color)))
5234 return WINED3D_OK;
5236 else
5238 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
5239 const struct wined3d_color_key *color_key = NULL;
5241 TRACE("Color blit.\n");
5242 if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5244 color_key = &fx->ddckSrcColorkey;
5245 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5247 else if (flags & WINEDDBLT_KEYSRC)
5249 color_key = &src_surface->container->async.src_blt_color_key;
5250 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5252 else if (flags & WINEDDBLT_ALPHATEST)
5254 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
5256 else if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5257 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5259 /* Upload */
5260 if (scale)
5261 TRACE("Not doing upload because of scaling.\n");
5262 else if (convert)
5263 TRACE("Not doing upload because of format conversion.\n");
5264 else
5266 POINT dst_point = {dst_rect->left, dst_rect->top};
5268 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
5270 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
5272 struct wined3d_context *context = context_acquire(device, dst_surface);
5273 surface_load_location(dst_surface, context, dst_surface->container->resource.draw_binding);
5274 context_release(context);
5276 return WINED3D_OK;
5280 else if (dst_swapchain && dst_swapchain->back_buffers
5281 && dst_surface->container == dst_swapchain->front_buffer
5282 && src_surface->container == dst_swapchain->back_buffers[0])
5284 /* Use present for back -> front blits. The idea behind this is
5285 * that present is potentially faster than a blit, in particular
5286 * when FBO blits aren't available. Some ddraw applications like
5287 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5288 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5289 * applications can't blit directly to the frontbuffer. */
5290 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5292 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5294 /* Set the swap effect to COPY, we don't want the backbuffer
5295 * to become undefined. */
5296 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5297 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5298 dst_swapchain->desc.swap_effect = swap_effect;
5300 return WINED3D_OK;
5303 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
5304 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5305 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5307 struct wined3d_context *context;
5308 TRACE("Using FBO blit.\n");
5310 context = context_acquire(device, NULL);
5311 surface_blt_fbo(device, context, filter,
5312 src_surface, src_surface->container->resource.draw_binding, src_rect,
5313 dst_surface, dst_surface->container->resource.draw_binding, dst_rect);
5314 context_release(context);
5316 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5317 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5319 return WINED3D_OK;
5322 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
5323 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5324 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format);
5325 if (blitter)
5327 blitter->blit_surface(device, blit_op, filter, src_surface,
5328 src_rect, dst_surface, dst_rect, color_key);
5329 return WINED3D_OK;
5334 fallback:
5335 /* Special cases for render targets. */
5336 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
5337 return WINED3D_OK;
5339 cpu:
5340 return surface_cpu_blt(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter);
5343 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5344 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5346 struct wined3d_device *device = container->resource.device;
5347 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5348 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5349 BOOL lockable = flags & WINED3D_TEXTURE_CREATE_MAPPABLE;
5350 UINT multisample_quality = desc->multisample_quality;
5351 unsigned int resource_size;
5352 HRESULT hr;
5354 /* Quick lockable sanity check.
5355 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5356 * this function is too deep to need to care about things like this.
5357 * Levels need to be checked too, since they all affect what can be done. */
5358 switch (desc->pool)
5360 case WINED3D_POOL_MANAGED:
5361 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5362 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5363 break;
5365 case WINED3D_POOL_DEFAULT:
5366 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5367 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5368 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5369 break;
5371 case WINED3D_POOL_SCRATCH:
5372 case WINED3D_POOL_SYSTEM_MEM:
5373 break;
5375 default:
5376 FIXME("Unknown pool %#x.\n", desc->pool);
5377 break;
5380 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5381 FIXME("Trying to create a render target that isn't in the default pool.\n");
5383 /* FIXME: Check that the format is supported by the device. */
5385 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5386 if (!resource_size)
5387 return WINED3DERR_INVALIDCALL;
5389 if (device->wined3d->flags & WINED3D_NO3D)
5390 surface->surface_ops = &gdi_surface_ops;
5391 else
5392 surface->surface_ops = &surface_ops;
5394 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE,
5395 format, desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height,
5396 1, resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5398 WARN("Failed to initialize resource, returning %#x.\n", hr);
5399 return hr;
5402 surface->container = container;
5403 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5404 list_init(&surface->renderbuffers);
5405 list_init(&surface->overlays);
5407 /* Flags */
5408 if (flags & WINED3D_TEXTURE_CREATE_DISCARD)
5409 surface->flags |= SFLAG_DISCARD;
5410 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5411 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5413 surface->texture_target = target;
5414 surface->texture_level = level;
5415 surface->texture_layer = layer;
5417 /* Call the private setup routine */
5418 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5420 ERR("Private setup failed, hr %#x.\n", hr);
5421 surface_cleanup(surface);
5422 return hr;
5425 /* Similar to lockable rendertargets above, creating the DIB section
5426 * during surface initialization prevents the sysmem pointer from changing
5427 * after a wined3d_surface_getdc() call. */
5428 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5429 && SUCCEEDED(surface_create_dib_section(surface)))
5430 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5432 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5434 wined3d_resource_free_sysmem(&surface->resource);
5435 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5436 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5439 return hr;
5442 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5443 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
5445 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
5446 const struct wined3d_parent_ops *parent_ops;
5447 struct wined3d_surface *object;
5448 void *parent;
5449 HRESULT hr;
5451 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
5452 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
5453 container, desc->width, desc->height, debug_d3dformat(desc->format),
5454 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
5455 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
5457 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
5458 return E_OUTOFMEMORY;
5460 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
5462 WARN("Failed to initialize surface, returning %#x.\n", hr);
5463 HeapFree(GetProcessHeap(), 0, object);
5464 return hr;
5467 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
5468 container, layer * container->level_count + level, object, &parent, &parent_ops)))
5470 WARN("Failed to create surface parent, hr %#x.\n", hr);
5471 wined3d_surface_destroy(object);
5472 return hr;
5475 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
5477 object->resource.parent = parent;
5478 object->resource.parent_ops = parent_ops;
5479 *surface = object;
5481 return hr;
5484 /* Context activation is done by the caller. */
5485 void wined3d_surface_prepare(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5487 switch (location)
5489 case WINED3D_LOCATION_TEXTURE_RGB:
5490 wined3d_texture_prepare_texture(surface->container, context, FALSE);
5491 break;
5493 case WINED3D_LOCATION_TEXTURE_SRGB:
5494 wined3d_texture_prepare_texture(surface->container, context, TRUE);
5495 break;
5497 case WINED3D_LOCATION_RB_MULTISAMPLE:
5498 surface_prepare_rb(surface, context->gl_info, TRUE);
5499 break;
5501 case WINED3D_LOCATION_RB_RESOLVED:
5502 surface_prepare_rb(surface, context->gl_info, FALSE);
5503 break;