wined3d: Get rid of context_invalidate_active_texture().
[wine/multimedia.git] / dlls / wined3d / surface.c
blob77a0eaec063135868ac274542536c5370fd53959
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2011, 2013-2014 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d);
37 #define MAXLOCKCOUNT 50 /* After this amount of locks do not free the sysmem copy. */
39 static const DWORD surface_simple_locations =
40 WINED3D_LOCATION_SYSMEM | WINED3D_LOCATION_USER_MEMORY
41 | WINED3D_LOCATION_DIB | WINED3D_LOCATION_BUFFER;
43 static void surface_cleanup(struct wined3d_surface *surface)
45 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->pbo || surface->rb_multisample
50 || surface->rb_resolved || !list_empty(&surface->renderbuffers))
52 struct wined3d_renderbuffer_entry *entry, *entry2;
53 const struct wined3d_gl_info *gl_info;
54 struct wined3d_context *context;
56 context = context_acquire(surface->resource.device, NULL);
57 gl_info = context->gl_info;
59 if (surface->pbo)
61 TRACE("Deleting PBO %u.\n", surface->pbo);
62 GL_EXTCALL(glDeleteBuffers(1, &surface->pbo));
65 if (surface->rb_multisample)
67 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
68 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
71 if (surface->rb_resolved)
73 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
74 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
77 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
79 TRACE("Deleting renderbuffer %u.\n", entry->id);
80 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
81 HeapFree(GetProcessHeap(), 0, entry);
84 context_release(context);
87 if (surface->flags & SFLAG_DIBSECTION)
89 DeleteDC(surface->hDC);
90 DeleteObject(surface->dib.DIBsection);
91 surface->dib.bitmap_data = NULL;
94 if (surface->overlay_dest)
95 list_remove(&surface->overlay_entry);
97 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
99 list_remove(&overlay->overlay_entry);
100 overlay->overlay_dest = NULL;
103 resource_cleanup(&surface->resource);
106 void wined3d_surface_destroy(struct wined3d_surface *surface)
108 TRACE("surface %p.\n", surface);
110 surface_cleanup(surface);
111 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
112 HeapFree(GetProcessHeap(), 0, surface);
115 void surface_get_drawable_size(const struct wined3d_surface *surface, const struct wined3d_context *context,
116 unsigned int *width, unsigned int *height)
118 if (surface->container->swapchain)
120 /* The drawable size of an onscreen drawable is the surface size.
121 * (Actually: The window size, but the surface is created in window
122 * size.) */
123 *width = context->current_rt->resource.width;
124 *height = context->current_rt->resource.height;
126 else if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER)
128 const struct wined3d_swapchain *swapchain = context->swapchain;
130 /* The drawable size of a backbuffer / aux buffer offscreen target is
131 * the size of the current context's drawable, which is the size of
132 * the back buffer of the swapchain the active context belongs to. */
133 *width = swapchain->desc.backbuffer_width;
134 *height = swapchain->desc.backbuffer_height;
136 else
138 /* The drawable size of an FBO target is the OpenGL texture size,
139 * which is the power of two size. */
140 *width = context->current_rt->pow2Width;
141 *height = context->current_rt->pow2Height;
145 struct blt_info
147 GLenum binding;
148 GLenum bind_target;
149 enum wined3d_gl_resource_type tex_type;
150 GLfloat coords[4][3];
153 struct float_rect
155 float l;
156 float t;
157 float r;
158 float b;
161 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
163 f->l = ((r->left * 2.0f) / w) - 1.0f;
164 f->t = ((r->top * 2.0f) / h) - 1.0f;
165 f->r = ((r->right * 2.0f) / w) - 1.0f;
166 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
169 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
171 GLfloat (*coords)[3] = info->coords;
172 struct float_rect f;
174 switch (target)
176 default:
177 FIXME("Unsupported texture target %#x\n", target);
178 /* Fall back to GL_TEXTURE_2D */
179 case GL_TEXTURE_2D:
180 info->binding = GL_TEXTURE_BINDING_2D;
181 info->bind_target = GL_TEXTURE_2D;
182 info->tex_type = WINED3D_GL_RES_TYPE_TEX_2D;
183 coords[0][0] = (float)rect->left / w;
184 coords[0][1] = (float)rect->top / h;
185 coords[0][2] = 0.0f;
187 coords[1][0] = (float)rect->right / w;
188 coords[1][1] = (float)rect->top / h;
189 coords[1][2] = 0.0f;
191 coords[2][0] = (float)rect->left / w;
192 coords[2][1] = (float)rect->bottom / h;
193 coords[2][2] = 0.0f;
195 coords[3][0] = (float)rect->right / w;
196 coords[3][1] = (float)rect->bottom / h;
197 coords[3][2] = 0.0f;
198 break;
200 case GL_TEXTURE_RECTANGLE_ARB:
201 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
202 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
203 info->tex_type = WINED3D_GL_RES_TYPE_TEX_RECT;
204 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
205 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
206 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
207 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
208 break;
210 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
211 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
212 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
213 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
214 cube_coords_float(rect, w, h, &f);
216 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
217 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
218 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
219 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
220 break;
222 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
223 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
224 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
225 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
226 cube_coords_float(rect, w, h, &f);
228 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
229 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
230 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
231 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
232 break;
234 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
235 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
236 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
237 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
238 cube_coords_float(rect, w, h, &f);
240 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
241 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
242 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
243 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
244 break;
246 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
247 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
248 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
249 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
250 cube_coords_float(rect, w, h, &f);
252 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
253 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
254 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
255 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
256 break;
258 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
259 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
260 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
261 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
262 cube_coords_float(rect, w, h, &f);
264 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
265 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
266 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
267 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
268 break;
270 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
271 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
272 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
273 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
274 cube_coords_float(rect, w, h, &f);
276 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
277 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
278 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
279 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
280 break;
284 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
286 if (rect_in)
287 *rect_out = *rect_in;
288 else
290 rect_out->left = 0;
291 rect_out->top = 0;
292 rect_out->right = surface->resource.width;
293 rect_out->bottom = surface->resource.height;
297 /* Context activation is done by the caller. */
298 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
299 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
301 const struct wined3d_gl_info *gl_info = context->gl_info;
302 struct wined3d_texture *texture = src_surface->container;
303 struct blt_info info;
305 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
307 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
308 checkGLcall("glEnable(bind_target)");
310 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
312 /* Filtering for StretchRect */
313 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
314 checkGLcall("glTexParameteri");
315 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
316 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
317 checkGLcall("glTexParameteri");
318 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
319 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
320 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
321 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
322 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
323 checkGLcall("glTexEnvi");
325 /* Draw a quad */
326 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
327 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
328 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
330 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
331 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
333 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
334 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
336 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
337 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
338 gl_info->gl_ops.gl.p_glEnd();
340 /* Unbind the texture */
341 context_bind_texture(context, info.bind_target, 0);
343 /* We changed the filtering settings on the texture. Inform the
344 * container about this to get the filters reset properly next draw. */
345 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
346 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
347 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
348 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
351 /* Works correctly only for <= 4 bpp formats. */
352 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
354 masks[0] = ((1 << format->red_size) - 1) << format->red_offset;
355 masks[1] = ((1 << format->green_size) - 1) << format->green_offset;
356 masks[2] = ((1 << format->blue_size) - 1) << format->blue_offset;
359 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
361 const struct wined3d_format *format = surface->resource.format;
362 SYSTEM_INFO sysInfo;
363 BITMAPINFO *b_info;
364 int extraline = 0;
365 DWORD *masks;
367 TRACE("surface %p.\n", surface);
369 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
371 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
372 return WINED3DERR_INVALIDCALL;
375 switch (format->byte_count)
377 case 2:
378 case 4:
379 /* Allocate extra space to store the RGB bit masks. */
380 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
381 break;
383 case 3:
384 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
385 break;
387 default:
388 /* Allocate extra space for a palette. */
389 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
390 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
391 break;
394 if (!b_info)
395 return E_OUTOFMEMORY;
397 /* Some applications access the surface in via DWORDs, and do not take
398 * the necessary care at the end of the surface. So we need at least
399 * 4 extra bytes at the end of the surface. Check against the page size,
400 * if the last page used for the surface has at least 4 spare bytes we're
401 * safe, otherwise add an extra line to the DIB section. */
402 GetSystemInfo(&sysInfo);
403 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
405 extraline = 1;
406 TRACE("Adding an extra line to the DIB section.\n");
409 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
410 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
411 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
412 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
413 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
414 * wined3d_surface_get_pitch(surface);
415 b_info->bmiHeader.biPlanes = 1;
416 b_info->bmiHeader.biBitCount = format->byte_count * 8;
418 b_info->bmiHeader.biXPelsPerMeter = 0;
419 b_info->bmiHeader.biYPelsPerMeter = 0;
420 b_info->bmiHeader.biClrUsed = 0;
421 b_info->bmiHeader.biClrImportant = 0;
423 /* Get the bit masks */
424 masks = (DWORD *)b_info->bmiColors;
425 switch (surface->resource.format->id)
427 case WINED3DFMT_B8G8R8_UNORM:
428 b_info->bmiHeader.biCompression = BI_RGB;
429 break;
431 case WINED3DFMT_B5G5R5X1_UNORM:
432 case WINED3DFMT_B5G5R5A1_UNORM:
433 case WINED3DFMT_B4G4R4A4_UNORM:
434 case WINED3DFMT_B4G4R4X4_UNORM:
435 case WINED3DFMT_B2G3R3_UNORM:
436 case WINED3DFMT_B2G3R3A8_UNORM:
437 case WINED3DFMT_R10G10B10A2_UNORM:
438 case WINED3DFMT_R8G8B8A8_UNORM:
439 case WINED3DFMT_R8G8B8X8_UNORM:
440 case WINED3DFMT_B10G10R10A2_UNORM:
441 case WINED3DFMT_B5G6R5_UNORM:
442 case WINED3DFMT_R16G16B16A16_UNORM:
443 b_info->bmiHeader.biCompression = BI_BITFIELDS;
444 get_color_masks(format, masks);
445 break;
447 default:
448 /* Don't know palette */
449 b_info->bmiHeader.biCompression = BI_RGB;
450 break;
453 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
454 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
455 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
456 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
458 if (!surface->dib.DIBsection)
460 ERR("Failed to create DIB section.\n");
461 HeapFree(GetProcessHeap(), 0, b_info);
462 return HRESULT_FROM_WIN32(GetLastError());
465 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
466 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
468 HeapFree(GetProcessHeap(), 0, b_info);
470 /* Now allocate a DC. */
471 surface->hDC = CreateCompatibleDC(0);
472 SelectObject(surface->hDC, surface->dib.DIBsection);
474 surface->flags |= SFLAG_DIBSECTION;
476 return WINED3D_OK;
479 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
480 DWORD location)
482 if (location & WINED3D_LOCATION_BUFFER)
484 data->addr = NULL;
485 data->buffer_object = surface->pbo;
486 return;
488 if (location & WINED3D_LOCATION_USER_MEMORY)
490 data->addr = surface->user_memory;
491 data->buffer_object = 0;
492 return;
494 if (location & WINED3D_LOCATION_DIB)
496 data->addr = surface->dib.bitmap_data;
497 data->buffer_object = 0;
498 return;
500 if (location & WINED3D_LOCATION_SYSMEM)
502 data->addr = surface->resource.heap_memory;
503 data->buffer_object = 0;
504 return;
507 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
508 data->addr = NULL;
509 data->buffer_object = 0;
512 static void surface_prepare_buffer(struct wined3d_surface *surface)
514 struct wined3d_context *context;
515 GLenum error;
516 const struct wined3d_gl_info *gl_info;
518 if (surface->pbo)
519 return;
521 context = context_acquire(surface->resource.device, NULL);
522 gl_info = context->gl_info;
524 GL_EXTCALL(glGenBuffers(1, &surface->pbo));
525 error = gl_info->gl_ops.gl.p_glGetError();
526 if (!surface->pbo || error != GL_NO_ERROR)
527 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
529 TRACE("Binding PBO %u.\n", surface->pbo);
531 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
532 checkGLcall("glBindBuffer");
534 GL_EXTCALL(glBufferData(GL_PIXEL_UNPACK_BUFFER, surface->resource.size + 4,
535 NULL, GL_STREAM_DRAW));
536 checkGLcall("glBufferData");
538 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
539 checkGLcall("glBindBuffer");
541 context_release(context);
544 static void surface_prepare_system_memory(struct wined3d_surface *surface)
546 TRACE("surface %p.\n", surface);
548 if (surface->resource.heap_memory)
549 return;
551 /* Whatever surface we have, make sure that there is memory allocated
552 * for the downloaded copy, or a PBO to map. */
553 if (!wined3d_resource_allocate_sysmem(&surface->resource))
554 ERR("Failed to allocate system memory.\n");
556 if (surface->locations & WINED3D_LOCATION_SYSMEM)
557 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
560 void surface_prepare_map_memory(struct wined3d_surface *surface)
562 switch (surface->resource.map_binding)
564 case WINED3D_LOCATION_SYSMEM:
565 surface_prepare_system_memory(surface);
566 break;
568 case WINED3D_LOCATION_USER_MEMORY:
569 if (!surface->user_memory)
570 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
571 break;
573 case WINED3D_LOCATION_DIB:
574 if (!surface->dib.bitmap_data)
575 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
576 break;
578 case WINED3D_LOCATION_BUFFER:
579 surface_prepare_buffer(surface);
580 break;
582 default:
583 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
587 static void surface_evict_sysmem(struct wined3d_surface *surface)
589 /* In some conditions the surface memory must not be freed:
590 * WINED3D_TEXTURE_CONVERTED: Converting the data back would take too long
591 * WINED3D_TEXTURE_DYNAMIC_MAP: Avoid freeing the data for performance
592 * SFLAG_CLIENT: OpenGL uses our memory as backup */
593 if (surface->resource.map_count || surface->flags & SFLAG_CLIENT
594 || surface->container->flags & (WINED3D_TEXTURE_CONVERTED | WINED3D_TEXTURE_PIN_SYSMEM
595 | WINED3D_TEXTURE_DYNAMIC_MAP))
596 return;
598 wined3d_resource_free_sysmem(&surface->resource);
599 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
602 static void surface_release_client_storage(struct wined3d_surface *surface)
604 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
605 const struct wined3d_gl_info *gl_info = context->gl_info;
607 if (surface->container->texture_rgb.name)
609 wined3d_texture_bind_and_dirtify(surface->container, context, FALSE);
610 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
611 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
613 if (surface->container->texture_srgb.name)
615 wined3d_texture_bind_and_dirtify(surface->container, context, TRUE);
616 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
617 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
619 wined3d_texture_force_reload(surface->container);
621 context_release(context);
624 static BOOL surface_use_pbo(const struct wined3d_surface *surface)
626 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
627 struct wined3d_texture *texture = surface->container;
629 return texture->resource.pool == WINED3D_POOL_DEFAULT
630 && surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU
631 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
632 && !texture->resource.format->convert
633 && !(texture->flags & WINED3D_TEXTURE_PIN_SYSMEM)
634 && !(surface->flags & SFLAG_NONPOW2);
637 static HRESULT surface_private_setup(struct wined3d_surface *surface)
639 /* TODO: Check against the maximum texture sizes supported by the video card. */
640 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
641 unsigned int pow2Width, pow2Height;
643 TRACE("surface %p.\n", surface);
645 /* Non-power2 support */
646 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
647 || gl_info->supported[ARB_TEXTURE_RECTANGLE])
649 pow2Width = surface->resource.width;
650 pow2Height = surface->resource.height;
652 else
654 /* Find the nearest pow2 match */
655 pow2Width = pow2Height = 1;
656 while (pow2Width < surface->resource.width)
657 pow2Width <<= 1;
658 while (pow2Height < surface->resource.height)
659 pow2Height <<= 1;
661 surface->pow2Width = pow2Width;
662 surface->pow2Height = pow2Height;
664 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
666 /* TODO: Add support for non power two compressed textures. */
667 if (surface->resource.format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE))
669 FIXME("(%p) Compressed or height scaled non-power-two textures are not supported w(%d) h(%d)\n",
670 surface, surface->resource.width, surface->resource.height);
671 return WINED3DERR_NOTAVAILABLE;
675 if (pow2Width != surface->resource.width
676 || pow2Height != surface->resource.height)
678 surface->flags |= SFLAG_NONPOW2;
681 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
682 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
684 /* One of three options:
685 * 1: Do the same as we do with NPOT and scale the texture, (any
686 * texture ops would require the texture to be scaled which is
687 * potentially slow)
688 * 2: Set the texture to the maximum size (bad idea).
689 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
690 * 4: Create the surface, but allow it to be used only for DirectDraw
691 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
692 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
693 * the render target. */
694 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
696 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
697 return WINED3DERR_NOTAVAILABLE;
700 /* We should never use this surface in combination with OpenGL! */
701 TRACE("Creating an oversized surface: %ux%u.\n",
702 surface->pow2Width, surface->pow2Height);
705 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
706 surface->locations = WINED3D_LOCATION_DISCARDED;
708 if (surface_use_pbo(surface))
709 surface->resource.map_binding = WINED3D_LOCATION_BUFFER;
711 return WINED3D_OK;
714 static void surface_unmap(struct wined3d_surface *surface)
716 struct wined3d_device *device = surface->resource.device;
717 const struct wined3d_gl_info *gl_info;
718 struct wined3d_context *context;
720 TRACE("surface %p.\n", surface);
722 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
724 switch (surface->resource.map_binding)
726 case WINED3D_LOCATION_SYSMEM:
727 case WINED3D_LOCATION_USER_MEMORY:
728 case WINED3D_LOCATION_DIB:
729 break;
731 case WINED3D_LOCATION_BUFFER:
732 context = context_acquire(device, NULL);
733 gl_info = context->gl_info;
735 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
736 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
737 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
738 checkGLcall("glUnmapBuffer");
739 context_release(context);
740 break;
742 default:
743 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
746 if (surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_TEXTURE_RGB))
748 TRACE("Not dirtified, nothing to do.\n");
749 return;
752 if (surface->container->swapchain && surface->container->swapchain->front_buffer == surface->container)
753 surface_load_location(surface, surface->container->resource.draw_binding);
754 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
755 FIXME("Depth / stencil buffer locking is not implemented.\n");
758 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
760 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
761 return FALSE;
762 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
763 return FALSE;
764 return TRUE;
767 static void surface_depth_blt_fbo(const struct wined3d_device *device,
768 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
769 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
771 const struct wined3d_gl_info *gl_info;
772 struct wined3d_context *context;
773 DWORD src_mask, dst_mask;
774 GLbitfield gl_mask;
776 TRACE("device %p\n", device);
777 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
778 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
779 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
780 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
782 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
783 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
785 if (src_mask != dst_mask)
787 ERR("Incompatible formats %s and %s.\n",
788 debug_d3dformat(src_surface->resource.format->id),
789 debug_d3dformat(dst_surface->resource.format->id));
790 return;
793 if (!src_mask)
795 ERR("Not a depth / stencil format: %s.\n",
796 debug_d3dformat(src_surface->resource.format->id));
797 return;
800 gl_mask = 0;
801 if (src_mask & WINED3DFMT_FLAG_DEPTH)
802 gl_mask |= GL_DEPTH_BUFFER_BIT;
803 if (src_mask & WINED3DFMT_FLAG_STENCIL)
804 gl_mask |= GL_STENCIL_BUFFER_BIT;
806 /* Make sure the locations are up-to-date. Loading the destination
807 * surface isn't required if the entire surface is overwritten. */
808 surface_load_location(src_surface, src_location);
809 if (!surface_is_full_rect(dst_surface, dst_rect))
810 surface_load_location(dst_surface, dst_location);
812 context = context_acquire(device, NULL);
813 if (!context->valid)
815 context_release(context);
816 WARN("Invalid context, skipping blit.\n");
817 return;
820 gl_info = context->gl_info;
822 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
823 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
825 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
826 context_set_draw_buffer(context, GL_NONE);
827 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
828 context_invalidate_state(context, STATE_FRAMEBUFFER);
830 if (gl_mask & GL_DEPTH_BUFFER_BIT)
832 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
833 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
835 if (gl_mask & GL_STENCIL_BUFFER_BIT)
837 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
839 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
840 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
842 gl_info->gl_ops.gl.p_glStencilMask(~0U);
843 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
846 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
847 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
849 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
850 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
851 checkGLcall("glBlitFramebuffer()");
853 if (wined3d_settings.strict_draw_ordering)
854 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
856 context_release(context);
859 /* Blit between surface locations. Onscreen on different swapchains is not supported.
860 * Depth / stencil is not supported. */
861 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
862 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
863 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
865 const struct wined3d_gl_info *gl_info;
866 struct wined3d_context *context;
867 RECT src_rect, dst_rect;
868 GLenum gl_filter;
869 GLenum buffer;
871 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
872 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
873 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
874 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
875 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
877 src_rect = *src_rect_in;
878 dst_rect = *dst_rect_in;
880 switch (filter)
882 case WINED3D_TEXF_LINEAR:
883 gl_filter = GL_LINEAR;
884 break;
886 default:
887 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
888 case WINED3D_TEXF_NONE:
889 case WINED3D_TEXF_POINT:
890 gl_filter = GL_NEAREST;
891 break;
894 /* Resolve the source surface first if needed. */
895 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
896 && (src_surface->resource.format->id != dst_surface->resource.format->id
897 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
898 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
899 src_location = WINED3D_LOCATION_RB_RESOLVED;
901 /* Make sure the locations are up-to-date. Loading the destination
902 * surface isn't required if the entire surface is overwritten. (And is
903 * in fact harmful if we're being called by surface_load_location() with
904 * the purpose of loading the destination surface.) */
905 surface_load_location(src_surface, src_location);
906 if (!surface_is_full_rect(dst_surface, &dst_rect))
907 surface_load_location(dst_surface, dst_location);
909 if (src_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, src_surface);
910 else if (dst_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, dst_surface);
911 else context = context_acquire(device, NULL);
913 if (!context->valid)
915 context_release(context);
916 WARN("Invalid context, skipping blit.\n");
917 return;
920 gl_info = context->gl_info;
922 if (src_location == WINED3D_LOCATION_DRAWABLE)
924 TRACE("Source surface %p is onscreen.\n", src_surface);
925 buffer = surface_get_gl_buffer(src_surface);
926 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
928 else
930 TRACE("Source surface %p is offscreen.\n", src_surface);
931 buffer = GL_COLOR_ATTACHMENT0;
934 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
935 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
936 checkGLcall("glReadBuffer()");
937 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
939 if (dst_location == WINED3D_LOCATION_DRAWABLE)
941 TRACE("Destination surface %p is onscreen.\n", dst_surface);
942 buffer = surface_get_gl_buffer(dst_surface);
943 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
945 else
947 TRACE("Destination surface %p is offscreen.\n", dst_surface);
948 buffer = GL_COLOR_ATTACHMENT0;
951 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
952 context_set_draw_buffer(context, buffer);
953 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
954 context_invalidate_state(context, STATE_FRAMEBUFFER);
956 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
957 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
958 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
959 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
960 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
962 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
963 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
965 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
966 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
967 checkGLcall("glBlitFramebuffer()");
969 if (wined3d_settings.strict_draw_ordering
970 || (dst_location == WINED3D_LOCATION_DRAWABLE
971 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
972 gl_info->gl_ops.gl.p_glFlush();
974 context_release(context);
977 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
978 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
979 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
981 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
982 return FALSE;
984 /* Source and/or destination need to be on the GL side */
985 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
986 return FALSE;
988 switch (blit_op)
990 case WINED3D_BLIT_OP_COLOR_BLIT:
991 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
992 return FALSE;
993 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
994 return FALSE;
995 break;
997 case WINED3D_BLIT_OP_DEPTH_BLIT:
998 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
999 return FALSE;
1000 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1001 return FALSE;
1002 break;
1004 default:
1005 return FALSE;
1008 if (!(src_format->id == dst_format->id
1009 || (is_identity_fixup(src_format->color_fixup)
1010 && is_identity_fixup(dst_format->color_fixup))))
1011 return FALSE;
1013 return TRUE;
1016 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1018 const struct wined3d_format *format = surface->resource.format;
1020 switch (format->id)
1022 case WINED3DFMT_S1_UINT_D15_UNORM:
1023 *float_depth = depth / (float)0x00007fff;
1024 break;
1026 case WINED3DFMT_D16_UNORM:
1027 *float_depth = depth / (float)0x0000ffff;
1028 break;
1030 case WINED3DFMT_D24_UNORM_S8_UINT:
1031 case WINED3DFMT_X8D24_UNORM:
1032 *float_depth = depth / (float)0x00ffffff;
1033 break;
1035 case WINED3DFMT_D32_UNORM:
1036 *float_depth = depth / (float)0xffffffff;
1037 break;
1039 default:
1040 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1041 return FALSE;
1044 return TRUE;
1047 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1049 const struct wined3d_resource *resource = &surface->container->resource;
1050 struct wined3d_device *device = resource->device;
1051 const struct blit_shader *blitter;
1053 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, WINED3D_BLIT_OP_DEPTH_FILL,
1054 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1055 if (!blitter)
1057 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1058 return WINED3DERR_INVALIDCALL;
1061 return blitter->depth_fill(device, surface, rect, depth);
1064 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1065 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1067 struct wined3d_device *device = src_surface->resource.device;
1069 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1070 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1071 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1072 return WINED3DERR_INVALIDCALL;
1074 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1076 surface_modify_ds_location(dst_surface, dst_location,
1077 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1079 return WINED3D_OK;
1082 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1083 struct wined3d_surface *render_target)
1085 TRACE("surface %p, render_target %p.\n", surface, render_target);
1087 /* TODO: Check surface sizes, pools, etc. */
1089 if (render_target->resource.multisample_type)
1090 return WINED3DERR_INVALIDCALL;
1092 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1095 /* Context activation is done by the caller. */
1096 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1098 GL_EXTCALL(glDeleteBuffers(1, &surface->pbo));
1099 checkGLcall("glDeleteBuffers(1, &surface->pbo)");
1101 surface->pbo = 0;
1102 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1105 static ULONG surface_resource_incref(struct wined3d_resource *resource)
1107 return wined3d_surface_incref(surface_from_resource(resource));
1110 static ULONG surface_resource_decref(struct wined3d_resource *resource)
1112 return wined3d_surface_decref(surface_from_resource(resource));
1115 static void surface_unload(struct wined3d_resource *resource)
1117 struct wined3d_surface *surface = surface_from_resource(resource);
1118 struct wined3d_renderbuffer_entry *entry, *entry2;
1119 struct wined3d_device *device = resource->device;
1120 const struct wined3d_gl_info *gl_info;
1121 struct wined3d_context *context;
1123 TRACE("surface %p.\n", surface);
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 surface_prepare_system_memory(surface);
1137 memset(surface->resource.heap_memory, 0, surface->resource.size);
1138 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1139 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1141 /* We also get here when the ddraw swapchain is destroyed, for example
1142 * for a mode switch. In this case this surface won't necessarily be
1143 * an implicit surface. We have to mark it lost so that the
1144 * application can restore it after the mode switch. */
1145 surface->flags |= SFLAG_LOST;
1147 else
1149 surface_prepare_map_memory(surface);
1150 surface_load_location(surface, surface->resource.map_binding);
1151 surface_invalidate_location(surface, ~surface->resource.map_binding);
1154 context = context_acquire(device, NULL);
1155 gl_info = context->gl_info;
1157 /* Destroy PBOs, but load them into real sysmem before */
1158 if (surface->pbo)
1159 surface_remove_pbo(surface, gl_info);
1161 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1162 * all application-created targets the application has to release the surface
1163 * before calling _Reset
1165 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1167 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1168 list_remove(&entry->entry);
1169 HeapFree(GetProcessHeap(), 0, entry);
1171 list_init(&surface->renderbuffers);
1172 surface->current_renderbuffer = NULL;
1174 if (surface->rb_multisample)
1176 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1177 surface->rb_multisample = 0;
1179 if (surface->rb_resolved)
1181 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1182 surface->rb_resolved = 0;
1185 context_release(context);
1187 resource_unload(resource);
1190 static const struct wined3d_resource_ops surface_resource_ops =
1192 surface_resource_incref,
1193 surface_resource_decref,
1194 surface_unload,
1197 static const struct wined3d_surface_ops surface_ops =
1199 surface_private_setup,
1200 surface_unmap,
1203 /*****************************************************************************
1204 * Initializes the GDI surface, aka creates the DIB section we render to
1205 * The DIB section creation is done by calling GetDC, which will create the
1206 * section and releasing the dc to allow the app to use it. The dib section
1207 * will stay until the surface is released
1209 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1210 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1211 * avoid confusion in the shared surface code.
1213 * Returns:
1214 * WINED3D_OK on success
1215 * The return values of called methods on failure
1217 *****************************************************************************/
1218 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1220 HRESULT hr;
1222 TRACE("surface %p.\n", surface);
1224 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1226 ERR("Overlays not yet supported by GDI surfaces.\n");
1227 return WINED3DERR_INVALIDCALL;
1230 /* Sysmem textures have memory already allocated - release it,
1231 * this avoids an unnecessary memcpy. */
1232 hr = surface_create_dib_section(surface);
1233 if (FAILED(hr))
1234 return hr;
1235 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1237 /* We don't mind the nonpow2 stuff in GDI. */
1238 surface->pow2Width = surface->resource.width;
1239 surface->pow2Height = surface->resource.height;
1241 return WINED3D_OK;
1244 static void gdi_surface_unmap(struct wined3d_surface *surface)
1246 TRACE("surface %p.\n", surface);
1248 /* Tell the swapchain to update the screen. */
1249 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
1250 x11_copy_to_screen(surface->container->swapchain, &surface->lockedRect);
1252 memset(&surface->lockedRect, 0, sizeof(RECT));
1255 static const struct wined3d_surface_ops gdi_surface_ops =
1257 gdi_surface_private_setup,
1258 gdi_surface_unmap,
1261 /* This call just downloads data, the caller is responsible for binding the
1262 * correct texture. */
1263 /* Context activation is done by the caller. */
1264 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1265 DWORD dst_location)
1267 const struct wined3d_format *format = surface->resource.format;
1268 struct wined3d_bo_address data;
1270 /* Only support read back of converted P8 surfaces. */
1271 if (surface->container->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1273 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1274 return;
1277 surface_get_memory(surface, &data, dst_location);
1279 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1281 TRACE("(%p) : Calling glGetCompressedTexImage level %d, format %#x, type %#x, data %p.\n",
1282 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1284 if (data.buffer_object)
1286 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1287 checkGLcall("glBindBuffer");
1288 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, NULL));
1289 checkGLcall("glGetCompressedTexImage");
1290 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1291 checkGLcall("glBindBuffer");
1293 else
1295 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target,
1296 surface->texture_level, data.addr));
1297 checkGLcall("glGetCompressedTexImage");
1300 else
1302 void *mem;
1303 GLenum gl_format = format->glFormat;
1304 GLenum gl_type = format->glType;
1305 int src_pitch = 0;
1306 int dst_pitch = 0;
1308 if (surface->flags & SFLAG_NONPOW2)
1310 unsigned char alignment = surface->resource.device->surface_alignment;
1311 src_pitch = format->byte_count * surface->pow2Width;
1312 dst_pitch = wined3d_surface_get_pitch(surface);
1313 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1314 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1316 else
1318 mem = data.addr;
1321 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1322 surface, surface->texture_level, gl_format, gl_type, mem);
1324 if (data.buffer_object)
1326 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1327 checkGLcall("glBindBuffer");
1329 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1330 gl_format, gl_type, NULL);
1331 checkGLcall("glGetTexImage");
1333 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1334 checkGLcall("glBindBuffer");
1336 else
1338 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1339 gl_format, gl_type, mem);
1340 checkGLcall("glGetTexImage");
1343 if (surface->flags & SFLAG_NONPOW2)
1345 const BYTE *src_data;
1346 BYTE *dst_data;
1347 UINT y;
1349 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1350 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1351 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1353 * We're doing this...
1355 * instead of boxing the texture :
1356 * |<-texture width ->| -->pow2width| /\
1357 * |111111111111111111| | |
1358 * |222 Texture 222222| boxed empty | texture height
1359 * |3333 Data 33333333| | |
1360 * |444444444444444444| | \/
1361 * ----------------------------------- |
1362 * | boxed empty | boxed empty | pow2height
1363 * | | | \/
1364 * -----------------------------------
1367 * we're repacking the data to the expected texture width
1369 * |<-texture width ->| -->pow2width| /\
1370 * |111111111111111111222222222222222| |
1371 * |222333333333333333333444444444444| texture height
1372 * |444444 | |
1373 * | | \/
1374 * | | |
1375 * | empty | pow2height
1376 * | | \/
1377 * -----------------------------------
1379 * == is the same as
1381 * |<-texture width ->| /\
1382 * |111111111111111111|
1383 * |222222222222222222|texture height
1384 * |333333333333333333|
1385 * |444444444444444444| \/
1386 * --------------------
1388 * This also means that any references to surface memory should work with the data as if it were a
1389 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1391 * internally the texture is still stored in a boxed format so any references to textureName will
1392 * get a boxed texture with width pow2width and not a texture of width resource.width.
1394 * Performance should not be an issue, because applications normally do not lock the surfaces when
1395 * rendering. If an app does, the WINED3D_TEXTURE_DYNAMIC_MAP flag will kick in and the memory copy
1396 * won't be released, and doesn't have to be re-read. */
1397 src_data = mem;
1398 dst_data = data.addr;
1399 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1400 for (y = 0; y < surface->resource.height; ++y)
1402 memcpy(dst_data, src_data, dst_pitch);
1403 src_data += src_pitch;
1404 dst_data += dst_pitch;
1407 HeapFree(GetProcessHeap(), 0, mem);
1412 /* This call just uploads data, the caller is responsible for binding the
1413 * correct texture. */
1414 /* Context activation is done by the caller. */
1415 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1416 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1417 BOOL srgb, const struct wined3d_const_bo_address *data)
1419 UINT update_w = src_rect->right - src_rect->left;
1420 UINT update_h = src_rect->bottom - src_rect->top;
1422 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1423 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1424 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1426 if (surface->resource.map_count)
1428 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
1429 surface->container->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
1432 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1434 update_h *= format->height_scale.numerator;
1435 update_h /= format->height_scale.denominator;
1438 if (data->buffer_object)
1440 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
1441 checkGLcall("glBindBuffer");
1444 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1446 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1447 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1448 const BYTE *addr = data->addr;
1449 GLenum internal;
1451 addr += (src_rect->top / format->block_height) * src_pitch;
1452 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1454 if (srgb)
1455 internal = format->glGammaInternal;
1456 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1457 && wined3d_resource_is_offscreen(&surface->container->resource))
1458 internal = format->rtInternal;
1459 else
1460 internal = format->glInternal;
1462 TRACE("glCompressedTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, "
1463 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1464 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1466 if (row_length == src_pitch)
1468 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1469 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1471 else
1473 UINT row, y;
1475 /* glCompressedTexSubImage2D() ignores pixel store state, so we
1476 * can't use the unpack row length like for glTexSubImage2D. */
1477 for (row = 0, y = dst_point->y; row < row_count; ++row)
1479 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1480 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1481 y += format->block_height;
1482 addr += src_pitch;
1485 checkGLcall("glCompressedTexSubImage2D");
1487 else
1489 const BYTE *addr = data->addr;
1491 addr += src_rect->top * src_pitch;
1492 addr += src_rect->left * format->byte_count;
1494 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1495 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1496 update_w, update_h, format->glFormat, format->glType, addr);
1498 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1499 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1500 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1501 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1502 checkGLcall("glTexSubImage2D");
1505 if (data->buffer_object)
1507 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1508 checkGLcall("glBindBuffer");
1511 if (wined3d_settings.strict_draw_ordering)
1512 gl_info->gl_ops.gl.p_glFlush();
1514 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1516 struct wined3d_device *device = surface->resource.device;
1517 unsigned int i;
1519 for (i = 0; i < device->context_count; ++i)
1521 context_surface_update(device->contexts[i], surface);
1526 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
1528 UINT width_mask, height_mask;
1530 if (!rect->left && !rect->top
1531 && rect->right == surface->resource.width
1532 && rect->bottom == surface->resource.height)
1533 return TRUE;
1535 /* This assumes power of two block sizes, but NPOT block sizes would be
1536 * silly anyway. */
1537 width_mask = surface->resource.format->block_width - 1;
1538 height_mask = surface->resource.format->block_height - 1;
1540 if (!(rect->left & width_mask) && !(rect->top & height_mask)
1541 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
1542 return TRUE;
1544 return FALSE;
1547 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1548 struct wined3d_surface *src_surface, const RECT *src_rect)
1550 const struct wined3d_format *src_format;
1551 const struct wined3d_format *dst_format;
1552 const struct wined3d_gl_info *gl_info;
1553 struct wined3d_context *context;
1554 struct wined3d_bo_address data;
1555 UINT update_w, update_h;
1556 UINT dst_w, dst_h;
1557 RECT r, dst_rect;
1558 UINT src_pitch;
1559 POINT p;
1561 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1562 dst_surface, wine_dbgstr_point(dst_point),
1563 src_surface, wine_dbgstr_rect(src_rect));
1565 src_format = src_surface->resource.format;
1566 dst_format = dst_surface->resource.format;
1568 if (src_format->id != dst_format->id)
1570 WARN("Source and destination surfaces should have the same format.\n");
1571 return WINED3DERR_INVALIDCALL;
1574 if (!dst_point)
1576 p.x = 0;
1577 p.y = 0;
1578 dst_point = &p;
1580 else if (dst_point->x < 0 || dst_point->y < 0)
1582 WARN("Invalid destination point.\n");
1583 return WINED3DERR_INVALIDCALL;
1586 if (!src_rect)
1588 r.left = 0;
1589 r.top = 0;
1590 r.right = src_surface->resource.width;
1591 r.bottom = src_surface->resource.height;
1592 src_rect = &r;
1594 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1595 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1597 WARN("Invalid source rectangle.\n");
1598 return WINED3DERR_INVALIDCALL;
1601 dst_w = dst_surface->resource.width;
1602 dst_h = dst_surface->resource.height;
1604 update_w = src_rect->right - src_rect->left;
1605 update_h = src_rect->bottom - src_rect->top;
1607 if (update_w > dst_w || dst_point->x > dst_w - update_w
1608 || update_h > dst_h || dst_point->y > dst_h - update_h)
1610 WARN("Destination out of bounds.\n");
1611 return WINED3DERR_INVALIDCALL;
1614 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
1616 WARN("Source rectangle not block-aligned.\n");
1617 return WINED3DERR_INVALIDCALL;
1620 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1621 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
1623 WARN("Destination rectangle not block-aligned.\n");
1624 return WINED3DERR_INVALIDCALL;
1627 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1628 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_surface->container, FALSE))
1629 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1631 context = context_acquire(dst_surface->resource.device, NULL);
1632 gl_info = context->gl_info;
1634 /* Only load the surface for partial updates. For newly allocated texture
1635 * the texture wouldn't be the current location, and we'd upload zeroes
1636 * just to overwrite them again. */
1637 if (update_w == dst_w && update_h == dst_h)
1638 wined3d_texture_prepare_texture(dst_surface->container, context, FALSE);
1639 else
1640 surface_load_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1641 wined3d_texture_bind_and_dirtify(dst_surface->container, context, FALSE);
1643 surface_get_memory(src_surface, &data, src_surface->locations);
1644 src_pitch = wined3d_surface_get_pitch(src_surface);
1646 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1647 src_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1649 context_release(context);
1651 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1652 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1654 return WINED3D_OK;
1657 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1658 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1659 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1660 /* Context activation is done by the caller. */
1661 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1663 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1664 struct wined3d_renderbuffer_entry *entry;
1665 GLuint renderbuffer = 0;
1666 unsigned int src_width, src_height;
1667 unsigned int width, height;
1669 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1671 width = rt->pow2Width;
1672 height = rt->pow2Height;
1674 else
1676 width = surface->pow2Width;
1677 height = surface->pow2Height;
1680 src_width = surface->pow2Width;
1681 src_height = surface->pow2Height;
1683 /* A depth stencil smaller than the render target is not valid */
1684 if (width > src_width || height > src_height) return;
1686 /* Remove any renderbuffer set if the sizes match */
1687 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1688 || (width == src_width && height == src_height))
1690 surface->current_renderbuffer = NULL;
1691 return;
1694 /* Look if we've already got a renderbuffer of the correct dimensions */
1695 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1697 if (entry->width == width && entry->height == height)
1699 renderbuffer = entry->id;
1700 surface->current_renderbuffer = entry;
1701 break;
1705 if (!renderbuffer)
1707 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1708 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1709 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1710 surface->resource.format->glInternal, width, height);
1712 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1713 entry->width = width;
1714 entry->height = height;
1715 entry->id = renderbuffer;
1716 list_add_head(&surface->renderbuffers, &entry->entry);
1718 surface->current_renderbuffer = entry;
1721 checkGLcall("set_compatible_renderbuffer");
1724 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1726 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1728 TRACE("surface %p.\n", surface);
1730 if (!swapchain)
1732 ERR("Surface %p is not on a swapchain.\n", surface);
1733 return GL_NONE;
1736 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
1738 if (swapchain->render_to_fbo)
1740 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1741 return GL_COLOR_ATTACHMENT0;
1743 TRACE("Returning GL_BACK\n");
1744 return GL_BACK;
1746 else if (surface->container == swapchain->front_buffer)
1748 TRACE("Returning GL_FRONT\n");
1749 return GL_FRONT;
1752 FIXME("Higher back buffer, returning GL_BACK\n");
1753 return GL_BACK;
1756 void surface_load(struct wined3d_surface *surface, BOOL srgb)
1758 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1760 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1762 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
1763 ERR("Not supported on scratch surfaces.\n");
1765 if (surface->locations & location)
1767 TRACE("surface is already in texture\n");
1768 return;
1770 TRACE("Reloading because surface is dirty.\n");
1772 surface_load_location(surface, location);
1773 surface_evict_sysmem(surface);
1776 /* See also float_16_to_32() in wined3d_private.h */
1777 static inline unsigned short float_32_to_16(const float *in)
1779 int exp = 0;
1780 float tmp = fabsf(*in);
1781 unsigned int mantissa;
1782 unsigned short ret;
1784 /* Deal with special numbers */
1785 if (*in == 0.0f)
1786 return 0x0000;
1787 if (isnan(*in))
1788 return 0x7c01;
1789 if (isinf(*in))
1790 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1792 if (tmp < powf(2, 10))
1796 tmp = tmp * 2.0f;
1797 exp--;
1798 } while (tmp < powf(2, 10));
1800 else if (tmp >= powf(2, 11))
1804 tmp /= 2.0f;
1805 exp++;
1806 } while (tmp >= powf(2, 11));
1809 mantissa = (unsigned int)tmp;
1810 if (tmp - mantissa >= 0.5f)
1811 ++mantissa; /* Round to nearest, away from zero. */
1813 exp += 10; /* Normalize the mantissa. */
1814 exp += 15; /* Exponent is encoded with excess 15. */
1816 if (exp > 30) /* too big */
1818 ret = 0x7c00; /* INF */
1820 else if (exp <= 0)
1822 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1823 while (exp <= 0)
1825 mantissa = mantissa >> 1;
1826 ++exp;
1828 ret = mantissa & 0x3ff;
1830 else
1832 ret = (exp << 10) | (mantissa & 0x3ff);
1835 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1836 return ret;
1839 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
1841 TRACE("surface %p, container %p.\n", surface, surface->container);
1843 return wined3d_texture_incref(surface->container);
1846 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
1848 TRACE("surface %p, container %p.\n", surface, surface->container);
1850 return wined3d_texture_decref(surface->container);
1853 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
1855 TRACE("surface %p.\n", surface);
1857 if (!surface->resource.device->d3d_initialized)
1859 ERR("D3D not initialized.\n");
1860 return;
1863 wined3d_texture_preload(surface->container);
1866 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
1868 TRACE("surface %p.\n", surface);
1870 return surface->resource.parent;
1873 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
1875 TRACE("surface %p.\n", surface);
1877 return &surface->resource;
1880 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
1882 TRACE("surface %p, flags %#x.\n", surface, flags);
1884 switch (flags)
1886 case WINEDDGBS_CANBLT:
1887 case WINEDDGBS_ISBLTDONE:
1888 return WINED3D_OK;
1890 default:
1891 return WINED3DERR_INVALIDCALL;
1895 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
1897 TRACE("surface %p, flags %#x.\n", surface, flags);
1899 /* XXX: DDERR_INVALIDSURFACETYPE */
1901 switch (flags)
1903 case WINEDDGFS_CANFLIP:
1904 case WINEDDGFS_ISFLIPDONE:
1905 return WINED3D_OK;
1907 default:
1908 return WINED3DERR_INVALIDCALL;
1912 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
1914 TRACE("surface %p.\n", surface);
1916 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
1917 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
1920 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
1922 TRACE("surface %p.\n", surface);
1924 surface->flags &= ~SFLAG_LOST;
1925 return WINED3D_OK;
1928 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
1930 unsigned int alignment;
1931 DWORD pitch;
1933 TRACE("surface %p.\n", surface);
1935 if (surface->pitch)
1936 return surface->pitch;
1938 alignment = surface->resource.device->surface_alignment;
1939 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
1940 pitch = (pitch + alignment - 1) & ~(alignment - 1);
1942 TRACE("Returning %u.\n", pitch);
1944 return pitch;
1947 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
1949 LONG w, h;
1951 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
1953 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1955 WARN("Not an overlay surface.\n");
1956 return WINEDDERR_NOTAOVERLAYSURFACE;
1959 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
1960 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
1961 surface->overlay_destrect.left = x;
1962 surface->overlay_destrect.top = y;
1963 surface->overlay_destrect.right = x + w;
1964 surface->overlay_destrect.bottom = y + h;
1966 return WINED3D_OK;
1969 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
1971 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
1973 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1975 TRACE("Not an overlay surface.\n");
1976 return WINEDDERR_NOTAOVERLAYSURFACE;
1979 if (!surface->overlay_dest)
1981 TRACE("Overlay not visible.\n");
1982 *x = 0;
1983 *y = 0;
1984 return WINEDDERR_OVERLAYNOTVISIBLE;
1987 *x = surface->overlay_destrect.left;
1988 *y = surface->overlay_destrect.top;
1990 TRACE("Returning position %d, %d.\n", *x, *y);
1992 return WINED3D_OK;
1995 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
1996 DWORD flags, struct wined3d_surface *ref)
1998 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2000 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2002 TRACE("Not an overlay surface.\n");
2003 return WINEDDERR_NOTAOVERLAYSURFACE;
2006 return WINED3D_OK;
2009 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2010 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2012 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2013 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2015 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2017 WARN("Not an overlay surface.\n");
2018 return WINEDDERR_NOTAOVERLAYSURFACE;
2020 else if (!dst_surface)
2022 WARN("Dest surface is NULL.\n");
2023 return WINED3DERR_INVALIDCALL;
2026 if (src_rect)
2028 surface->overlay_srcrect = *src_rect;
2030 else
2032 surface->overlay_srcrect.left = 0;
2033 surface->overlay_srcrect.top = 0;
2034 surface->overlay_srcrect.right = surface->resource.width;
2035 surface->overlay_srcrect.bottom = surface->resource.height;
2038 if (dst_rect)
2040 surface->overlay_destrect = *dst_rect;
2042 else
2044 surface->overlay_destrect.left = 0;
2045 surface->overlay_destrect.top = 0;
2046 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2047 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2050 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2052 surface->overlay_dest = NULL;
2053 list_remove(&surface->overlay_entry);
2056 if (flags & WINEDDOVER_SHOW)
2058 if (surface->overlay_dest != dst_surface)
2060 surface->overlay_dest = dst_surface;
2061 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2064 else if (flags & WINEDDOVER_HIDE)
2066 /* tests show that the rectangles are erased on hide */
2067 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2068 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2069 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2070 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2071 surface->overlay_dest = NULL;
2074 return WINED3D_OK;
2077 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface,
2078 const struct wined3d_gl_info *gl_info, void *mem, unsigned int pitch)
2080 struct wined3d_resource *texture_resource = &surface->container->resource;
2081 unsigned int width, height;
2082 BOOL create_dib = FALSE;
2083 DWORD valid_location = 0;
2084 HRESULT hr;
2086 if (surface->flags & SFLAG_DIBSECTION)
2088 DeleteDC(surface->hDC);
2089 DeleteObject(surface->dib.DIBsection);
2090 surface->dib.bitmap_data = NULL;
2091 surface->flags &= ~SFLAG_DIBSECTION;
2092 create_dib = TRUE;
2095 surface->locations = 0;
2096 wined3d_resource_free_sysmem(&surface->resource);
2098 width = texture_resource->width;
2099 height = texture_resource->height;
2100 surface->resource.width = width;
2101 surface->resource.height = height;
2102 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2103 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2105 surface->pow2Width = width;
2106 surface->pow2Height = height;
2108 else
2110 surface->pow2Width = surface->pow2Height = 1;
2111 while (surface->pow2Width < width)
2112 surface->pow2Width <<= 1;
2113 while (surface->pow2Height < height)
2114 surface->pow2Height <<= 1;
2117 if (surface->pow2Width != width || surface->pow2Height != height)
2118 surface->flags |= SFLAG_NONPOW2;
2119 else
2120 surface->flags &= ~SFLAG_NONPOW2;
2122 if ((surface->user_memory = mem))
2124 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
2125 valid_location = WINED3D_LOCATION_USER_MEMORY;
2127 surface->pitch = pitch;
2128 surface->resource.format = texture_resource->format;
2129 surface->resource.multisample_type = texture_resource->multisample_type;
2130 surface->resource.multisample_quality = texture_resource->multisample_quality;
2131 if (surface->pitch)
2133 surface->resource.size = height * surface->pitch;
2135 else
2137 /* User memory surfaces don't have the regular surface alignment. */
2138 surface->resource.size = wined3d_format_calculate_size(texture_resource->format,
2139 1, width, height, 1);
2140 surface->pitch = wined3d_format_calculate_pitch(texture_resource->format, width);
2143 /* The format might be changed to a format that needs conversion.
2144 * If the surface didn't use PBOs previously but could now, don't
2145 * change it - whatever made us not use PBOs might come back, e.g.
2146 * color keys. */
2147 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2148 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2150 if (create_dib)
2152 if (FAILED(hr = surface_create_dib_section(surface)))
2154 ERR("Failed to create dib section, hr %#x.\n", hr);
2155 return hr;
2157 if (!valid_location)
2158 valid_location = WINED3D_LOCATION_DIB;
2161 if (!valid_location)
2163 surface_prepare_system_memory(surface);
2164 valid_location = WINED3D_LOCATION_SYSMEM;
2167 surface_validate_location(surface, valid_location);
2169 return WINED3D_OK;
2172 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2173 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2175 unsigned short *dst_s;
2176 const float *src_f;
2177 unsigned int x, y;
2179 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2181 for (y = 0; y < h; ++y)
2183 src_f = (const float *)(src + y * pitch_in);
2184 dst_s = (unsigned short *) (dst + y * pitch_out);
2185 for (x = 0; x < w; ++x)
2187 dst_s[x] = float_32_to_16(src_f + x);
2192 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2193 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2195 static const unsigned char convert_5to8[] =
2197 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2198 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2199 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2200 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2202 static const unsigned char convert_6to8[] =
2204 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2205 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2206 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2207 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2208 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2209 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2210 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2211 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2213 unsigned int x, y;
2215 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2217 for (y = 0; y < h; ++y)
2219 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2220 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2221 for (x = 0; x < w; ++x)
2223 WORD pixel = src_line[x];
2224 dst_line[x] = 0xff000000
2225 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2226 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2227 | convert_5to8[(pixel & 0x001f)];
2232 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2233 * in both cases we're just setting the X / Alpha channel to 0xff. */
2234 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2235 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2237 unsigned int x, y;
2239 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2241 for (y = 0; y < h; ++y)
2243 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2244 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2246 for (x = 0; x < w; ++x)
2248 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2253 static inline BYTE cliptobyte(int x)
2255 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2258 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2259 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2261 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2262 unsigned int x, y;
2264 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2266 for (y = 0; y < h; ++y)
2268 const BYTE *src_line = src + y * pitch_in;
2269 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2270 for (x = 0; x < w; ++x)
2272 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2273 * C = Y - 16; D = U - 128; E = V - 128;
2274 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2275 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2276 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2277 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2278 * U and V are shared between the pixels. */
2279 if (!(x & 1)) /* For every even pixel, read new U and V. */
2281 d = (int) src_line[1] - 128;
2282 e = (int) src_line[3] - 128;
2283 r2 = 409 * e + 128;
2284 g2 = - 100 * d - 208 * e + 128;
2285 b2 = 516 * d + 128;
2287 c2 = 298 * ((int) src_line[0] - 16);
2288 dst_line[x] = 0xff000000
2289 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2290 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2291 | cliptobyte((c2 + b2) >> 8); /* blue */
2292 /* Scale RGB values to 0..255 range,
2293 * then clip them if still not in range (may be negative),
2294 * then shift them within DWORD if necessary. */
2295 src_line += 2;
2300 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2301 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2303 unsigned int x, y;
2304 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2306 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2308 for (y = 0; y < h; ++y)
2310 const BYTE *src_line = src + y * pitch_in;
2311 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2312 for (x = 0; x < w; ++x)
2314 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2315 * C = Y - 16; D = U - 128; E = V - 128;
2316 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2317 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2318 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2319 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2320 * U and V are shared between the pixels. */
2321 if (!(x & 1)) /* For every even pixel, read new U and V. */
2323 d = (int) src_line[1] - 128;
2324 e = (int) src_line[3] - 128;
2325 r2 = 409 * e + 128;
2326 g2 = - 100 * d - 208 * e + 128;
2327 b2 = 516 * d + 128;
2329 c2 = 298 * ((int) src_line[0] - 16);
2330 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2331 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2332 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2333 /* Scale RGB values to 0..255 range,
2334 * then clip them if still not in range (may be negative),
2335 * then shift them within DWORD if necessary. */
2336 src_line += 2;
2341 struct d3dfmt_converter_desc
2343 enum wined3d_format_id from, to;
2344 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2347 static const struct d3dfmt_converter_desc converters[] =
2349 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2350 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2351 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2352 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2353 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2354 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2357 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2358 enum wined3d_format_id to)
2360 unsigned int i;
2362 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2364 if (converters[i].from == from && converters[i].to == to)
2365 return &converters[i];
2368 return NULL;
2371 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2373 struct wined3d_map_desc src_map, dst_map;
2374 const struct d3dfmt_converter_desc *conv;
2375 struct wined3d_texture *ret = NULL;
2376 struct wined3d_resource_desc desc;
2377 struct wined3d_surface *dst;
2379 conv = find_converter(source->resource.format->id, to_fmt);
2380 if (!conv)
2382 FIXME("Cannot find a conversion function from format %s to %s.\n",
2383 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2384 return NULL;
2387 /* FIXME: Multisampled conversion? */
2388 wined3d_resource_get_desc(&source->resource, &desc);
2389 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2390 desc.format = to_fmt;
2391 desc.usage = 0;
2392 desc.pool = WINED3D_POOL_SCRATCH;
2393 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2394 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, NULL, &wined3d_null_parent_ops, &ret)))
2396 ERR("Failed to create a destination surface for conversion.\n");
2397 return NULL;
2399 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2401 memset(&src_map, 0, sizeof(src_map));
2402 memset(&dst_map, 0, sizeof(dst_map));
2404 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2406 ERR("Failed to lock the source surface.\n");
2407 wined3d_texture_decref(ret);
2408 return NULL;
2410 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2412 ERR("Failed to lock the destination surface.\n");
2413 wined3d_surface_unmap(source);
2414 wined3d_texture_decref(ret);
2415 return NULL;
2418 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2419 source->resource.width, source->resource.height);
2421 wined3d_surface_unmap(dst);
2422 wined3d_surface_unmap(source);
2424 return ret;
2427 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2428 unsigned int bpp, UINT pitch, DWORD color)
2430 BYTE *first;
2431 unsigned int x, y;
2433 /* Do first row */
2435 #define COLORFILL_ROW(type) \
2436 do { \
2437 type *d = (type *)buf; \
2438 for (x = 0; x < width; ++x) \
2439 d[x] = (type)color; \
2440 } while(0)
2442 switch (bpp)
2444 case 1:
2445 COLORFILL_ROW(BYTE);
2446 break;
2448 case 2:
2449 COLORFILL_ROW(WORD);
2450 break;
2452 case 3:
2454 BYTE *d = buf;
2455 for (x = 0; x < width; ++x, d += 3)
2457 d[0] = (color ) & 0xff;
2458 d[1] = (color >> 8) & 0xff;
2459 d[2] = (color >> 16) & 0xff;
2461 break;
2463 case 4:
2464 COLORFILL_ROW(DWORD);
2465 break;
2467 default:
2468 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2469 return WINED3DERR_NOTAVAILABLE;
2472 #undef COLORFILL_ROW
2474 /* Now copy first row. */
2475 first = buf;
2476 for (y = 1; y < height; ++y)
2478 buf += pitch;
2479 memcpy(buf, first, width * bpp);
2482 return WINED3D_OK;
2485 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2487 return surface_from_resource(resource);
2490 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2492 TRACE("surface %p.\n", surface);
2494 if (!surface->resource.map_count)
2496 WARN("Trying to unmap unmapped surface.\n");
2497 return WINEDDERR_NOTLOCKED;
2499 --surface->resource.map_count;
2501 surface->surface_ops->surface_unmap(surface);
2503 return WINED3D_OK;
2506 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2507 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
2509 const struct wined3d_format *format = surface->resource.format;
2510 struct wined3d_device *device = surface->resource.device;
2511 struct wined3d_context *context;
2512 const struct wined3d_gl_info *gl_info;
2513 BYTE *base_memory;
2515 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
2516 surface, map_desc, wine_dbgstr_rect(rect), flags);
2518 if (surface->resource.map_count)
2520 WARN("Surface is already mapped.\n");
2521 return WINED3DERR_INVALIDCALL;
2524 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
2525 && !surface_check_block_align(surface, rect))
2527 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
2528 wine_dbgstr_rect(rect), format->block_width, format->block_height);
2530 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2531 return WINED3DERR_INVALIDCALL;
2534 ++surface->resource.map_count;
2536 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2537 WARN("Trying to lock unlockable surface.\n");
2539 /* Performance optimization: Count how often a surface is mapped, if it is
2540 * mapped regularly do not throw away the system memory copy. This avoids
2541 * the need to download the surface from OpenGL all the time. The surface
2542 * is still downloaded if the OpenGL texture is changed. Note that this
2543 * only really makes sense for managed textures.*/
2544 if (!(surface->container->flags & WINED3D_TEXTURE_DYNAMIC_MAP)
2545 && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2547 if (++surface->lockCount > MAXLOCKCOUNT)
2549 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2550 surface->container->flags |= WINED3D_TEXTURE_DYNAMIC_MAP;
2554 surface_prepare_map_memory(surface);
2555 if (flags & WINED3D_MAP_DISCARD)
2557 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2558 wined3d_debug_location(surface->resource.map_binding));
2559 surface_validate_location(surface, surface->resource.map_binding);
2561 else
2563 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2564 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2566 surface_load_location(surface, surface->resource.map_binding);
2569 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2570 surface_invalidate_location(surface, ~surface->resource.map_binding);
2572 switch (surface->resource.map_binding)
2574 case WINED3D_LOCATION_SYSMEM:
2575 base_memory = surface->resource.heap_memory;
2576 break;
2578 case WINED3D_LOCATION_USER_MEMORY:
2579 base_memory = surface->user_memory;
2580 break;
2582 case WINED3D_LOCATION_DIB:
2583 base_memory = surface->dib.bitmap_data;
2584 break;
2586 case WINED3D_LOCATION_BUFFER:
2587 context = context_acquire(device, NULL);
2588 gl_info = context->gl_info;
2590 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
2591 base_memory = GL_EXTCALL(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE));
2592 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
2593 checkGLcall("map PBO");
2595 context_release(context);
2596 break;
2598 default:
2599 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2600 base_memory = NULL;
2603 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2604 map_desc->row_pitch = surface->resource.width * format->byte_count;
2605 else
2606 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2607 map_desc->slice_pitch = 0;
2609 if (!rect)
2611 map_desc->data = base_memory;
2612 surface->lockedRect.left = 0;
2613 surface->lockedRect.top = 0;
2614 surface->lockedRect.right = surface->resource.width;
2615 surface->lockedRect.bottom = surface->resource.height;
2617 else
2619 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2621 /* Compressed textures are block based, so calculate the offset of
2622 * the block that contains the top-left pixel of the locked rectangle. */
2623 map_desc->data = base_memory
2624 + ((rect->top / format->block_height) * map_desc->row_pitch)
2625 + ((rect->left / format->block_width) * format->block_byte_count);
2627 else
2629 map_desc->data = base_memory
2630 + (map_desc->row_pitch * rect->top)
2631 + (rect->left * format->byte_count);
2633 surface->lockedRect.left = rect->left;
2634 surface->lockedRect.top = rect->top;
2635 surface->lockedRect.right = rect->right;
2636 surface->lockedRect.bottom = rect->bottom;
2639 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2640 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2642 return WINED3D_OK;
2645 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
2647 HRESULT hr;
2649 TRACE("surface %p, dc %p.\n", surface, dc);
2651 /* Give more detailed info for ddraw. */
2652 if (surface->flags & SFLAG_DCINUSE)
2653 return WINEDDERR_DCALREADYCREATED;
2655 /* Can't GetDC if the surface is locked. */
2656 if (surface->resource.map_count)
2657 return WINED3DERR_INVALIDCALL;
2659 /* Create a DIB section if there isn't a dc yet. */
2660 if (!surface->hDC)
2662 if (surface->flags & SFLAG_CLIENT)
2664 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
2665 surface_release_client_storage(surface);
2667 hr = surface_create_dib_section(surface);
2668 if (FAILED(hr))
2669 return WINED3DERR_INVALIDCALL;
2670 if (!(surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2671 || surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2672 || surface->pbo))
2673 surface->resource.map_binding = WINED3D_LOCATION_DIB;
2676 surface_load_location(surface, WINED3D_LOCATION_DIB);
2677 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
2679 surface->flags |= SFLAG_DCINUSE;
2680 surface->resource.map_count++;
2682 *dc = surface->hDC;
2683 TRACE("Returning dc %p.\n", *dc);
2685 return WINED3D_OK;
2688 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
2690 TRACE("surface %p, dc %p.\n", surface, dc);
2692 if (!(surface->flags & SFLAG_DCINUSE))
2693 return WINEDDERR_NODC;
2695 if (surface->hDC != dc)
2697 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
2698 dc, surface->hDC);
2699 return WINEDDERR_NODC;
2702 surface->resource.map_count--;
2703 surface->flags &= ~SFLAG_DCINUSE;
2705 if (surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2706 || (surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2707 && surface->resource.map_binding != WINED3D_LOCATION_DIB))
2709 /* The game Salammbo modifies the surface contents without mapping the surface between
2710 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
2711 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
2712 * Do not only copy the DIB to the map location, but also make sure the map location is
2713 * copied back to the DIB in the next getdc call.
2715 * The same consideration applies to user memory surfaces. */
2716 surface_load_location(surface, surface->resource.map_binding);
2717 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
2720 return WINED3D_OK;
2723 static void read_from_framebuffer(struct wined3d_surface *surface, DWORD dst_location)
2725 struct wined3d_device *device = surface->resource.device;
2726 const struct wined3d_gl_info *gl_info;
2727 struct wined3d_context *context;
2728 BYTE *mem;
2729 BYTE *row, *top, *bottom;
2730 int i;
2731 BOOL srcIsUpsideDown;
2732 struct wined3d_bo_address data;
2734 surface_get_memory(surface, &data, dst_location);
2736 context = context_acquire(device, surface);
2737 context_apply_blit_state(context, device);
2738 gl_info = context->gl_info;
2740 /* Select the correct read buffer, and give some debug output.
2741 * There is no need to keep track of the current read buffer or reset it, every part of the code
2742 * that reads sets the read buffer as desired.
2744 if (wined3d_resource_is_offscreen(&surface->container->resource))
2746 /* Mapping the primary render target which is not on a swapchain.
2747 * Read from the back buffer. */
2748 TRACE("Mapping offscreen render target.\n");
2749 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2750 srcIsUpsideDown = TRUE;
2752 else
2754 /* Onscreen surfaces are always part of a swapchain */
2755 GLenum buffer = surface_get_gl_buffer(surface);
2756 TRACE("Mapping %#x buffer.\n", buffer);
2757 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2758 checkGLcall("glReadBuffer");
2759 srcIsUpsideDown = FALSE;
2762 if (data.buffer_object)
2764 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
2765 checkGLcall("glBindBuffer");
2768 /* Setup pixel store pack state -- to glReadPixels into the correct place */
2769 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH,
2770 wined3d_surface_get_pitch(surface) / surface->resource.format->byte_count);
2771 checkGLcall("glPixelStorei");
2773 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
2774 surface->resource.width, surface->resource.height,
2775 surface->resource.format->glFormat,
2776 surface->resource.format->glType, data.addr);
2777 checkGLcall("glReadPixels");
2779 /* Reset previous pixel store pack state */
2780 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
2781 checkGLcall("glPixelStorei");
2783 if (!srcIsUpsideDown)
2785 /* glReadPixels returns the image upside down, and there is no way to prevent this.
2786 * Flip the lines in software. */
2787 UINT pitch = wined3d_surface_get_pitch(surface);
2789 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
2790 goto error;
2792 if (data.buffer_object)
2794 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
2795 checkGLcall("glMapBuffer");
2797 else
2798 mem = data.addr;
2800 top = mem;
2801 bottom = mem + pitch * (surface->resource.height - 1);
2802 for (i = 0; i < surface->resource.height / 2; i++)
2804 memcpy(row, top, pitch);
2805 memcpy(top, bottom, pitch);
2806 memcpy(bottom, row, pitch);
2807 top += pitch;
2808 bottom -= pitch;
2810 HeapFree(GetProcessHeap(), 0, row);
2812 if (data.buffer_object)
2813 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
2816 error:
2817 if (data.buffer_object)
2819 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2820 checkGLcall("glBindBuffer");
2823 context_release(context);
2826 /* Read the framebuffer contents into a texture. Note that this function
2827 * doesn't do any kind of flipping. Using this on an onscreen surface will
2828 * result in a flipped D3D texture. */
2829 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
2831 struct wined3d_device *device = surface->resource.device;
2832 const struct wined3d_gl_info *gl_info;
2833 struct wined3d_context *context;
2835 context = context_acquire(device, surface);
2836 gl_info = context->gl_info;
2837 device_invalidate_state(device, STATE_FRAMEBUFFER);
2839 wined3d_texture_prepare_texture(surface->container, context, srgb);
2840 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
2842 TRACE("Reading back offscreen render target %p.\n", surface);
2844 if (wined3d_resource_is_offscreen(&surface->container->resource))
2845 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2846 else
2847 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
2848 checkGLcall("glReadBuffer");
2850 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
2851 0, 0, 0, 0, surface->resource.width, surface->resource.height);
2852 checkGLcall("glCopyTexSubImage2D");
2854 context_release(context);
2857 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2859 if (multisample)
2861 if (surface->rb_multisample)
2862 return;
2864 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2865 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2866 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
2867 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
2868 checkGLcall("glRenderbufferStorageMultisample()");
2869 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2871 else
2873 if (surface->rb_resolved)
2874 return;
2876 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2877 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2878 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
2879 surface->pow2Width, surface->pow2Height);
2880 checkGLcall("glRenderbufferStorage()");
2881 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2885 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
2887 if (front->container->level_count != 1 || front->container->layer_count != 1
2888 || back->container->level_count != 1 || back->container->layer_count != 1)
2889 ERR("Flip between surfaces %p and %p not supported.\n", front, back);
2891 /* Flip the surface contents */
2892 /* Flip the DC */
2894 HDC tmp;
2895 tmp = front->hDC;
2896 front->hDC = back->hDC;
2897 back->hDC = tmp;
2900 /* Flip the DIBsection */
2902 HBITMAP tmp = front->dib.DIBsection;
2903 front->dib.DIBsection = back->dib.DIBsection;
2904 back->dib.DIBsection = tmp;
2907 /* Flip the surface data */
2909 void* tmp;
2911 tmp = front->dib.bitmap_data;
2912 front->dib.bitmap_data = back->dib.bitmap_data;
2913 back->dib.bitmap_data = tmp;
2915 tmp = front->resource.heap_memory;
2916 front->resource.heap_memory = back->resource.heap_memory;
2917 back->resource.heap_memory = tmp;
2920 /* Flip the PBO */
2922 GLuint tmp_pbo = front->pbo;
2923 front->pbo = back->pbo;
2924 back->pbo = tmp_pbo;
2927 /* Flip the opengl texture */
2929 GLuint tmp;
2931 tmp = back->container->texture_rgb.name;
2932 back->container->texture_rgb.name = front->container->texture_rgb.name;
2933 front->container->texture_rgb.name = tmp;
2935 tmp = back->container->texture_srgb.name;
2936 back->container->texture_srgb.name = front->container->texture_srgb.name;
2937 front->container->texture_srgb.name = tmp;
2939 tmp = back->rb_multisample;
2940 back->rb_multisample = front->rb_multisample;
2941 front->rb_multisample = tmp;
2943 tmp = back->rb_resolved;
2944 back->rb_resolved = front->rb_resolved;
2945 front->rb_resolved = tmp;
2947 resource_unload(&back->resource);
2948 resource_unload(&front->resource);
2952 DWORD tmp_flags = back->flags;
2953 back->flags = front->flags;
2954 front->flags = tmp_flags;
2956 tmp_flags = back->locations;
2957 back->locations = front->locations;
2958 front->locations = tmp_flags;
2962 /* Does a direct frame buffer -> texture copy. Stretching is done with single
2963 * pixel copy calls. */
2964 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2965 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2967 struct wined3d_device *device = dst_surface->resource.device;
2968 const struct wined3d_gl_info *gl_info;
2969 float xrel, yrel;
2970 struct wined3d_context *context;
2971 BOOL upsidedown = FALSE;
2972 RECT dst_rect = *dst_rect_in;
2974 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2975 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2977 if(dst_rect.top > dst_rect.bottom) {
2978 UINT tmp = dst_rect.bottom;
2979 dst_rect.bottom = dst_rect.top;
2980 dst_rect.top = tmp;
2981 upsidedown = TRUE;
2984 context = context_acquire(device, src_surface);
2985 gl_info = context->gl_info;
2986 context_apply_blit_state(context, device);
2987 wined3d_texture_load(dst_surface->container, context, FALSE);
2989 /* Bind the target texture */
2990 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
2991 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
2993 TRACE("Reading from an offscreen target\n");
2994 upsidedown = !upsidedown;
2995 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2997 else
2999 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3001 checkGLcall("glReadBuffer");
3003 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3004 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3006 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3008 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3010 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3011 ERR("Texture filtering not supported in direct blit.\n");
3013 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3014 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3016 ERR("Texture filtering not supported in direct blit\n");
3019 if (upsidedown
3020 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3021 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3023 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
3024 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3025 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3026 src_rect->left, src_surface->resource.height - src_rect->bottom,
3027 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3029 else
3031 LONG row;
3032 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
3033 /* I have to process this row by row to swap the image,
3034 * otherwise it would be upside down, so stretching in y direction
3035 * doesn't cost extra time
3037 * However, stretching in x direction can be avoided if not necessary
3039 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3040 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3042 /* Well, that stuff works, but it's very slow.
3043 * find a better way instead
3045 LONG col;
3047 for (col = dst_rect.left; col < dst_rect.right; ++col)
3049 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3050 dst_rect.left + col /* x offset */, row /* y offset */,
3051 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3054 else
3056 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3057 dst_rect.left /* x offset */, row /* y offset */,
3058 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3062 checkGLcall("glCopyTexSubImage2D");
3064 context_release(context);
3066 /* The texture is now most up to date - If the surface is a render target
3067 * and has a drawable, this path is never entered. */
3068 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3069 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3072 /* Uses the hardware to stretch and flip the image */
3073 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3074 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3076 struct wined3d_device *device = dst_surface->resource.device;
3077 GLuint src, backup = 0;
3078 float left, right, top, bottom; /* Texture coordinates */
3079 UINT fbwidth = src_surface->resource.width;
3080 UINT fbheight = src_surface->resource.height;
3081 const struct wined3d_gl_info *gl_info;
3082 struct wined3d_context *context;
3083 GLenum drawBuffer = GL_BACK;
3084 GLenum texture_target;
3085 BOOL noBackBufferBackup;
3086 BOOL src_offscreen;
3087 BOOL upsidedown = FALSE;
3088 RECT dst_rect = *dst_rect_in;
3090 TRACE("Using hwstretch blit\n");
3091 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3092 context = context_acquire(device, src_surface);
3093 gl_info = context->gl_info;
3094 context_apply_blit_state(context, device);
3095 wined3d_texture_load(dst_surface->container, context, FALSE);
3097 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
3098 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3099 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3101 /* Get it a description */
3102 wined3d_texture_load(src_surface->container, context, FALSE);
3105 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3106 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3108 if (context->aux_buffers >= 2)
3110 /* Got more than one aux buffer? Use the 2nd aux buffer */
3111 drawBuffer = GL_AUX1;
3113 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3115 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3116 drawBuffer = GL_AUX0;
3119 if (noBackBufferBackup)
3121 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3122 checkGLcall("glGenTextures");
3123 context_bind_texture(context, GL_TEXTURE_2D, backup);
3124 texture_target = GL_TEXTURE_2D;
3126 else
3128 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3129 * we are reading from the back buffer, the backup can be used as source texture
3131 texture_target = src_surface->texture_target;
3132 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3133 gl_info->gl_ops.gl.p_glEnable(texture_target);
3134 checkGLcall("glEnable(texture_target)");
3136 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3137 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3140 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3141 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3143 if(dst_rect.top > dst_rect.bottom) {
3144 UINT tmp = dst_rect.bottom;
3145 dst_rect.bottom = dst_rect.top;
3146 dst_rect.top = tmp;
3147 upsidedown = TRUE;
3150 if (src_offscreen)
3152 TRACE("Reading from an offscreen target\n");
3153 upsidedown = !upsidedown;
3154 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3156 else
3158 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3161 /* TODO: Only back up the part that will be overwritten */
3162 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3164 checkGLcall("glCopyTexSubImage2D");
3166 /* No issue with overriding these - the sampler is dirty due to blit usage */
3167 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
3168 checkGLcall("glTexParameteri");
3169 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3170 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
3171 checkGLcall("glTexParameteri");
3173 if (!src_surface->container->swapchain
3174 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
3176 src = backup ? backup : src_surface->container->texture_rgb.name;
3178 else
3180 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3181 checkGLcall("glReadBuffer(GL_FRONT)");
3183 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3184 checkGLcall("glGenTextures(1, &src)");
3185 context_bind_texture(context, GL_TEXTURE_2D, src);
3187 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3188 * out for power of 2 sizes
3190 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3191 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3192 checkGLcall("glTexImage2D");
3193 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3195 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3196 checkGLcall("glTexParameteri");
3197 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3198 checkGLcall("glTexParameteri");
3200 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3201 checkGLcall("glReadBuffer(GL_BACK)");
3203 if (texture_target != GL_TEXTURE_2D)
3205 gl_info->gl_ops.gl.p_glDisable(texture_target);
3206 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3207 texture_target = GL_TEXTURE_2D;
3210 checkGLcall("glEnd and previous");
3212 left = src_rect->left;
3213 right = src_rect->right;
3215 if (!upsidedown)
3217 top = src_surface->resource.height - src_rect->top;
3218 bottom = src_surface->resource.height - src_rect->bottom;
3220 else
3222 top = src_surface->resource.height - src_rect->bottom;
3223 bottom = src_surface->resource.height - src_rect->top;
3226 if (src_surface->container->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
3228 left /= src_surface->pow2Width;
3229 right /= src_surface->pow2Width;
3230 top /= src_surface->pow2Height;
3231 bottom /= src_surface->pow2Height;
3234 /* draw the source texture stretched and upside down. The correct surface is bound already */
3235 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3236 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3238 context_set_draw_buffer(context, drawBuffer);
3239 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3241 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3242 /* bottom left */
3243 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3244 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3246 /* top left */
3247 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3248 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3250 /* top right */
3251 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3252 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3254 /* bottom right */
3255 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3256 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3257 gl_info->gl_ops.gl.p_glEnd();
3258 checkGLcall("glEnd and previous");
3260 if (texture_target != dst_surface->texture_target)
3262 gl_info->gl_ops.gl.p_glDisable(texture_target);
3263 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3264 texture_target = dst_surface->texture_target;
3267 /* Now read the stretched and upside down image into the destination texture */
3268 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
3269 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
3271 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3272 0, 0, /* We blitted the image to the origin */
3273 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3274 checkGLcall("glCopyTexSubImage2D");
3276 if (drawBuffer == GL_BACK)
3278 /* Write the back buffer backup back. */
3279 if (backup)
3281 if (texture_target != GL_TEXTURE_2D)
3283 gl_info->gl_ops.gl.p_glDisable(texture_target);
3284 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3285 texture_target = GL_TEXTURE_2D;
3287 context_bind_texture(context, GL_TEXTURE_2D, backup);
3289 else
3291 if (texture_target != src_surface->texture_target)
3293 gl_info->gl_ops.gl.p_glDisable(texture_target);
3294 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3295 texture_target = src_surface->texture_target;
3297 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3300 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3301 /* top left */
3302 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3303 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3305 /* bottom left */
3306 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3307 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3309 /* bottom right */
3310 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3311 (float)fbheight / (float)src_surface->pow2Height);
3312 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3314 /* top right */
3315 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3316 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3317 gl_info->gl_ops.gl.p_glEnd();
3319 gl_info->gl_ops.gl.p_glDisable(texture_target);
3320 checkGLcall("glDisable(texture_target)");
3322 /* Cleanup */
3323 if (src != src_surface->container->texture_rgb.name && src != backup)
3325 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3326 checkGLcall("glDeleteTextures(1, &src)");
3328 if (backup)
3330 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3331 checkGLcall("glDeleteTextures(1, &backup)");
3334 if (wined3d_settings.strict_draw_ordering)
3335 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3337 context_release(context);
3339 /* The texture is now most up to date - If the surface is a render target
3340 * and has a drawable, this path is never entered. */
3341 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3342 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3345 /* Front buffer coordinates are always full screen coordinates, but our GL
3346 * drawable is limited to the window's client area. The sysmem and texture
3347 * copies do have the full screen size. Note that GL has a bottom-left
3348 * origin, while D3D has a top-left origin. */
3349 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3351 UINT drawable_height;
3353 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3355 POINT offset = {0, 0};
3356 RECT windowsize;
3358 ScreenToClient(window, &offset);
3359 OffsetRect(rect, offset.x, offset.y);
3361 GetClientRect(window, &windowsize);
3362 drawable_height = windowsize.bottom - windowsize.top;
3364 else
3366 drawable_height = surface->resource.height;
3369 rect->top = drawable_height - rect->top;
3370 rect->bottom = drawable_height - rect->bottom;
3373 static void surface_blt_to_drawable(const struct wined3d_device *device,
3374 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3375 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3376 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3378 const struct wined3d_gl_info *gl_info;
3379 struct wined3d_context *context;
3380 RECT src_rect, dst_rect;
3382 src_rect = *src_rect_in;
3383 dst_rect = *dst_rect_in;
3385 context = context_acquire(device, dst_surface);
3386 gl_info = context->gl_info;
3388 /* Make sure the surface is up-to-date. This should probably use
3389 * surface_load_location() and worry about the destination surface too,
3390 * unless we're overwriting it completely. */
3391 wined3d_texture_load(src_surface->container, context, FALSE);
3393 /* Activate the destination context, set it up for blitting */
3394 context_apply_blit_state(context, device);
3396 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3397 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3399 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
3401 if (alpha_test)
3403 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3404 checkGLcall("glEnable(GL_ALPHA_TEST)");
3406 /* For P8 surfaces, the alpha component contains the palette index.
3407 * Which means that the colorkey is one of the palette entries. In
3408 * other cases pixels that should be masked away have alpha set to 0. */
3409 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3410 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3411 (float)src_surface->container->async.src_blt_color_key.color_space_low_value / 255.0f);
3412 else
3413 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3414 checkGLcall("glAlphaFunc");
3416 else
3418 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3419 checkGLcall("glDisable(GL_ALPHA_TEST)");
3422 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3424 if (alpha_test)
3426 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3427 checkGLcall("glDisable(GL_ALPHA_TEST)");
3430 /* Leave the opengl state valid for blitting */
3431 device->blitter->unset_shader(context->gl_info);
3433 if (wined3d_settings.strict_draw_ordering
3434 || (dst_surface->container->swapchain
3435 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3436 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3438 context_release(context);
3441 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3443 struct wined3d_device *device = s->resource.device;
3444 const struct blit_shader *blitter;
3446 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, WINED3D_BLIT_OP_COLOR_FILL,
3447 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
3448 if (!blitter)
3450 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3451 return WINED3DERR_INVALIDCALL;
3454 return blitter->color_fill(device, s, rect, color);
3457 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3458 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3459 enum wined3d_texture_filter_type filter)
3461 struct wined3d_device *device = dst_surface->resource.device;
3462 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3463 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3465 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3466 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3467 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3469 /* Get the swapchain. One of the surfaces has to be a primary surface */
3470 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3472 WARN("Destination is in sysmem, rejecting gl blt\n");
3473 return WINED3DERR_INVALIDCALL;
3476 dst_swapchain = dst_surface->container->swapchain;
3478 if (src_surface)
3480 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3482 WARN("Src is in sysmem, rejecting gl blt\n");
3483 return WINED3DERR_INVALIDCALL;
3486 src_swapchain = src_surface->container->swapchain;
3488 else
3490 src_swapchain = NULL;
3493 /* Early sort out of cases where no render target is used */
3494 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
3496 TRACE("No surface is render target, not using hardware blit.\n");
3497 return WINED3DERR_INVALIDCALL;
3500 /* No destination color keying supported */
3501 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
3503 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3504 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3505 return WINED3DERR_INVALIDCALL;
3508 if (dst_swapchain && dst_swapchain == src_swapchain)
3510 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3511 return WINED3DERR_INVALIDCALL;
3514 if (dst_swapchain && src_swapchain)
3516 FIXME("Implement hardware blit between two different swapchains\n");
3517 return WINED3DERR_INVALIDCALL;
3520 if (dst_swapchain)
3522 /* Handled with regular texture -> swapchain blit */
3523 if (src_surface == rt)
3524 TRACE("Blit from active render target to a swapchain\n");
3526 else if (src_swapchain && dst_surface == rt)
3528 FIXME("Implement blit from a swapchain to the active render target\n");
3529 return WINED3DERR_INVALIDCALL;
3532 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
3534 /* Blit from render target to texture */
3535 BOOL stretchx;
3537 /* P8 read back is not implemented */
3538 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
3539 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
3541 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3542 return WINED3DERR_INVALIDCALL;
3545 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3547 TRACE("Color keying not supported by frame buffer to texture blit\n");
3548 return WINED3DERR_INVALIDCALL;
3549 /* Destination color key is checked above */
3552 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
3553 stretchx = TRUE;
3554 else
3555 stretchx = FALSE;
3557 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3558 * flip the image nor scale it.
3560 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3561 * -> If the app wants an image width an unscaled width, copy it line per line
3562 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
3563 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3564 * back buffer. This is slower than reading line per line, thus not used for flipping
3565 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3566 * pixel by pixel. */
3567 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
3568 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
3570 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
3571 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
3573 else
3575 TRACE("Using hardware stretching to flip / stretch the texture.\n");
3576 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
3579 surface_evict_sysmem(dst_surface);
3581 return WINED3D_OK;
3584 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3585 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3586 return WINED3DERR_INVALIDCALL;
3589 /* Context activation is done by the caller. */
3590 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
3591 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
3593 struct wined3d_device *device = surface->resource.device;
3594 const struct wined3d_gl_info *gl_info = context->gl_info;
3595 GLint compare_mode = GL_NONE;
3596 struct blt_info info;
3597 GLint old_binding = 0;
3598 RECT rect;
3600 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
3602 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
3603 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
3604 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3605 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
3606 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
3607 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
3608 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
3609 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
3610 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3611 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
3612 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
3614 SetRect(&rect, 0, h, w, 0);
3615 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
3616 context_active_texture(context, context->gl_info, 0);
3617 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
3618 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
3619 if (gl_info->supported[ARB_SHADOW])
3621 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
3622 if (compare_mode != GL_NONE)
3623 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
3626 device->shader_backend->shader_select_depth_blt(device->shader_priv,
3627 gl_info, info.tex_type, &surface->ds_current_size);
3629 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
3630 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
3631 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
3632 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
3633 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
3634 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
3635 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
3636 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
3637 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
3638 gl_info->gl_ops.gl.p_glEnd();
3640 if (compare_mode != GL_NONE)
3641 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
3642 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
3644 gl_info->gl_ops.gl.p_glPopAttrib();
3646 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
3649 void surface_modify_ds_location(struct wined3d_surface *surface,
3650 DWORD location, UINT w, UINT h)
3652 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
3654 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
3655 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
3656 wined3d_texture_set_dirty(surface->container);
3658 surface->ds_current_size.cx = w;
3659 surface->ds_current_size.cy = h;
3660 surface->locations = location;
3663 /* Context activation is done by the caller. */
3664 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3666 const struct wined3d_gl_info *gl_info = context->gl_info;
3667 struct wined3d_device *device = surface->resource.device;
3668 GLsizei w, h;
3670 TRACE("surface %p, new location %#x.\n", surface, location);
3672 /* TODO: Make this work for modes other than FBO */
3673 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3675 if (!(surface->locations & location))
3677 w = surface->ds_current_size.cx;
3678 h = surface->ds_current_size.cy;
3679 surface->ds_current_size.cx = 0;
3680 surface->ds_current_size.cy = 0;
3682 else
3684 w = surface->resource.width;
3685 h = surface->resource.height;
3688 if (surface->ds_current_size.cx == surface->resource.width
3689 && surface->ds_current_size.cy == surface->resource.height)
3691 TRACE("Location (%#x) is already up to date.\n", location);
3692 return;
3695 if (surface->current_renderbuffer)
3697 FIXME("Not supported with fixed up depth stencil.\n");
3698 return;
3701 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3703 TRACE("Surface was discarded, no need copy data.\n");
3704 switch (location)
3706 case WINED3D_LOCATION_TEXTURE_RGB:
3707 wined3d_texture_prepare_texture(surface->container, context, FALSE);
3708 break;
3709 case WINED3D_LOCATION_RB_MULTISAMPLE:
3710 surface_prepare_rb(surface, gl_info, TRUE);
3711 break;
3712 case WINED3D_LOCATION_DRAWABLE:
3713 /* Nothing to do */
3714 break;
3715 default:
3716 FIXME("Unhandled location %#x\n", location);
3718 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
3719 surface->locations |= location;
3720 surface->ds_current_size.cx = surface->resource.width;
3721 surface->ds_current_size.cy = surface->resource.height;
3722 return;
3725 if (!surface->locations)
3727 FIXME("No up to date depth stencil location.\n");
3728 surface->locations |= location;
3729 surface->ds_current_size.cx = surface->resource.width;
3730 surface->ds_current_size.cy = surface->resource.height;
3731 return;
3734 if (location == WINED3D_LOCATION_TEXTURE_RGB)
3736 GLint old_binding = 0;
3737 GLenum bind_target;
3739 /* The render target is allowed to be smaller than the depth/stencil
3740 * buffer, so the onscreen depth/stencil buffer is potentially smaller
3741 * than the offscreen surface. Don't overwrite the offscreen surface
3742 * with undefined data. */
3743 w = min(w, context->swapchain->desc.backbuffer_width);
3744 h = min(h, context->swapchain->desc.backbuffer_height);
3746 TRACE("Copying onscreen depth buffer to depth texture.\n");
3748 if (!device->depth_blt_texture)
3749 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
3751 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3752 * directly on the FBO texture. That's because we need to flip. */
3753 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3754 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3755 NULL, WINED3D_LOCATION_DRAWABLE);
3756 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
3758 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
3759 bind_target = GL_TEXTURE_RECTANGLE_ARB;
3761 else
3763 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3764 bind_target = GL_TEXTURE_2D;
3766 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
3767 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
3768 * internal format, because the internal format might include stencil
3769 * data. In principle we should copy stencil data as well, but unless
3770 * the driver supports stencil export it's hard to do, and doesn't
3771 * seem to be needed in practice. If the hardware doesn't support
3772 * writing stencil data, the glCopyTexImage2D() call might trigger
3773 * software fallbacks. */
3774 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
3775 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3776 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3777 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3778 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3779 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
3780 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
3781 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
3783 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3784 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
3785 context_set_draw_buffer(context, GL_NONE);
3787 /* Do the actual blit */
3788 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
3789 checkGLcall("depth_blt");
3791 context_invalidate_state(context, STATE_FRAMEBUFFER);
3793 if (wined3d_settings.strict_draw_ordering)
3794 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3796 else if (location == WINED3D_LOCATION_DRAWABLE)
3798 TRACE("Copying depth texture to onscreen depth buffer.\n");
3800 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3801 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3802 NULL, WINED3D_LOCATION_DRAWABLE);
3803 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
3804 0, surface->pow2Height - h, w, h, surface->texture_target);
3805 checkGLcall("depth_blt");
3807 context_invalidate_state(context, STATE_FRAMEBUFFER);
3809 if (wined3d_settings.strict_draw_ordering)
3810 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3812 else
3814 ERR("Invalid location (%#x) specified.\n", location);
3817 surface->locations |= location;
3818 surface->ds_current_size.cx = surface->resource.width;
3819 surface->ds_current_size.cy = surface->resource.height;
3822 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
3824 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3826 surface->locations |= location;
3829 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
3831 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3833 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3834 wined3d_texture_set_dirty(surface->container);
3835 surface->locations &= ~location;
3837 if (!surface->locations)
3838 ERR("Surface %p does not have any up to date location.\n", surface);
3841 static DWORD resource_access_from_location(DWORD location)
3843 switch (location)
3845 case WINED3D_LOCATION_SYSMEM:
3846 case WINED3D_LOCATION_USER_MEMORY:
3847 case WINED3D_LOCATION_DIB:
3848 case WINED3D_LOCATION_BUFFER:
3849 return WINED3D_RESOURCE_ACCESS_CPU;
3851 case WINED3D_LOCATION_DRAWABLE:
3852 case WINED3D_LOCATION_TEXTURE_SRGB:
3853 case WINED3D_LOCATION_TEXTURE_RGB:
3854 case WINED3D_LOCATION_RB_MULTISAMPLE:
3855 case WINED3D_LOCATION_RB_RESOLVED:
3856 return WINED3D_RESOURCE_ACCESS_GPU;
3858 default:
3859 FIXME("Unhandled location %#x.\n", location);
3860 return 0;
3864 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
3866 struct wined3d_device *device = surface->resource.device;
3867 struct wined3d_context *context;
3868 const struct wined3d_gl_info *gl_info;
3869 struct wined3d_bo_address dst, src;
3870 UINT size = surface->resource.size;
3872 surface_get_memory(surface, &dst, location);
3873 surface_get_memory(surface, &src, surface->locations);
3875 if (dst.buffer_object)
3877 context = context_acquire(device, NULL);
3878 gl_info = context->gl_info;
3879 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
3880 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr));
3881 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
3882 checkGLcall("Upload PBO");
3883 context_release(context);
3884 return;
3886 if (src.buffer_object)
3888 context = context_acquire(device, NULL);
3889 gl_info = context->gl_info;
3890 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
3891 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr));
3892 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
3893 checkGLcall("Download PBO");
3894 context_release(context);
3895 return;
3897 memcpy(dst.addr, src.addr, size);
3900 static void surface_load_sysmem(struct wined3d_surface *surface,
3901 const struct wined3d_gl_info *gl_info, DWORD dst_location)
3903 if (surface->locations & surface_simple_locations)
3905 surface_copy_simple_location(surface, dst_location);
3906 return;
3909 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
3910 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
3912 /* Download the surface to system memory. */
3913 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3915 struct wined3d_device *device = surface->resource.device;
3916 struct wined3d_context *context;
3918 /* TODO: Use already acquired context when possible. */
3919 context = context_acquire(device, NULL);
3921 wined3d_texture_bind_and_dirtify(surface->container, context,
3922 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
3923 surface_download_data(surface, gl_info, dst_location);
3925 context_release(context);
3927 return;
3930 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
3932 read_from_framebuffer(surface, dst_location);
3933 return;
3936 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
3937 surface, wined3d_debug_location(surface->locations));
3940 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
3941 const struct wined3d_gl_info *gl_info)
3943 RECT r;
3945 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3946 && wined3d_resource_is_offscreen(&surface->container->resource))
3948 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
3949 return WINED3DERR_INVALIDCALL;
3952 surface_get_rect(surface, NULL, &r);
3953 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
3954 surface_blt_to_drawable(surface->resource.device,
3955 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
3957 return WINED3D_OK;
3960 static HRESULT surface_load_texture(struct wined3d_surface *surface,
3961 const struct wined3d_gl_info *gl_info, BOOL srgb)
3963 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
3964 struct wined3d_device *device = surface->resource.device;
3965 const struct wined3d_color_key_conversion *conversion;
3966 struct wined3d_texture *texture = surface->container;
3967 struct wined3d_context *context;
3968 UINT width, src_pitch, dst_pitch;
3969 struct wined3d_bo_address data;
3970 struct wined3d_format format;
3971 POINT dst_point = {0, 0};
3972 BYTE *mem = NULL;
3974 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
3975 && wined3d_resource_is_offscreen(&texture->resource)
3976 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
3978 surface_load_fb_texture(surface, srgb);
3980 return WINED3D_OK;
3983 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
3984 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
3985 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3986 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3987 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3989 if (srgb)
3990 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
3991 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
3992 else
3993 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
3994 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
3996 return WINED3D_OK;
3999 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
4000 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
4001 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4002 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4003 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4005 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
4006 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
4007 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
4008 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4010 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
4011 &rect, surface, dst_location, &rect);
4013 return WINED3D_OK;
4016 /* Upload from system memory */
4018 if (srgb)
4020 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
4021 == WINED3D_LOCATION_TEXTURE_RGB)
4023 /* Performance warning... */
4024 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
4025 surface_prepare_map_memory(surface);
4026 surface_load_location(surface, surface->resource.map_binding);
4029 else
4031 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
4032 == WINED3D_LOCATION_TEXTURE_SRGB)
4034 /* Performance warning... */
4035 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
4036 surface_prepare_map_memory(surface);
4037 surface_load_location(surface, surface->resource.map_binding);
4041 if (!(surface->locations & surface_simple_locations))
4043 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
4044 /* Lets hope we get it from somewhere... */
4045 surface_prepare_system_memory(surface);
4046 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
4049 /* TODO: Use already acquired context when possible. */
4050 context = context_acquire(device, NULL);
4052 wined3d_texture_prepare_texture(texture, context, srgb);
4053 wined3d_texture_bind_and_dirtify(texture, context, srgb);
4055 width = surface->resource.width;
4056 src_pitch = wined3d_surface_get_pitch(surface);
4058 format = *texture->resource.format;
4059 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
4060 format = *wined3d_get_format(gl_info, conversion->dst_format);
4062 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
4063 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
4064 * getting called. */
4065 if ((format.convert || conversion) && surface->pbo)
4067 TRACE("Removing the pbo attached to surface %p.\n", surface);
4069 if (surface->flags & SFLAG_DIBSECTION)
4070 surface->resource.map_binding = WINED3D_LOCATION_DIB;
4071 else
4072 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
4074 surface_prepare_map_memory(surface);
4075 surface_load_location(surface, surface->resource.map_binding);
4076 surface_remove_pbo(surface, gl_info);
4079 surface_get_memory(surface, &data, surface->locations);
4080 if (format.convert)
4082 /* This code is entered for texture formats which need a fixup. */
4083 UINT height = surface->resource.height;
4085 format.byte_count = format.conv_byte_count;
4086 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4088 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4090 ERR("Out of memory (%u).\n", dst_pitch * height);
4091 context_release(context);
4092 return E_OUTOFMEMORY;
4094 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4095 dst_pitch, dst_pitch * height, width, height, 1);
4096 src_pitch = dst_pitch;
4097 data.addr = mem;
4099 else if (conversion)
4101 /* This code is only entered for color keying fixups */
4102 struct wined3d_palette *palette = NULL;
4103 UINT height = surface->resource.height;
4105 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4106 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4108 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4110 ERR("Out of memory (%u).\n", dst_pitch * height);
4111 context_release(context);
4112 return E_OUTOFMEMORY;
4114 if (texture->swapchain && texture->swapchain->palette)
4115 palette = texture->swapchain->palette;
4116 conversion->convert(data.addr, src_pitch, mem, dst_pitch,
4117 width, height, palette, &texture->async.gl_color_key);
4118 src_pitch = dst_pitch;
4119 data.addr = mem;
4122 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
4123 src_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
4125 context_release(context);
4127 HeapFree(GetProcessHeap(), 0, mem);
4129 return WINED3D_OK;
4132 static void surface_multisample_resolve(struct wined3d_surface *surface)
4134 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4136 if (!(surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE))
4137 ERR("Trying to resolve multisampled surface %p, but location WINED3D_LOCATION_RB_MULTISAMPLE not current.\n",
4138 surface);
4140 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
4141 surface, WINED3D_LOCATION_RB_MULTISAMPLE, &rect, surface, WINED3D_LOCATION_RB_RESOLVED, &rect);
4144 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
4146 struct wined3d_device *device = surface->resource.device;
4147 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4148 HRESULT hr;
4150 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4152 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4154 if (location == WINED3D_LOCATION_TEXTURE_RGB
4155 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4157 struct wined3d_context *context = context_acquire(device, NULL);
4158 surface_load_ds_location(surface, context, location);
4159 context_release(context);
4160 return WINED3D_OK;
4162 else if (location & surface->locations
4163 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
4165 /* Already up to date, nothing to do. */
4166 return WINED3D_OK;
4168 else
4170 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4171 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4172 return WINED3DERR_INVALIDCALL;
4176 if (surface->locations & location)
4178 TRACE("Location already up to date.\n");
4179 return WINED3D_OK;
4182 if (WARN_ON(d3d_surface))
4184 DWORD required_access = resource_access_from_location(location);
4185 if ((surface->resource.access_flags & required_access) != required_access)
4186 WARN("Operation requires %#x access, but surface only has %#x.\n",
4187 required_access, surface->resource.access_flags);
4190 if (!surface->locations)
4192 ERR("Surface %p does not have any up to date location.\n", surface);
4193 surface->flags |= SFLAG_LOST;
4194 return WINED3DERR_DEVICELOST;
4197 switch (location)
4199 case WINED3D_LOCATION_DIB:
4200 case WINED3D_LOCATION_USER_MEMORY:
4201 case WINED3D_LOCATION_SYSMEM:
4202 case WINED3D_LOCATION_BUFFER:
4203 surface_load_sysmem(surface, gl_info, location);
4204 break;
4206 case WINED3D_LOCATION_DRAWABLE:
4207 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
4208 return hr;
4209 break;
4211 case WINED3D_LOCATION_RB_RESOLVED:
4212 surface_multisample_resolve(surface);
4213 break;
4215 case WINED3D_LOCATION_TEXTURE_RGB:
4216 case WINED3D_LOCATION_TEXTURE_SRGB:
4217 if (FAILED(hr = surface_load_texture(surface, gl_info, location == WINED3D_LOCATION_TEXTURE_SRGB)))
4218 return hr;
4219 break;
4221 default:
4222 ERR("Don't know how to handle location %#x.\n", location);
4223 break;
4226 surface_validate_location(surface, location);
4228 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
4229 surface_evict_sysmem(surface);
4231 return WINED3D_OK;
4234 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
4235 /* Context activation is done by the caller. */
4236 static void ffp_blit_free(struct wined3d_device *device) { }
4238 /* Context activation is done by the caller. */
4239 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4240 const struct wined3d_color_key *color_key)
4242 const struct wined3d_gl_info *gl_info = context->gl_info;
4244 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
4245 checkGLcall("glEnable(target)");
4247 return WINED3D_OK;
4250 /* Context activation is done by the caller. */
4251 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
4253 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
4254 checkGLcall("glDisable(GL_TEXTURE_2D)");
4255 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4257 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4258 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4260 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4262 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
4263 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4267 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
4268 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4269 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4270 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4272 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4274 TRACE("Source or destination is in system memory.\n");
4275 return FALSE;
4278 switch (blit_op)
4280 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
4281 if (d3d_info->shader_color_key)
4283 TRACE("Color keying requires converted textures.\n");
4284 return FALSE;
4286 case WINED3D_BLIT_OP_COLOR_BLIT:
4287 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4288 return FALSE;
4290 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4292 TRACE("Checking support for fixup:\n");
4293 dump_color_fixup_desc(src_format->color_fixup);
4296 /* We only support identity conversions. */
4297 if (!is_identity_fixup(src_format->color_fixup)
4298 || !is_identity_fixup(dst_format->color_fixup))
4300 TRACE("Fixups are not supported.\n");
4301 return FALSE;
4304 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4306 TRACE("Can only blit to render targets.\n");
4307 return FALSE;
4309 return TRUE;
4311 case WINED3D_BLIT_OP_COLOR_FILL:
4312 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4314 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4315 return FALSE;
4317 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4319 TRACE("Color fill not supported\n");
4320 return FALSE;
4323 /* FIXME: We should reject color fills on formats with fixups,
4324 * but this would break P8 color fills for example. */
4326 return TRUE;
4328 case WINED3D_BLIT_OP_DEPTH_FILL:
4329 return TRUE;
4331 default:
4332 TRACE("Unsupported blit_op=%d\n", blit_op);
4333 return FALSE;
4337 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4338 const RECT *dst_rect, const struct wined3d_color *color)
4340 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4341 struct wined3d_rendertarget_view *view;
4342 struct wined3d_fb_state fb = {&view, NULL};
4343 HRESULT hr;
4345 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4346 NULL, &wined3d_null_parent_ops, &view)))
4348 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4349 return hr;
4352 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4353 wined3d_rendertarget_view_decref(view);
4355 return WINED3D_OK;
4358 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4359 const RECT *dst_rect, float depth)
4361 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4362 struct wined3d_fb_state fb = {NULL, NULL};
4363 HRESULT hr;
4365 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4366 NULL, &wined3d_null_parent_ops, &fb.depth_stencil)))
4368 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4369 return hr;
4372 device_clear_render_targets(device, 0, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4373 wined3d_rendertarget_view_decref(fb.depth_stencil);
4375 return WINED3D_OK;
4378 static void ffp_blit_blit_surface(struct wined3d_device *device, DWORD filter,
4379 struct wined3d_surface *src_surface, const RECT *src_rect,
4380 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4381 const struct wined3d_color_key *color_key)
4383 /* Blit from offscreen surface to render target */
4384 struct wined3d_color_key old_blt_key = src_surface->container->async.src_blt_color_key;
4385 DWORD old_color_key_flags = src_surface->container->async.color_key_flags;
4387 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4389 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT, color_key);
4391 surface_blt_to_drawable(device, filter,
4392 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
4394 /* Restore the color key parameters */
4395 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT,
4396 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
4398 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
4399 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
4402 const struct blit_shader ffp_blit = {
4403 ffp_blit_alloc,
4404 ffp_blit_free,
4405 ffp_blit_set,
4406 ffp_blit_unset,
4407 ffp_blit_supported,
4408 ffp_blit_color_fill,
4409 ffp_blit_depth_fill,
4410 ffp_blit_blit_surface,
4413 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4415 return WINED3D_OK;
4418 /* Context activation is done by the caller. */
4419 static void cpu_blit_free(struct wined3d_device *device)
4423 /* Context activation is done by the caller. */
4424 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4425 const struct wined3d_color_key *color_key)
4427 return WINED3D_OK;
4430 /* Context activation is done by the caller. */
4431 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4435 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
4436 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4437 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4438 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4440 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4442 return TRUE;
4445 return FALSE;
4448 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4449 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4450 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4452 UINT row_block_count;
4453 const BYTE *src_row;
4454 BYTE *dst_row;
4455 UINT x, y;
4457 src_row = src_data;
4458 dst_row = dst_data;
4460 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4462 if (!flags)
4464 for (y = 0; y < update_h; y += format->block_height)
4466 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4467 src_row += src_pitch;
4468 dst_row += dst_pitch;
4471 return WINED3D_OK;
4474 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4476 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4478 switch (format->id)
4480 case WINED3DFMT_DXT1:
4481 for (y = 0; y < update_h; y += format->block_height)
4483 struct block
4485 WORD color[2];
4486 BYTE control_row[4];
4489 const struct block *s = (const struct block *)src_row;
4490 struct block *d = (struct block *)dst_row;
4492 for (x = 0; x < row_block_count; ++x)
4494 d[x].color[0] = s[x].color[0];
4495 d[x].color[1] = s[x].color[1];
4496 d[x].control_row[0] = s[x].control_row[3];
4497 d[x].control_row[1] = s[x].control_row[2];
4498 d[x].control_row[2] = s[x].control_row[1];
4499 d[x].control_row[3] = s[x].control_row[0];
4501 src_row -= src_pitch;
4502 dst_row += dst_pitch;
4504 return WINED3D_OK;
4506 case WINED3DFMT_DXT2:
4507 case WINED3DFMT_DXT3:
4508 for (y = 0; y < update_h; y += format->block_height)
4510 struct block
4512 WORD alpha_row[4];
4513 WORD color[2];
4514 BYTE control_row[4];
4517 const struct block *s = (const struct block *)src_row;
4518 struct block *d = (struct block *)dst_row;
4520 for (x = 0; x < row_block_count; ++x)
4522 d[x].alpha_row[0] = s[x].alpha_row[3];
4523 d[x].alpha_row[1] = s[x].alpha_row[2];
4524 d[x].alpha_row[2] = s[x].alpha_row[1];
4525 d[x].alpha_row[3] = s[x].alpha_row[0];
4526 d[x].color[0] = s[x].color[0];
4527 d[x].color[1] = s[x].color[1];
4528 d[x].control_row[0] = s[x].control_row[3];
4529 d[x].control_row[1] = s[x].control_row[2];
4530 d[x].control_row[2] = s[x].control_row[1];
4531 d[x].control_row[3] = s[x].control_row[0];
4533 src_row -= src_pitch;
4534 dst_row += dst_pitch;
4536 return WINED3D_OK;
4538 default:
4539 FIXME("Compressed flip not implemented for format %s.\n",
4540 debug_d3dformat(format->id));
4541 return E_NOTIMPL;
4545 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
4546 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
4548 return E_NOTIMPL;
4551 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4552 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4553 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4555 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
4556 const struct wined3d_format *src_format, *dst_format;
4557 struct wined3d_texture *src_texture = NULL;
4558 struct wined3d_map_desc dst_map, src_map;
4559 const BYTE *sbase = NULL;
4560 HRESULT hr = WINED3D_OK;
4561 const BYTE *sbuf;
4562 BYTE *dbuf;
4563 int x, y;
4565 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4566 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4567 flags, fx, debug_d3dtexturefiltertype(filter));
4569 if (src_surface == dst_surface)
4571 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
4572 src_map = dst_map;
4573 src_format = dst_surface->resource.format;
4574 dst_format = src_format;
4576 else
4578 dst_format = dst_surface->resource.format;
4579 if (src_surface)
4581 if (dst_surface->resource.format->id != src_surface->resource.format->id)
4583 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
4585 /* The conv function writes a FIXME */
4586 WARN("Cannot convert source surface format to dest format.\n");
4587 goto release;
4589 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
4591 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
4592 src_format = src_surface->resource.format;
4594 else
4596 src_format = dst_format;
4599 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
4602 bpp = dst_surface->resource.format->byte_count;
4603 srcheight = src_rect->bottom - src_rect->top;
4604 srcwidth = src_rect->right - src_rect->left;
4605 dstheight = dst_rect->bottom - dst_rect->top;
4606 dstwidth = dst_rect->right - dst_rect->left;
4607 width = (dst_rect->right - dst_rect->left) * bpp;
4609 if (src_surface)
4610 sbase = (BYTE *)src_map.data
4611 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
4612 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
4613 if (src_surface != dst_surface)
4614 dbuf = dst_map.data;
4615 else
4616 dbuf = (BYTE *)dst_map.data
4617 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
4618 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
4620 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
4622 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
4624 if (src_surface == dst_surface)
4626 FIXME("Only plain blits supported on compressed surfaces.\n");
4627 hr = E_NOTIMPL;
4628 goto release;
4631 if (srcheight != dstheight || srcwidth != dstwidth)
4633 WARN("Stretching not supported on compressed surfaces.\n");
4634 hr = WINED3DERR_INVALIDCALL;
4635 goto release;
4638 if (!surface_check_block_align(src_surface, src_rect))
4640 WARN("Source rectangle not block-aligned.\n");
4641 hr = WINED3DERR_INVALIDCALL;
4642 goto release;
4645 if (!surface_check_block_align(dst_surface, dst_rect))
4647 WARN("Destination rectangle not block-aligned.\n");
4648 hr = WINED3DERR_INVALIDCALL;
4649 goto release;
4652 hr = surface_cpu_blt_compressed(sbase, dbuf,
4653 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
4654 src_format, flags, fx);
4655 goto release;
4658 /* First, all the 'source-less' blits */
4659 if (flags & WINEDDBLT_COLORFILL)
4661 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
4662 flags &= ~WINEDDBLT_COLORFILL;
4665 if (flags & WINEDDBLT_DEPTHFILL)
4667 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
4669 if (flags & WINEDDBLT_DDROPS)
4671 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
4673 /* Now the 'with source' blits. */
4674 if (src_surface)
4676 int sx, xinc, sy, yinc;
4678 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
4679 goto release;
4681 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
4682 && (srcwidth != dstwidth || srcheight != dstheight))
4684 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
4685 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
4688 xinc = (srcwidth << 16) / dstwidth;
4689 yinc = (srcheight << 16) / dstheight;
4691 if (!flags)
4693 /* No effects, we can cheat here. */
4694 if (dstwidth == srcwidth)
4696 if (dstheight == srcheight)
4698 /* No stretching in either direction. This needs to be as
4699 * fast as possible. */
4700 sbuf = sbase;
4702 /* Check for overlapping surfaces. */
4703 if (src_surface != dst_surface || dst_rect->top < src_rect->top
4704 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
4706 /* No overlap, or dst above src, so copy from top downwards. */
4707 for (y = 0; y < dstheight; ++y)
4709 memcpy(dbuf, sbuf, width);
4710 sbuf += src_map.row_pitch;
4711 dbuf += dst_map.row_pitch;
4714 else if (dst_rect->top > src_rect->top)
4716 /* Copy from bottom upwards. */
4717 sbuf += src_map.row_pitch * dstheight;
4718 dbuf += dst_map.row_pitch * dstheight;
4719 for (y = 0; y < dstheight; ++y)
4721 sbuf -= src_map.row_pitch;
4722 dbuf -= dst_map.row_pitch;
4723 memcpy(dbuf, sbuf, width);
4726 else
4728 /* Src and dst overlapping on the same line, use memmove. */
4729 for (y = 0; y < dstheight; ++y)
4731 memmove(dbuf, sbuf, width);
4732 sbuf += src_map.row_pitch;
4733 dbuf += dst_map.row_pitch;
4737 else
4739 /* Stretching in y direction only. */
4740 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4742 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4743 memcpy(dbuf, sbuf, width);
4744 dbuf += dst_map.row_pitch;
4748 else
4750 /* Stretching in X direction. */
4751 int last_sy = -1;
4752 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4754 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4756 if ((sy >> 16) == (last_sy >> 16))
4758 /* This source row is the same as last source row -
4759 * Copy the already stretched row. */
4760 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
4762 else
4764 #define STRETCH_ROW(type) \
4765 do { \
4766 const type *s = (const type *)sbuf; \
4767 type *d = (type *)dbuf; \
4768 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4769 d[x] = s[sx >> 16]; \
4770 } while(0)
4772 switch(bpp)
4774 case 1:
4775 STRETCH_ROW(BYTE);
4776 break;
4777 case 2:
4778 STRETCH_ROW(WORD);
4779 break;
4780 case 4:
4781 STRETCH_ROW(DWORD);
4782 break;
4783 case 3:
4785 const BYTE *s;
4786 BYTE *d = dbuf;
4787 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
4789 DWORD pixel;
4791 s = sbuf + 3 * (sx >> 16);
4792 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4793 d[0] = (pixel ) & 0xff;
4794 d[1] = (pixel >> 8) & 0xff;
4795 d[2] = (pixel >> 16) & 0xff;
4796 d += 3;
4798 break;
4800 default:
4801 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
4802 hr = WINED3DERR_NOTAVAILABLE;
4803 goto error;
4805 #undef STRETCH_ROW
4807 dbuf += dst_map.row_pitch;
4808 last_sy = sy;
4812 else
4814 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
4815 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
4816 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
4817 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
4819 /* The color keying flags are checked for correctness in ddraw */
4820 if (flags & WINEDDBLT_KEYSRC)
4822 keylow = src_surface->container->async.src_blt_color_key.color_space_low_value;
4823 keyhigh = src_surface->container->async.src_blt_color_key.color_space_high_value;
4825 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4827 keylow = fx->ddckSrcColorkey.color_space_low_value;
4828 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
4831 if (flags & WINEDDBLT_KEYDEST)
4833 /* Destination color keys are taken from the source surface! */
4834 destkeylow = src_surface->container->async.dst_blt_color_key.color_space_low_value;
4835 destkeyhigh = src_surface->container->async.dst_blt_color_key.color_space_high_value;
4837 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
4839 destkeylow = fx->ddckDestColorkey.color_space_low_value;
4840 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
4843 if (bpp == 1)
4845 keymask = 0xff;
4847 else
4849 DWORD masks[3];
4850 get_color_masks(src_format, masks);
4851 keymask = masks[0]
4852 | masks[1]
4853 | masks[2];
4855 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
4858 if (flags & WINEDDBLT_DDFX)
4860 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
4861 LONG tmpxy;
4862 dTopLeft = dbuf;
4863 dTopRight = dbuf + ((dstwidth - 1) * bpp);
4864 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
4865 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
4867 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
4869 /* I don't think we need to do anything about this flag */
4870 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
4872 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
4874 tmp = dTopRight;
4875 dTopRight = dTopLeft;
4876 dTopLeft = tmp;
4877 tmp = dBottomRight;
4878 dBottomRight = dBottomLeft;
4879 dBottomLeft = tmp;
4880 dstxinc = dstxinc * -1;
4882 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
4884 tmp = dTopLeft;
4885 dTopLeft = dBottomLeft;
4886 dBottomLeft = tmp;
4887 tmp = dTopRight;
4888 dTopRight = dBottomRight;
4889 dBottomRight = tmp;
4890 dstyinc = dstyinc * -1;
4892 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
4894 /* I don't think we need to do anything about this flag */
4895 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
4897 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
4899 tmp = dBottomRight;
4900 dBottomRight = dTopLeft;
4901 dTopLeft = tmp;
4902 tmp = dBottomLeft;
4903 dBottomLeft = dTopRight;
4904 dTopRight = tmp;
4905 dstxinc = dstxinc * -1;
4906 dstyinc = dstyinc * -1;
4908 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
4910 tmp = dTopLeft;
4911 dTopLeft = dBottomLeft;
4912 dBottomLeft = dBottomRight;
4913 dBottomRight = dTopRight;
4914 dTopRight = tmp;
4915 tmpxy = dstxinc;
4916 dstxinc = dstyinc;
4917 dstyinc = tmpxy;
4918 dstxinc = dstxinc * -1;
4920 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
4922 tmp = dTopLeft;
4923 dTopLeft = dTopRight;
4924 dTopRight = dBottomRight;
4925 dBottomRight = dBottomLeft;
4926 dBottomLeft = tmp;
4927 tmpxy = dstxinc;
4928 dstxinc = dstyinc;
4929 dstyinc = tmpxy;
4930 dstyinc = dstyinc * -1;
4932 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
4934 /* I don't think we need to do anything about this flag */
4935 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
4937 dbuf = dTopLeft;
4938 flags &= ~(WINEDDBLT_DDFX);
4941 #define COPY_COLORKEY_FX(type) \
4942 do { \
4943 const type *s; \
4944 type *d = (type *)dbuf, *dx, tmp; \
4945 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
4947 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
4948 dx = d; \
4949 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4951 tmp = s[sx >> 16]; \
4952 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
4953 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
4955 dx[0] = tmp; \
4957 dx = (type *)(((BYTE *)dx) + dstxinc); \
4959 d = (type *)(((BYTE *)d) + dstyinc); \
4961 } while(0)
4963 switch (bpp)
4965 case 1:
4966 COPY_COLORKEY_FX(BYTE);
4967 break;
4968 case 2:
4969 COPY_COLORKEY_FX(WORD);
4970 break;
4971 case 4:
4972 COPY_COLORKEY_FX(DWORD);
4973 break;
4974 case 3:
4976 const BYTE *s;
4977 BYTE *d = dbuf, *dx;
4978 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4980 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4981 dx = d;
4982 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
4984 DWORD pixel, dpixel = 0;
4985 s = sbuf + 3 * (sx>>16);
4986 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4987 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
4988 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
4989 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
4991 dx[0] = (pixel ) & 0xff;
4992 dx[1] = (pixel >> 8) & 0xff;
4993 dx[2] = (pixel >> 16) & 0xff;
4995 dx += dstxinc;
4997 d += dstyinc;
4999 break;
5001 default:
5002 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
5003 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
5004 hr = WINED3DERR_NOTAVAILABLE;
5005 goto error;
5006 #undef COPY_COLORKEY_FX
5011 error:
5012 if (flags && FIXME_ON(d3d_surface))
5014 FIXME("\tUnsupported flags: %#x.\n", flags);
5017 release:
5018 wined3d_surface_unmap(dst_surface);
5019 if (src_surface && src_surface != dst_surface)
5020 wined3d_surface_unmap(src_surface);
5021 /* Release the converted surface, if any. */
5022 if (src_texture)
5023 wined3d_texture_decref(src_texture);
5025 return hr;
5028 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5029 const RECT *dst_rect, const struct wined3d_color *color)
5031 static const RECT src_rect;
5032 WINEDDBLTFX BltFx;
5034 memset(&BltFx, 0, sizeof(BltFx));
5035 BltFx.dwSize = sizeof(BltFx);
5036 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
5037 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
5038 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
5041 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
5042 struct wined3d_surface *surface, const RECT *rect, float depth)
5044 FIXME("Depth filling not implemented by cpu_blit.\n");
5045 return WINED3DERR_INVALIDCALL;
5048 static void cpu_blit_blit_surface(struct wined3d_device *device, DWORD filter,
5049 struct wined3d_surface *src_surface, const RECT *src_rect,
5050 struct wined3d_surface *dst_surface, const RECT *dst_rect,
5051 const struct wined3d_color_key *color_key)
5053 /* FIXME: Remove error returns from surface_blt_cpu. */
5054 ERR("Blit method not implemented by cpu_blit.\n");
5057 const struct blit_shader cpu_blit = {
5058 cpu_blit_alloc,
5059 cpu_blit_free,
5060 cpu_blit_set,
5061 cpu_blit_unset,
5062 cpu_blit_supported,
5063 cpu_blit_color_fill,
5064 cpu_blit_depth_fill,
5065 cpu_blit_blit_surface,
5068 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5069 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5070 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5072 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5073 struct wined3d_device *device = dst_surface->resource.device;
5074 DWORD src_ds_flags, dst_ds_flags;
5075 RECT src_rect, dst_rect;
5076 BOOL scale, convert;
5078 static const DWORD simple_blit = WINEDDBLT_ASYNC
5079 | WINEDDBLT_COLORFILL
5080 | WINEDDBLT_KEYSRC
5081 | WINEDDBLT_KEYSRCOVERRIDE
5082 | WINEDDBLT_WAIT
5083 | WINEDDBLT_DEPTHFILL
5084 | WINEDDBLT_DONOTWAIT;
5086 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5087 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
5088 flags, fx, debug_d3dtexturefiltertype(filter));
5089 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5091 if (fx)
5093 TRACE("dwSize %#x.\n", fx->dwSize);
5094 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5095 TRACE("dwROP %#x.\n", fx->dwROP);
5096 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5097 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5098 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5099 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5100 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5101 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5102 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5103 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5104 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5105 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5106 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5107 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5108 TRACE("dwReserved %#x.\n", fx->dwReserved);
5109 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5110 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5111 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5112 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5113 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5114 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5115 fx->ddckDestColorkey.color_space_low_value,
5116 fx->ddckDestColorkey.color_space_high_value);
5117 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5118 fx->ddckSrcColorkey.color_space_low_value,
5119 fx->ddckSrcColorkey.color_space_high_value);
5122 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5124 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5125 return WINEDDERR_SURFACEBUSY;
5128 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5130 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5131 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5132 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5133 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5134 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5136 WARN("The application gave us a bad destination rectangle.\n");
5137 return WINEDDERR_INVALIDRECT;
5140 if (src_surface)
5142 surface_get_rect(src_surface, src_rect_in, &src_rect);
5144 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5145 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5146 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5147 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5148 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5150 WARN("Application gave us bad source rectangle for Blt.\n");
5151 return WINEDDERR_INVALIDRECT;
5154 else
5156 memset(&src_rect, 0, sizeof(src_rect));
5159 if (!fx || !(fx->dwDDFX))
5160 flags &= ~WINEDDBLT_DDFX;
5162 if (flags & WINEDDBLT_WAIT)
5163 flags &= ~WINEDDBLT_WAIT;
5165 if (flags & WINEDDBLT_ASYNC)
5167 static unsigned int once;
5169 if (!once++)
5170 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5171 flags &= ~WINEDDBLT_ASYNC;
5174 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5175 if (flags & WINEDDBLT_DONOTWAIT)
5177 static unsigned int once;
5179 if (!once++)
5180 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5181 flags &= ~WINEDDBLT_DONOTWAIT;
5184 if (!device->d3d_initialized)
5186 WARN("D3D not initialized, using fallback.\n");
5187 goto cpu;
5190 /* We want to avoid invalidating the sysmem location for converted
5191 * surfaces, since otherwise we'd have to convert the data back when
5192 * locking them. */
5193 if (dst_surface->container->flags & WINED3D_TEXTURE_CONVERTED
5194 || dst_surface->container->resource.format->convert
5195 || wined3d_format_get_color_key_conversion(dst_surface->container, TRUE))
5197 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5198 goto cpu;
5201 if (flags & ~simple_blit)
5203 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5204 goto fallback;
5207 if (src_surface)
5208 src_swapchain = src_surface->container->swapchain;
5209 else
5210 src_swapchain = NULL;
5212 dst_swapchain = dst_surface->container->swapchain;
5214 /* This isn't strictly needed. FBO blits for example could deal with
5215 * cross-swapchain blits by first downloading the source to a texture
5216 * before switching to the destination context. We just have this here to
5217 * not have to deal with the issue, since cross-swapchain blits should be
5218 * rare. */
5219 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5221 FIXME("Using fallback for cross-swapchain blit.\n");
5222 goto fallback;
5225 scale = src_surface
5226 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5227 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5228 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5230 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5231 if (src_surface)
5232 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5233 else
5234 src_ds_flags = 0;
5236 if (src_ds_flags || dst_ds_flags)
5238 if (flags & WINEDDBLT_DEPTHFILL)
5240 float depth;
5242 TRACE("Depth fill.\n");
5244 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5245 return WINED3DERR_INVALIDCALL;
5247 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
5248 return WINED3D_OK;
5250 else
5252 if (src_ds_flags != dst_ds_flags)
5254 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
5255 return WINED3DERR_INVALIDCALL;
5258 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
5259 &src_rect, dst_surface, dst_surface->container->resource.draw_binding, &dst_rect)))
5260 return WINED3D_OK;
5263 else
5265 const struct blit_shader *blitter;
5267 /* In principle this would apply to depth blits as well, but we don't
5268 * implement those in the CPU blitter at the moment. */
5269 if ((dst_surface->locations & dst_surface->resource.map_binding)
5270 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
5272 if (scale)
5273 TRACE("Not doing sysmem blit because of scaling.\n");
5274 else if (convert)
5275 TRACE("Not doing sysmem blit because of format conversion.\n");
5276 else
5277 goto cpu;
5280 if (flags & WINEDDBLT_COLORFILL)
5282 struct wined3d_color color;
5283 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
5285 TRACE("Color fill.\n");
5287 if (!wined3d_format_convert_color_to_float(dst_surface->resource.format,
5288 palette, fx->u5.dwFillColor, &color))
5289 goto fallback;
5291 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
5292 return WINED3D_OK;
5294 else
5296 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
5297 const struct wined3d_color_key *color_key = NULL;
5299 TRACE("Color blit.\n");
5300 if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5302 color_key = &fx->ddckSrcColorkey;
5303 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5305 else if (flags & WINEDDBLT_KEYSRC)
5307 color_key = &src_surface->container->async.src_blt_color_key;
5308 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5310 else if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5311 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5313 /* Upload */
5314 if (scale)
5315 TRACE("Not doing upload because of scaling.\n");
5316 else if (convert)
5317 TRACE("Not doing upload because of format conversion.\n");
5318 else
5320 POINT dst_point = {dst_rect.left, dst_rect.top};
5322 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
5324 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
5325 surface_load_location(dst_surface, dst_surface->container->resource.draw_binding);
5326 return WINED3D_OK;
5330 else if (dst_swapchain && dst_swapchain->back_buffers
5331 && dst_surface->container == dst_swapchain->front_buffer
5332 && src_surface->container == dst_swapchain->back_buffers[0])
5334 /* Use present for back -> front blits. The idea behind this is
5335 * that present is potentially faster than a blit, in particular
5336 * when FBO blits aren't available. Some ddraw applications like
5337 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5338 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5339 * applications can't blit directly to the frontbuffer. */
5340 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5342 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5344 /* Set the swap effect to COPY, we don't want the backbuffer
5345 * to become undefined. */
5346 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5347 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5348 dst_swapchain->desc.swap_effect = swap_effect;
5350 return WINED3D_OK;
5353 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
5354 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5355 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5357 TRACE("Using FBO blit.\n");
5359 surface_blt_fbo(device, filter,
5360 src_surface, src_surface->container->resource.draw_binding, &src_rect,
5361 dst_surface, dst_surface->container->resource.draw_binding, &dst_rect);
5362 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5363 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5365 return WINED3D_OK;
5368 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
5369 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5370 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format);
5371 if (blitter)
5373 blitter->blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect, color_key);
5374 return WINED3D_OK;
5379 fallback:
5380 /* Special cases for render targets. */
5381 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
5382 return WINED3D_OK;
5384 cpu:
5386 /* For the rest call the X11 surface implementation. For render targets
5387 * this should be implemented OpenGL accelerated in surface_blt_special(),
5388 * other blits are rather rare. */
5389 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
5392 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5393 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5395 struct wined3d_device *device = container->resource.device;
5396 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5397 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5398 UINT multisample_quality = desc->multisample_quality;
5399 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
5400 unsigned int resource_size;
5401 HRESULT hr;
5403 if (multisample_quality > 0)
5405 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
5406 multisample_quality = 0;
5409 /* Quick lockable sanity check.
5410 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5411 * this function is too deep to need to care about things like this.
5412 * Levels need to be checked too, since they all affect what can be done. */
5413 switch (desc->pool)
5415 case WINED3D_POOL_MANAGED:
5416 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5417 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5418 break;
5420 case WINED3D_POOL_DEFAULT:
5421 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5422 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5423 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5424 break;
5426 case WINED3D_POOL_SCRATCH:
5427 case WINED3D_POOL_SYSTEM_MEM:
5428 break;
5430 default:
5431 FIXME("Unknown pool %#x.\n", desc->pool);
5432 break;
5435 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5436 FIXME("Trying to create a render target that isn't in the default pool.\n");
5438 /* FIXME: Check that the format is supported by the device. */
5440 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5441 if (!resource_size)
5442 return WINED3DERR_INVALIDCALL;
5444 if (device->wined3d->flags & WINED3D_NO3D)
5445 surface->surface_ops = &gdi_surface_ops;
5446 else
5447 surface->surface_ops = &surface_ops;
5449 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
5450 desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height, 1,
5451 resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5453 WARN("Failed to initialize resource, returning %#x.\n", hr);
5454 return hr;
5457 surface->container = container;
5458 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5459 list_init(&surface->renderbuffers);
5460 list_init(&surface->overlays);
5462 /* Flags */
5463 if (flags & WINED3D_SURFACE_DISCARD)
5464 surface->flags |= SFLAG_DISCARD;
5465 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5466 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5468 surface->texture_target = target;
5469 surface->texture_level = level;
5470 surface->texture_layer = layer;
5472 /* Call the private setup routine */
5473 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5475 ERR("Private setup failed, hr %#x.\n", hr);
5476 surface_cleanup(surface);
5477 return hr;
5480 /* Similar to lockable rendertargets above, creating the DIB section
5481 * during surface initialization prevents the sysmem pointer from changing
5482 * after a wined3d_surface_getdc() call. */
5483 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5484 && SUCCEEDED(surface_create_dib_section(surface)))
5485 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5487 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5489 wined3d_resource_free_sysmem(&surface->resource);
5490 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5491 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5494 return hr;
5497 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5498 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
5500 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
5501 const struct wined3d_parent_ops *parent_ops;
5502 struct wined3d_surface *object;
5503 void *parent;
5504 HRESULT hr;
5506 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
5507 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
5508 container, desc->width, desc->height, debug_d3dformat(desc->format),
5509 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
5510 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
5512 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
5513 return E_OUTOFMEMORY;
5515 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
5517 WARN("Failed to initialize surface, returning %#x.\n", hr);
5518 HeapFree(GetProcessHeap(), 0, object);
5519 return hr;
5522 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
5523 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
5525 WARN("Failed to create surface parent, hr %#x.\n", hr);
5526 wined3d_surface_destroy(object);
5527 return hr;
5530 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
5532 object->resource.parent = parent;
5533 object->resource.parent_ops = parent_ops;
5534 *surface = object;
5536 return hr;