wined3d: Never pass a surface to wined3d_resource_is_offscreen().
[wine/multimedia.git] / dlls / wined3d / surface.c
blob9603e21cd1786b756237cb23823849a8009945d0
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(glDeleteBuffersARB(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 tex_types 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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,
314 wined3d_gl_mag_filter(magLookup, filter));
315 checkGLcall("glTexParameteri");
316 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
317 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
318 checkGLcall("glTexParameteri");
319 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
320 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
321 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
322 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
323 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
324 checkGLcall("glTexEnvi");
326 /* Draw a quad */
327 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
328 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
329 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
331 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
332 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
334 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
335 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
337 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
338 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
339 gl_info->gl_ops.gl.p_glEnd();
341 /* Unbind the texture */
342 context_bind_texture(context, info.bind_target, 0);
344 /* We changed the filtering settings on the texture. Inform the
345 * container about this to get the filters reset properly next draw. */
346 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_POINT;
347 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
348 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_NONE;
349 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
352 /* Works correctly only for <= 4 bpp formats. */
353 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
355 masks[0] = ((1 << format->red_size) - 1) << format->red_offset;
356 masks[1] = ((1 << format->green_size) - 1) << format->green_offset;
357 masks[2] = ((1 << format->blue_size) - 1) << format->blue_offset;
360 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
362 const struct wined3d_format *format = surface->resource.format;
363 SYSTEM_INFO sysInfo;
364 BITMAPINFO *b_info;
365 int extraline = 0;
366 DWORD *masks;
368 TRACE("surface %p.\n", surface);
370 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
372 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
373 return WINED3DERR_INVALIDCALL;
376 switch (format->byte_count)
378 case 2:
379 case 4:
380 /* Allocate extra space to store the RGB bit masks. */
381 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
382 break;
384 case 3:
385 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
386 break;
388 default:
389 /* Allocate extra space for a palette. */
390 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
391 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
392 break;
395 if (!b_info)
396 return E_OUTOFMEMORY;
398 /* Some applications access the surface in via DWORDs, and do not take
399 * the necessary care at the end of the surface. So we need at least
400 * 4 extra bytes at the end of the surface. Check against the page size,
401 * if the last page used for the surface has at least 4 spare bytes we're
402 * safe, otherwise add an extra line to the DIB section. */
403 GetSystemInfo(&sysInfo);
404 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
406 extraline = 1;
407 TRACE("Adding an extra line to the DIB section.\n");
410 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
411 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
412 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
413 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
414 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
415 * wined3d_surface_get_pitch(surface);
416 b_info->bmiHeader.biPlanes = 1;
417 b_info->bmiHeader.biBitCount = format->byte_count * 8;
419 b_info->bmiHeader.biXPelsPerMeter = 0;
420 b_info->bmiHeader.biYPelsPerMeter = 0;
421 b_info->bmiHeader.biClrUsed = 0;
422 b_info->bmiHeader.biClrImportant = 0;
424 /* Get the bit masks */
425 masks = (DWORD *)b_info->bmiColors;
426 switch (surface->resource.format->id)
428 case WINED3DFMT_B8G8R8_UNORM:
429 b_info->bmiHeader.biCompression = BI_RGB;
430 break;
432 case WINED3DFMT_B5G5R5X1_UNORM:
433 case WINED3DFMT_B5G5R5A1_UNORM:
434 case WINED3DFMT_B4G4R4A4_UNORM:
435 case WINED3DFMT_B4G4R4X4_UNORM:
436 case WINED3DFMT_B2G3R3_UNORM:
437 case WINED3DFMT_B2G3R3A8_UNORM:
438 case WINED3DFMT_R10G10B10A2_UNORM:
439 case WINED3DFMT_R8G8B8A8_UNORM:
440 case WINED3DFMT_R8G8B8X8_UNORM:
441 case WINED3DFMT_B10G10R10A2_UNORM:
442 case WINED3DFMT_B5G6R5_UNORM:
443 case WINED3DFMT_R16G16B16A16_UNORM:
444 b_info->bmiHeader.biCompression = BI_BITFIELDS;
445 get_color_masks(format, masks);
446 break;
448 default:
449 /* Don't know palette */
450 b_info->bmiHeader.biCompression = BI_RGB;
451 break;
454 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
455 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
456 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
457 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
459 if (!surface->dib.DIBsection)
461 ERR("Failed to create DIB section.\n");
462 HeapFree(GetProcessHeap(), 0, b_info);
463 return HRESULT_FROM_WIN32(GetLastError());
466 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
467 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
469 HeapFree(GetProcessHeap(), 0, b_info);
471 /* Now allocate a DC. */
472 surface->hDC = CreateCompatibleDC(0);
473 SelectObject(surface->hDC, surface->dib.DIBsection);
475 surface->flags |= SFLAG_DIBSECTION;
477 return WINED3D_OK;
480 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
481 DWORD location)
483 if (location & WINED3D_LOCATION_BUFFER)
485 data->addr = NULL;
486 data->buffer_object = surface->pbo;
487 return;
489 if (location & WINED3D_LOCATION_USER_MEMORY)
491 data->addr = surface->user_memory;
492 data->buffer_object = 0;
493 return;
495 if (location & WINED3D_LOCATION_DIB)
497 data->addr = surface->dib.bitmap_data;
498 data->buffer_object = 0;
499 return;
501 if (location & WINED3D_LOCATION_SYSMEM)
503 data->addr = surface->resource.heap_memory;
504 data->buffer_object = 0;
505 return;
508 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
509 data->addr = NULL;
510 data->buffer_object = 0;
513 static void surface_prepare_buffer(struct wined3d_surface *surface)
515 struct wined3d_context *context;
516 GLenum error;
517 const struct wined3d_gl_info *gl_info;
519 if (surface->pbo)
520 return;
522 context = context_acquire(surface->resource.device, NULL);
523 gl_info = context->gl_info;
525 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
526 error = gl_info->gl_ops.gl.p_glGetError();
527 if (!surface->pbo || error != GL_NO_ERROR)
528 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
530 TRACE("Binding PBO %u.\n", surface->pbo);
532 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
533 checkGLcall("glBindBufferARB");
535 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
536 NULL, GL_STREAM_DRAW_ARB));
537 checkGLcall("glBufferDataARB");
539 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
540 checkGLcall("glBindBufferARB");
542 context_release(context);
545 static void surface_prepare_system_memory(struct wined3d_surface *surface)
547 TRACE("surface %p.\n", surface);
549 if (surface->resource.heap_memory)
550 return;
552 /* Whatever surface we have, make sure that there is memory allocated
553 * for the downloaded copy, or a PBO to map. */
554 if (!wined3d_resource_allocate_sysmem(&surface->resource))
555 ERR("Failed to allocate system memory.\n");
557 if (surface->locations & WINED3D_LOCATION_SYSMEM)
558 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
561 void surface_prepare_map_memory(struct wined3d_surface *surface)
563 switch (surface->resource.map_binding)
565 case WINED3D_LOCATION_SYSMEM:
566 surface_prepare_system_memory(surface);
567 break;
569 case WINED3D_LOCATION_USER_MEMORY:
570 if (!surface->user_memory)
571 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
572 break;
574 case WINED3D_LOCATION_DIB:
575 if (!surface->dib.bitmap_data)
576 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
577 break;
579 case WINED3D_LOCATION_BUFFER:
580 surface_prepare_buffer(surface);
581 break;
583 default:
584 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
588 static void surface_evict_sysmem(struct wined3d_surface *surface)
590 if (surface->resource.map_count || surface->flags & SFLAG_DONOTFREE)
591 return;
593 wined3d_resource_free_sysmem(&surface->resource);
594 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
597 static void surface_force_reload(struct wined3d_surface *surface)
599 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
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);
620 context_release(context);
622 surface_invalidate_location(surface, WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
623 surface_force_reload(surface);
626 static BOOL surface_use_pbo(const struct wined3d_surface *surface)
628 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
630 return surface->resource.pool == WINED3D_POOL_DEFAULT
631 && surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU
632 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
633 && !surface->resource.format->convert
634 && !(surface->flags & (SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM));
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(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
736 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
737 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
738 checkGLcall("glUnmapBufferARB");
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_color_to_float(const struct wined3d_surface *surface,
1017 DWORD color, struct wined3d_color *float_color)
1019 const struct wined3d_format *format = surface->resource.format;
1020 const struct wined3d_palette *palette;
1022 switch (format->id)
1024 case WINED3DFMT_P8_UINT:
1025 palette = surface->container->swapchain ? surface->container->swapchain->palette : NULL;
1027 if (palette)
1029 float_color->r = palette->colors[color].rgbRed / 255.0f;
1030 float_color->g = palette->colors[color].rgbGreen / 255.0f;
1031 float_color->b = palette->colors[color].rgbBlue / 255.0f;
1033 else
1035 float_color->r = 0.0f;
1036 float_color->g = 0.0f;
1037 float_color->b = 0.0f;
1039 float_color->a = color / 255.0f;
1040 break;
1042 case WINED3DFMT_B5G6R5_UNORM:
1043 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1044 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1045 float_color->b = (color & 0x1f) / 31.0f;
1046 float_color->a = 1.0f;
1047 break;
1049 case WINED3DFMT_B8G8R8_UNORM:
1050 case WINED3DFMT_B8G8R8X8_UNORM:
1051 float_color->r = D3DCOLOR_R(color);
1052 float_color->g = D3DCOLOR_G(color);
1053 float_color->b = D3DCOLOR_B(color);
1054 float_color->a = 1.0f;
1055 break;
1057 case WINED3DFMT_B8G8R8A8_UNORM:
1058 float_color->r = D3DCOLOR_R(color);
1059 float_color->g = D3DCOLOR_G(color);
1060 float_color->b = D3DCOLOR_B(color);
1061 float_color->a = D3DCOLOR_A(color);
1062 break;
1064 default:
1065 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1066 return FALSE;
1069 return TRUE;
1072 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1074 const struct wined3d_format *format = surface->resource.format;
1076 switch (format->id)
1078 case WINED3DFMT_S1_UINT_D15_UNORM:
1079 *float_depth = depth / (float)0x00007fff;
1080 break;
1082 case WINED3DFMT_D16_UNORM:
1083 *float_depth = depth / (float)0x0000ffff;
1084 break;
1086 case WINED3DFMT_D24_UNORM_S8_UINT:
1087 case WINED3DFMT_X8D24_UNORM:
1088 *float_depth = depth / (float)0x00ffffff;
1089 break;
1091 case WINED3DFMT_D32_UNORM:
1092 *float_depth = depth / (float)0xffffffff;
1093 break;
1095 default:
1096 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1097 return FALSE;
1100 return TRUE;
1103 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1105 const struct wined3d_resource *resource = &surface->resource;
1106 struct wined3d_device *device = resource->device;
1107 const struct blit_shader *blitter;
1109 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1110 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1111 if (!blitter)
1113 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1114 return WINED3DERR_INVALIDCALL;
1117 return blitter->depth_fill(device, surface, rect, depth);
1120 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1121 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1123 struct wined3d_device *device = src_surface->resource.device;
1125 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1126 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1127 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1128 return WINED3DERR_INVALIDCALL;
1130 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1132 surface_modify_ds_location(dst_surface, dst_location,
1133 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1135 return WINED3D_OK;
1138 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1139 struct wined3d_surface *render_target)
1141 TRACE("surface %p, render_target %p.\n", surface, render_target);
1143 /* TODO: Check surface sizes, pools, etc. */
1145 if (render_target->resource.multisample_type)
1146 return WINED3DERR_INVALIDCALL;
1148 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1151 /* Context activation is done by the caller. */
1152 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1154 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1155 checkGLcall("glDeleteBuffersARB(1, &surface->pbo)");
1157 surface->pbo = 0;
1158 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1161 static ULONG surface_resource_incref(struct wined3d_resource *resource)
1163 return wined3d_surface_incref(surface_from_resource(resource));
1166 static ULONG surface_resource_decref(struct wined3d_resource *resource)
1168 return wined3d_surface_decref(surface_from_resource(resource));
1171 static void surface_unload(struct wined3d_resource *resource)
1173 struct wined3d_surface *surface = surface_from_resource(resource);
1174 struct wined3d_renderbuffer_entry *entry, *entry2;
1175 struct wined3d_device *device = resource->device;
1176 const struct wined3d_gl_info *gl_info;
1177 struct wined3d_context *context;
1179 TRACE("surface %p.\n", surface);
1181 if (resource->pool == WINED3D_POOL_DEFAULT)
1183 /* Default pool resources are supposed to be destroyed before Reset is called.
1184 * Implicit resources stay however. So this means we have an implicit render target
1185 * or depth stencil. The content may be destroyed, but we still have to tear down
1186 * opengl resources, so we cannot leave early.
1188 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1189 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1190 * or the depth stencil into an FBO the texture or render buffer will be removed
1191 * and all flags get lost */
1192 surface_prepare_system_memory(surface);
1193 memset(surface->resource.heap_memory, 0, surface->resource.size);
1194 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1195 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1197 /* We also get here when the ddraw swapchain is destroyed, for example
1198 * for a mode switch. In this case this surface won't necessarily be
1199 * an implicit surface. We have to mark it lost so that the
1200 * application can restore it after the mode switch. */
1201 surface->flags |= SFLAG_LOST;
1203 else
1205 surface_prepare_map_memory(surface);
1206 surface_load_location(surface, surface->resource.map_binding);
1207 surface_invalidate_location(surface, ~surface->resource.map_binding);
1209 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1211 context = context_acquire(device, NULL);
1212 gl_info = context->gl_info;
1214 /* Destroy PBOs, but load them into real sysmem before */
1215 if (surface->pbo)
1216 surface_remove_pbo(surface, gl_info);
1218 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1219 * all application-created targets the application has to release the surface
1220 * before calling _Reset
1222 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1224 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1225 list_remove(&entry->entry);
1226 HeapFree(GetProcessHeap(), 0, entry);
1228 list_init(&surface->renderbuffers);
1229 surface->current_renderbuffer = NULL;
1231 if (surface->rb_multisample)
1233 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1234 surface->rb_multisample = 0;
1236 if (surface->rb_resolved)
1238 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1239 surface->rb_resolved = 0;
1242 context_release(context);
1244 resource_unload(resource);
1247 static const struct wined3d_resource_ops surface_resource_ops =
1249 surface_resource_incref,
1250 surface_resource_decref,
1251 surface_unload,
1254 static const struct wined3d_surface_ops surface_ops =
1256 surface_private_setup,
1257 surface_unmap,
1260 /*****************************************************************************
1261 * Initializes the GDI surface, aka creates the DIB section we render to
1262 * The DIB section creation is done by calling GetDC, which will create the
1263 * section and releasing the dc to allow the app to use it. The dib section
1264 * will stay until the surface is released
1266 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1267 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1268 * avoid confusion in the shared surface code.
1270 * Returns:
1271 * WINED3D_OK on success
1272 * The return values of called methods on failure
1274 *****************************************************************************/
1275 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1277 HRESULT hr;
1279 TRACE("surface %p.\n", surface);
1281 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1283 ERR("Overlays not yet supported by GDI surfaces.\n");
1284 return WINED3DERR_INVALIDCALL;
1287 /* Sysmem textures have memory already allocated - release it,
1288 * this avoids an unnecessary memcpy. */
1289 hr = surface_create_dib_section(surface);
1290 if (FAILED(hr))
1291 return hr;
1292 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1294 /* We don't mind the nonpow2 stuff in GDI. */
1295 surface->pow2Width = surface->resource.width;
1296 surface->pow2Height = surface->resource.height;
1298 return WINED3D_OK;
1301 static void gdi_surface_unmap(struct wined3d_surface *surface)
1303 TRACE("surface %p.\n", surface);
1305 /* Tell the swapchain to update the screen. */
1306 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
1307 x11_copy_to_screen(surface->container->swapchain, &surface->lockedRect);
1309 memset(&surface->lockedRect, 0, sizeof(RECT));
1312 static const struct wined3d_surface_ops gdi_surface_ops =
1314 gdi_surface_private_setup,
1315 gdi_surface_unmap,
1318 /* This call just downloads data, the caller is responsible for binding the
1319 * correct texture. */
1320 /* Context activation is done by the caller. */
1321 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1322 DWORD dst_location)
1324 const struct wined3d_format *format = surface->resource.format;
1325 struct wined3d_bo_address data;
1327 /* Only support read back of converted P8 surfaces. */
1328 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1330 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1331 return;
1334 surface_get_memory(surface, &data, dst_location);
1336 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1338 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
1339 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1341 if (data.buffer_object)
1343 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1344 checkGLcall("glBindBufferARB");
1345 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
1346 checkGLcall("glGetCompressedTexImageARB");
1347 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1348 checkGLcall("glBindBufferARB");
1350 else
1352 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
1353 surface->texture_level, data.addr));
1354 checkGLcall("glGetCompressedTexImageARB");
1357 else
1359 void *mem;
1360 GLenum gl_format = format->glFormat;
1361 GLenum gl_type = format->glType;
1362 int src_pitch = 0;
1363 int dst_pitch = 0;
1365 if (surface->flags & SFLAG_NONPOW2)
1367 unsigned char alignment = surface->resource.device->surface_alignment;
1368 src_pitch = format->byte_count * surface->pow2Width;
1369 dst_pitch = wined3d_surface_get_pitch(surface);
1370 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1371 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1373 else
1375 mem = data.addr;
1378 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1379 surface, surface->texture_level, gl_format, gl_type, mem);
1381 if (data.buffer_object)
1383 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1384 checkGLcall("glBindBufferARB");
1386 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1387 gl_format, gl_type, NULL);
1388 checkGLcall("glGetTexImage");
1390 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1391 checkGLcall("glBindBufferARB");
1393 else
1395 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1396 gl_format, gl_type, mem);
1397 checkGLcall("glGetTexImage");
1400 if (surface->flags & SFLAG_NONPOW2)
1402 const BYTE *src_data;
1403 BYTE *dst_data;
1404 UINT y;
1406 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1407 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1408 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1410 * We're doing this...
1412 * instead of boxing the texture :
1413 * |<-texture width ->| -->pow2width| /\
1414 * |111111111111111111| | |
1415 * |222 Texture 222222| boxed empty | texture height
1416 * |3333 Data 33333333| | |
1417 * |444444444444444444| | \/
1418 * ----------------------------------- |
1419 * | boxed empty | boxed empty | pow2height
1420 * | | | \/
1421 * -----------------------------------
1424 * we're repacking the data to the expected texture width
1426 * |<-texture width ->| -->pow2width| /\
1427 * |111111111111111111222222222222222| |
1428 * |222333333333333333333444444444444| texture height
1429 * |444444 | |
1430 * | | \/
1431 * | | |
1432 * | empty | pow2height
1433 * | | \/
1434 * -----------------------------------
1436 * == is the same as
1438 * |<-texture width ->| /\
1439 * |111111111111111111|
1440 * |222222222222222222|texture height
1441 * |333333333333333333|
1442 * |444444444444444444| \/
1443 * --------------------
1445 * This also means that any references to surface memory should work with the data as if it were a
1446 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1448 * internally the texture is still stored in a boxed format so any references to textureName will
1449 * get a boxed texture with width pow2width and not a texture of width resource.width.
1451 * Performance should not be an issue, because applications normally do not lock the surfaces when
1452 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
1453 * and doesn't have to be re-read. */
1454 src_data = mem;
1455 dst_data = data.addr;
1456 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1457 for (y = 0; y < surface->resource.height; ++y)
1459 memcpy(dst_data, src_data, dst_pitch);
1460 src_data += src_pitch;
1461 dst_data += dst_pitch;
1464 HeapFree(GetProcessHeap(), 0, mem);
1469 /* This call just uploads data, the caller is responsible for binding the
1470 * correct texture. */
1471 /* Context activation is done by the caller. */
1472 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1473 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1474 BOOL srgb, const struct wined3d_bo_address *data)
1476 UINT update_w = src_rect->right - src_rect->left;
1477 UINT update_h = src_rect->bottom - src_rect->top;
1479 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1480 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1481 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1483 if (surface->resource.map_count)
1485 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
1486 surface->flags |= SFLAG_PIN_SYSMEM;
1489 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1491 update_h *= format->height_scale.numerator;
1492 update_h /= format->height_scale.denominator;
1495 if (data->buffer_object)
1497 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
1498 checkGLcall("glBindBufferARB");
1501 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1503 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1504 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1505 const BYTE *addr = data->addr;
1506 GLenum internal;
1508 addr += (src_rect->top / format->block_height) * src_pitch;
1509 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1511 if (srgb)
1512 internal = format->glGammaInternal;
1513 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1514 && wined3d_resource_is_offscreen(&surface->container->resource))
1515 internal = format->rtInternal;
1516 else
1517 internal = format->glInternal;
1519 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
1520 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1521 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1523 if (row_length == src_pitch)
1525 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1526 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1528 else
1530 UINT row, y;
1532 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
1533 * can't use the unpack row length like below. */
1534 for (row = 0, y = dst_point->y; row < row_count; ++row)
1536 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1537 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1538 y += format->block_height;
1539 addr += src_pitch;
1542 checkGLcall("glCompressedTexSubImage2DARB");
1544 else
1546 const BYTE *addr = data->addr;
1548 addr += src_rect->top * src_pitch;
1549 addr += src_rect->left * format->byte_count;
1551 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1552 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1553 update_w, update_h, format->glFormat, format->glType, addr);
1555 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1556 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1557 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1558 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1559 checkGLcall("glTexSubImage2D");
1562 if (data->buffer_object)
1564 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1565 checkGLcall("glBindBufferARB");
1568 if (wined3d_settings.strict_draw_ordering)
1569 gl_info->gl_ops.gl.p_glFlush();
1571 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1573 struct wined3d_device *device = surface->resource.device;
1574 unsigned int i;
1576 for (i = 0; i < device->context_count; ++i)
1578 context_surface_update(device->contexts[i], surface);
1583 static HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck, BOOL use_texturing,
1584 struct wined3d_format *format, enum wined3d_conversion_type *conversion_type)
1586 BOOL colorkey_active = need_alpha_ck && (surface->container->color_key_flags & WINEDDSD_CKSRCBLT);
1587 const struct wined3d_device *device = surface->resource.device;
1588 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1589 BOOL blit_supported = FALSE;
1591 /* Copy the default values from the surface. Below we might perform fixups */
1592 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
1593 *format = *surface->resource.format;
1594 *conversion_type = WINED3D_CT_NONE;
1596 /* Ok, now look if we have to do any conversion */
1597 switch (surface->resource.format->id)
1599 case WINED3DFMT_P8_UINT:
1600 /* Below the call to blit_supported is disabled for Wine 1.2
1601 * because the function isn't operating correctly yet. At the
1602 * moment 8-bit blits are handled in software and if certain GL
1603 * extensions are around, surface conversion is performed at
1604 * upload time. The blit_supported call recognizes it as a
1605 * destination fixup. This type of upload 'fixup' and 8-bit to
1606 * 8-bit blits need to be handled by the blit_shader.
1607 * TODO: get rid of this #if 0. */
1608 #if 0
1609 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1610 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
1611 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
1612 #endif
1613 blit_supported = gl_info->supported[ARB_FRAGMENT_PROGRAM];
1615 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
1616 * texturing. Further also use conversion in case of color keying.
1617 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1618 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1619 * conflicts with this.
1621 if (!((blit_supported && surface->container->swapchain
1622 && surface->container == surface->container->swapchain->front_buffer))
1623 || colorkey_active || !use_texturing)
1625 format->glFormat = GL_RGBA;
1626 format->glInternal = GL_RGBA;
1627 format->glType = GL_UNSIGNED_BYTE;
1628 format->conv_byte_count = 4;
1629 *conversion_type = WINED3D_CT_PALETTED;
1631 break;
1633 case WINED3DFMT_B2G3R3_UNORM:
1634 /* **********************
1635 GL_UNSIGNED_BYTE_3_3_2
1636 ********************** */
1637 if (colorkey_active) {
1638 /* This texture format will never be used.. So do not care about color keying
1639 up until the point in time it will be needed :-) */
1640 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1642 break;
1644 case WINED3DFMT_B5G6R5_UNORM:
1645 if (colorkey_active)
1647 *conversion_type = WINED3D_CT_CK_565;
1648 format->glFormat = GL_RGBA;
1649 format->glInternal = GL_RGB5_A1;
1650 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
1651 format->conv_byte_count = 2;
1653 break;
1655 case WINED3DFMT_B5G5R5X1_UNORM:
1656 if (colorkey_active)
1658 *conversion_type = WINED3D_CT_CK_5551;
1659 format->glFormat = GL_BGRA;
1660 format->glInternal = GL_RGB5_A1;
1661 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1662 format->conv_byte_count = 2;
1664 break;
1666 case WINED3DFMT_B8G8R8_UNORM:
1667 if (colorkey_active)
1669 *conversion_type = WINED3D_CT_CK_RGB24;
1670 format->glFormat = GL_RGBA;
1671 format->glInternal = GL_RGBA8;
1672 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1673 format->conv_byte_count = 4;
1675 break;
1677 case WINED3DFMT_B8G8R8X8_UNORM:
1678 if (colorkey_active)
1680 *conversion_type = WINED3D_CT_RGB32_888;
1681 format->glFormat = GL_RGBA;
1682 format->glInternal = GL_RGBA8;
1683 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1684 format->conv_byte_count = 4;
1686 break;
1688 case WINED3DFMT_B8G8R8A8_UNORM:
1689 if (colorkey_active)
1691 *conversion_type = WINED3D_CT_CK_ARGB32;
1692 format->conv_byte_count = 4;
1694 break;
1696 default:
1697 break;
1700 if (*conversion_type != WINED3D_CT_NONE)
1702 format->rtInternal = format->glInternal;
1703 format->glGammaInternal = format->glInternal;
1706 return WINED3D_OK;
1709 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
1711 UINT width_mask, height_mask;
1713 if (!rect->left && !rect->top
1714 && rect->right == surface->resource.width
1715 && rect->bottom == surface->resource.height)
1716 return TRUE;
1718 /* This assumes power of two block sizes, but NPOT block sizes would be
1719 * silly anyway. */
1720 width_mask = surface->resource.format->block_width - 1;
1721 height_mask = surface->resource.format->block_height - 1;
1723 if (!(rect->left & width_mask) && !(rect->top & height_mask)
1724 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
1725 return TRUE;
1727 return FALSE;
1730 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1731 struct wined3d_surface *src_surface, const RECT *src_rect)
1733 const struct wined3d_format *src_format;
1734 const struct wined3d_format *dst_format;
1735 const struct wined3d_gl_info *gl_info;
1736 enum wined3d_conversion_type convert;
1737 struct wined3d_context *context;
1738 struct wined3d_bo_address data;
1739 struct wined3d_format format;
1740 UINT update_w, update_h;
1741 UINT dst_w, dst_h;
1742 RECT r, dst_rect;
1743 UINT src_pitch;
1744 POINT p;
1746 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1747 dst_surface, wine_dbgstr_point(dst_point),
1748 src_surface, wine_dbgstr_rect(src_rect));
1750 src_format = src_surface->resource.format;
1751 dst_format = dst_surface->resource.format;
1753 if (src_format->id != dst_format->id)
1755 WARN("Source and destination surfaces should have the same format.\n");
1756 return WINED3DERR_INVALIDCALL;
1759 if (!dst_point)
1761 p.x = 0;
1762 p.y = 0;
1763 dst_point = &p;
1765 else if (dst_point->x < 0 || dst_point->y < 0)
1767 WARN("Invalid destination point.\n");
1768 return WINED3DERR_INVALIDCALL;
1771 if (!src_rect)
1773 r.left = 0;
1774 r.top = 0;
1775 r.right = src_surface->resource.width;
1776 r.bottom = src_surface->resource.height;
1777 src_rect = &r;
1779 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1780 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1782 WARN("Invalid source rectangle.\n");
1783 return WINED3DERR_INVALIDCALL;
1786 dst_w = dst_surface->resource.width;
1787 dst_h = dst_surface->resource.height;
1789 update_w = src_rect->right - src_rect->left;
1790 update_h = src_rect->bottom - src_rect->top;
1792 if (update_w > dst_w || dst_point->x > dst_w - update_w
1793 || update_h > dst_h || dst_point->y > dst_h - update_h)
1795 WARN("Destination out of bounds.\n");
1796 return WINED3DERR_INVALIDCALL;
1799 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
1801 WARN("Source rectangle not block-aligned.\n");
1802 return WINED3DERR_INVALIDCALL;
1805 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1806 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
1808 WARN("Destination rectangle not block-aligned.\n");
1809 return WINED3DERR_INVALIDCALL;
1812 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1813 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
1814 if (convert != WINED3D_CT_NONE || format.convert)
1815 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1817 context = context_acquire(dst_surface->resource.device, NULL);
1818 gl_info = context->gl_info;
1820 /* Only load the surface for partial updates. For newly allocated texture
1821 * the texture wouldn't be the current location, and we'd upload zeroes
1822 * just to overwrite them again. */
1823 if (update_w == dst_w && update_h == dst_h)
1824 surface_prepare_texture(dst_surface, context, FALSE);
1825 else
1826 surface_load_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1827 wined3d_texture_bind(dst_surface->container, context, FALSE);
1829 surface_get_memory(src_surface, &data, src_surface->locations);
1830 src_pitch = wined3d_surface_get_pitch(src_surface);
1832 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
1834 context_invalidate_active_texture(context);
1836 context_release(context);
1838 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1839 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1841 return WINED3D_OK;
1844 /* This call just allocates the texture, the caller is responsible for binding
1845 * the correct texture. */
1846 /* Context activation is done by the caller. */
1847 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1848 const struct wined3d_format *format, BOOL srgb)
1850 BOOL disable_client_storage = FALSE;
1851 GLsizei width = surface->pow2Width;
1852 GLsizei height = surface->pow2Height;
1853 const BYTE *mem = NULL;
1854 GLenum internal;
1856 if (srgb)
1857 internal = format->glGammaInternal;
1858 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1859 && wined3d_resource_is_offscreen(&surface->container->resource))
1860 internal = format->rtInternal;
1861 else
1862 internal = format->glInternal;
1864 if (!internal)
1865 FIXME("No GL internal format for format %s.\n", debug_d3dformat(format->id));
1867 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1869 height *= format->height_scale.numerator;
1870 height /= format->height_scale.denominator;
1873 TRACE("(%p) : Creating surface (target %#x) level %d, d3d format %s, internal format %#x, width %d, height %d, gl format %#x, gl type=%#x\n",
1874 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
1875 internal, width, height, format->glFormat, format->glType);
1877 if (gl_info->supported[APPLE_CLIENT_STORAGE])
1879 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
1880 || !surface->resource.heap_memory)
1882 /* In some cases we want to disable client storage.
1883 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
1884 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
1885 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
1886 * heap_memory == NULL: Not defined in the extension. Seems to disable client storage effectively
1888 surface->flags &= ~SFLAG_CLIENT;
1890 else
1892 surface->flags |= SFLAG_CLIENT;
1893 mem = surface->resource.heap_memory;
1895 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
1896 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
1897 disable_client_storage = TRUE;
1901 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
1903 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
1904 internal, width, height, 0, surface->resource.size, mem));
1905 checkGLcall("glCompressedTexImage2DARB");
1907 else
1909 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
1910 internal, width, height, 0, format->glFormat, format->glType, mem);
1911 checkGLcall("glTexImage2D");
1914 if (disable_client_storage)
1916 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
1917 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
1921 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1922 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1923 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1924 /* Context activation is done by the caller. */
1925 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1927 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1928 struct wined3d_renderbuffer_entry *entry;
1929 GLuint renderbuffer = 0;
1930 unsigned int src_width, src_height;
1931 unsigned int width, height;
1933 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1935 width = rt->pow2Width;
1936 height = rt->pow2Height;
1938 else
1940 width = surface->pow2Width;
1941 height = surface->pow2Height;
1944 src_width = surface->pow2Width;
1945 src_height = surface->pow2Height;
1947 /* A depth stencil smaller than the render target is not valid */
1948 if (width > src_width || height > src_height) return;
1950 /* Remove any renderbuffer set if the sizes match */
1951 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1952 || (width == src_width && height == src_height))
1954 surface->current_renderbuffer = NULL;
1955 return;
1958 /* Look if we've already got a renderbuffer of the correct dimensions */
1959 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1961 if (entry->width == width && entry->height == height)
1963 renderbuffer = entry->id;
1964 surface->current_renderbuffer = entry;
1965 break;
1969 if (!renderbuffer)
1971 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1972 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1973 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1974 surface->resource.format->glInternal, width, height);
1976 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1977 entry->width = width;
1978 entry->height = height;
1979 entry->id = renderbuffer;
1980 list_add_head(&surface->renderbuffers, &entry->entry);
1982 surface->current_renderbuffer = entry;
1985 checkGLcall("set_compatible_renderbuffer");
1988 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1990 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1992 TRACE("surface %p.\n", surface);
1994 if (!swapchain)
1996 ERR("Surface %p is not on a swapchain.\n", surface);
1997 return GL_NONE;
2000 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
2002 if (swapchain->render_to_fbo)
2004 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2005 return GL_COLOR_ATTACHMENT0;
2007 TRACE("Returning GL_BACK\n");
2008 return GL_BACK;
2010 else if (surface->container == swapchain->front_buffer)
2012 TRACE("Returning GL_FRONT\n");
2013 return GL_FRONT;
2016 FIXME("Higher back buffer, returning GL_BACK\n");
2017 return GL_BACK;
2020 void surface_load(struct wined3d_surface *surface, BOOL srgb)
2022 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2023 BOOL ck_changed;
2025 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2027 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
2028 ERR("Not supported on scratch surfaces.\n");
2030 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->container->color_key_flags & WINEDDSD_CKSRCBLT);
2032 /* Reload if either the texture and sysmem have different ideas about the
2033 * color key, or the actual key values changed. */
2034 if (ck_changed || ((surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
2035 && (surface->gl_color_key.color_space_low_value
2036 != surface->container->src_blt_color_key.color_space_low_value
2037 || surface->gl_color_key.color_space_high_value
2038 != surface->container->src_blt_color_key.color_space_high_value)))
2040 TRACE("Reloading because of color keying\n");
2041 /* To perform the color key conversion we need a sysmem copy of
2042 * the surface. Make sure we have it. */
2044 surface_prepare_map_memory(surface);
2045 surface_load_location(surface, surface->resource.map_binding);
2046 surface_invalidate_location(surface, ~surface->resource.map_binding);
2047 /* Switching color keying on / off may change the internal format. */
2048 if (ck_changed)
2049 surface_force_reload(surface);
2051 else if (!(surface->locations & location))
2053 TRACE("Reloading because surface is dirty.\n");
2055 else
2057 TRACE("surface is already in texture\n");
2058 return;
2061 surface_load_location(surface, location);
2062 surface_evict_sysmem(surface);
2065 /* See also float_16_to_32() in wined3d_private.h */
2066 static inline unsigned short float_32_to_16(const float *in)
2068 int exp = 0;
2069 float tmp = fabsf(*in);
2070 unsigned int mantissa;
2071 unsigned short ret;
2073 /* Deal with special numbers */
2074 if (*in == 0.0f)
2075 return 0x0000;
2076 if (isnan(*in))
2077 return 0x7c01;
2078 if (isinf(*in))
2079 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2081 if (tmp < powf(2, 10))
2085 tmp = tmp * 2.0f;
2086 exp--;
2087 } while (tmp < powf(2, 10));
2089 else if (tmp >= powf(2, 11))
2093 tmp /= 2.0f;
2094 exp++;
2095 } while (tmp >= powf(2, 11));
2098 mantissa = (unsigned int)tmp;
2099 if (tmp - mantissa >= 0.5f)
2100 ++mantissa; /* Round to nearest, away from zero. */
2102 exp += 10; /* Normalize the mantissa. */
2103 exp += 15; /* Exponent is encoded with excess 15. */
2105 if (exp > 30) /* too big */
2107 ret = 0x7c00; /* INF */
2109 else if (exp <= 0)
2111 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2112 while (exp <= 0)
2114 mantissa = mantissa >> 1;
2115 ++exp;
2117 ret = mantissa & 0x3ff;
2119 else
2121 ret = (exp << 10) | (mantissa & 0x3ff);
2124 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2125 return ret;
2128 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2130 TRACE("surface %p, container %p.\n", surface, surface->container);
2132 return wined3d_texture_incref(surface->container);
2135 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2137 TRACE("surface %p, container %p.\n", surface, surface->container);
2139 return wined3d_texture_decref(surface->container);
2142 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2144 TRACE("surface %p.\n", surface);
2146 if (!surface->resource.device->d3d_initialized)
2148 ERR("D3D not initialized.\n");
2149 return;
2152 wined3d_texture_preload(surface->container);
2155 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2157 TRACE("surface %p.\n", surface);
2159 return surface->resource.parent;
2162 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2164 TRACE("surface %p.\n", surface);
2166 return &surface->resource;
2169 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2171 TRACE("surface %p, flags %#x.\n", surface, flags);
2173 switch (flags)
2175 case WINEDDGBS_CANBLT:
2176 case WINEDDGBS_ISBLTDONE:
2177 return WINED3D_OK;
2179 default:
2180 return WINED3DERR_INVALIDCALL;
2184 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2186 TRACE("surface %p, flags %#x.\n", surface, flags);
2188 /* XXX: DDERR_INVALIDSURFACETYPE */
2190 switch (flags)
2192 case WINEDDGFS_CANFLIP:
2193 case WINEDDGFS_ISFLIPDONE:
2194 return WINED3D_OK;
2196 default:
2197 return WINED3DERR_INVALIDCALL;
2201 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2203 TRACE("surface %p.\n", surface);
2205 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2206 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2209 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2211 TRACE("surface %p.\n", surface);
2213 surface->flags &= ~SFLAG_LOST;
2214 return WINED3D_OK;
2217 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2219 unsigned int alignment;
2220 DWORD pitch;
2222 TRACE("surface %p.\n", surface);
2224 if (surface->pitch)
2225 return surface->pitch;
2227 alignment = surface->resource.device->surface_alignment;
2228 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
2229 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2231 TRACE("Returning %u.\n", pitch);
2233 return pitch;
2236 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2238 LONG w, h;
2240 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2242 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2244 WARN("Not an overlay surface.\n");
2245 return WINEDDERR_NOTAOVERLAYSURFACE;
2248 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2249 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2250 surface->overlay_destrect.left = x;
2251 surface->overlay_destrect.top = y;
2252 surface->overlay_destrect.right = x + w;
2253 surface->overlay_destrect.bottom = y + h;
2255 return WINED3D_OK;
2258 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2260 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2262 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2264 TRACE("Not an overlay surface.\n");
2265 return WINEDDERR_NOTAOVERLAYSURFACE;
2268 if (!surface->overlay_dest)
2270 TRACE("Overlay not visible.\n");
2271 *x = 0;
2272 *y = 0;
2273 return WINEDDERR_OVERLAYNOTVISIBLE;
2276 *x = surface->overlay_destrect.left;
2277 *y = surface->overlay_destrect.top;
2279 TRACE("Returning position %d, %d.\n", *x, *y);
2281 return WINED3D_OK;
2284 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2285 DWORD flags, struct wined3d_surface *ref)
2287 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2289 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2291 TRACE("Not an overlay surface.\n");
2292 return WINEDDERR_NOTAOVERLAYSURFACE;
2295 return WINED3D_OK;
2298 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2299 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2301 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2302 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2304 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2306 WARN("Not an overlay surface.\n");
2307 return WINEDDERR_NOTAOVERLAYSURFACE;
2309 else if (!dst_surface)
2311 WARN("Dest surface is NULL.\n");
2312 return WINED3DERR_INVALIDCALL;
2315 if (src_rect)
2317 surface->overlay_srcrect = *src_rect;
2319 else
2321 surface->overlay_srcrect.left = 0;
2322 surface->overlay_srcrect.top = 0;
2323 surface->overlay_srcrect.right = surface->resource.width;
2324 surface->overlay_srcrect.bottom = surface->resource.height;
2327 if (dst_rect)
2329 surface->overlay_destrect = *dst_rect;
2331 else
2333 surface->overlay_destrect.left = 0;
2334 surface->overlay_destrect.top = 0;
2335 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2336 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2339 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2341 surface->overlay_dest = NULL;
2342 list_remove(&surface->overlay_entry);
2345 if (flags & WINEDDOVER_SHOW)
2347 if (surface->overlay_dest != dst_surface)
2349 surface->overlay_dest = dst_surface;
2350 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2353 else if (flags & WINEDDOVER_HIDE)
2355 /* tests show that the rectangles are erased on hide */
2356 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2357 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2358 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2359 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2360 surface->overlay_dest = NULL;
2363 return WINED3D_OK;
2366 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
2367 UINT width, UINT height, enum wined3d_format_id format_id,
2368 enum wined3d_multisample_type multisample_type, UINT multisample_quality,
2369 void *mem, UINT pitch)
2371 struct wined3d_device *device = surface->resource.device;
2372 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2373 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
2374 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height, 1);
2375 struct wined3d_texture *texture;
2376 BOOL create_dib = FALSE;
2377 HRESULT hr;
2378 DWORD valid_location = 0;
2380 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u, "
2381 "mem %p, pitch %u.\n",
2382 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type, mem, pitch);
2384 if (!resource_size)
2385 return WINED3DERR_INVALIDCALL;
2387 if (surface->resource.map_count || (surface->flags & SFLAG_DCINUSE))
2389 WARN("Surface is mapped or the DC is in use.\n");
2390 return WINED3DERR_INVALIDCALL;
2393 if (device->d3d_initialized)
2394 surface->resource.resource_ops->resource_unload(&surface->resource);
2396 if (surface->flags & SFLAG_DIBSECTION)
2398 DeleteDC(surface->hDC);
2399 DeleteObject(surface->dib.DIBsection);
2400 surface->dib.bitmap_data = NULL;
2401 surface->flags &= ~SFLAG_DIBSECTION;
2402 create_dib = TRUE;
2405 surface->locations = 0;
2406 wined3d_resource_free_sysmem(&surface->resource);
2408 surface->resource.width = width;
2409 surface->resource.height = height;
2410 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2411 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2413 surface->pow2Width = width;
2414 surface->pow2Height = height;
2416 else
2418 surface->pow2Width = surface->pow2Height = 1;
2419 while (surface->pow2Width < width)
2420 surface->pow2Width <<= 1;
2421 while (surface->pow2Height < height)
2422 surface->pow2Height <<= 1;
2425 if (surface->pow2Width != width || surface->pow2Height != height)
2426 surface->flags |= SFLAG_NONPOW2;
2427 else
2428 surface->flags &= ~SFLAG_NONPOW2;
2430 surface->user_memory = mem;
2431 if (surface->user_memory)
2433 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
2434 valid_location = WINED3D_LOCATION_USER_MEMORY;
2436 surface->pitch = pitch;
2437 surface->resource.format = format;
2438 surface->resource.multisample_type = multisample_type;
2439 surface->resource.multisample_quality = multisample_quality;
2440 if (surface->pitch)
2441 surface->resource.size = height * surface->pitch;
2442 else
2443 surface->resource.size = resource_size;
2445 /* The format might be changed to a format that needs conversion.
2446 * If the surface didn't use PBOs previously but could now, don't
2447 * change it - whatever made us not use PBOs might come back, e.g.
2448 * color keys. */
2449 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2450 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2452 texture = surface->container;
2453 texture->resource.format = format;
2454 texture->resource.multisample_type = multisample_type;
2455 texture->resource.multisample_quality = multisample_quality;
2456 texture->resource.width = width;
2457 texture->resource.height = height;
2459 if (create_dib)
2461 if (FAILED(hr = surface_create_dib_section(surface)))
2463 ERR("Failed to create dib section, hr %#x.\n", hr);
2464 return hr;
2466 if (!valid_location)
2467 valid_location = WINED3D_LOCATION_DIB;
2470 if (!valid_location)
2472 surface_prepare_system_memory(surface);
2473 valid_location = WINED3D_LOCATION_SYSMEM;
2476 surface_validate_location(surface, valid_location);
2478 return WINED3D_OK;
2481 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2482 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2484 unsigned short *dst_s;
2485 const float *src_f;
2486 unsigned int x, y;
2488 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2490 for (y = 0; y < h; ++y)
2492 src_f = (const float *)(src + y * pitch_in);
2493 dst_s = (unsigned short *) (dst + y * pitch_out);
2494 for (x = 0; x < w; ++x)
2496 dst_s[x] = float_32_to_16(src_f + x);
2501 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2502 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2504 static const unsigned char convert_5to8[] =
2506 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2507 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2508 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2509 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2511 static const unsigned char convert_6to8[] =
2513 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2514 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2515 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2516 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2517 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2518 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2519 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2520 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2522 unsigned int x, y;
2524 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2526 for (y = 0; y < h; ++y)
2528 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2529 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2530 for (x = 0; x < w; ++x)
2532 WORD pixel = src_line[x];
2533 dst_line[x] = 0xff000000
2534 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2535 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2536 | convert_5to8[(pixel & 0x001f)];
2541 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2542 * in both cases we're just setting the X / Alpha channel to 0xff. */
2543 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2544 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2546 unsigned int x, y;
2548 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2550 for (y = 0; y < h; ++y)
2552 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2553 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2555 for (x = 0; x < w; ++x)
2557 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2562 static inline BYTE cliptobyte(int x)
2564 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2567 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2568 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2570 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2571 unsigned int x, y;
2573 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2575 for (y = 0; y < h; ++y)
2577 const BYTE *src_line = src + y * pitch_in;
2578 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2579 for (x = 0; x < w; ++x)
2581 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2582 * C = Y - 16; D = U - 128; E = V - 128;
2583 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2584 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2585 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2586 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2587 * U and V are shared between the pixels. */
2588 if (!(x & 1)) /* For every even pixel, read new U and V. */
2590 d = (int) src_line[1] - 128;
2591 e = (int) src_line[3] - 128;
2592 r2 = 409 * e + 128;
2593 g2 = - 100 * d - 208 * e + 128;
2594 b2 = 516 * d + 128;
2596 c2 = 298 * ((int) src_line[0] - 16);
2597 dst_line[x] = 0xff000000
2598 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2599 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2600 | cliptobyte((c2 + b2) >> 8); /* blue */
2601 /* Scale RGB values to 0..255 range,
2602 * then clip them if still not in range (may be negative),
2603 * then shift them within DWORD if necessary. */
2604 src_line += 2;
2609 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2610 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2612 unsigned int x, y;
2613 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2615 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2617 for (y = 0; y < h; ++y)
2619 const BYTE *src_line = src + y * pitch_in;
2620 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2621 for (x = 0; x < w; ++x)
2623 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2624 * C = Y - 16; D = U - 128; E = V - 128;
2625 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2626 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2627 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2628 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2629 * U and V are shared between the pixels. */
2630 if (!(x & 1)) /* For every even pixel, read new U and V. */
2632 d = (int) src_line[1] - 128;
2633 e = (int) src_line[3] - 128;
2634 r2 = 409 * e + 128;
2635 g2 = - 100 * d - 208 * e + 128;
2636 b2 = 516 * d + 128;
2638 c2 = 298 * ((int) src_line[0] - 16);
2639 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2640 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2641 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2642 /* Scale RGB values to 0..255 range,
2643 * then clip them if still not in range (may be negative),
2644 * then shift them within DWORD if necessary. */
2645 src_line += 2;
2650 struct d3dfmt_converter_desc
2652 enum wined3d_format_id from, to;
2653 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2656 static const struct d3dfmt_converter_desc converters[] =
2658 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2659 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2660 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2661 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2662 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2663 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2666 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2667 enum wined3d_format_id to)
2669 unsigned int i;
2671 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2673 if (converters[i].from == from && converters[i].to == to)
2674 return &converters[i];
2677 return NULL;
2680 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2682 struct wined3d_map_desc src_map, dst_map;
2683 const struct d3dfmt_converter_desc *conv;
2684 struct wined3d_texture *ret = NULL;
2685 struct wined3d_resource_desc desc;
2686 struct wined3d_surface *dst;
2688 conv = find_converter(source->resource.format->id, to_fmt);
2689 if (!conv)
2691 FIXME("Cannot find a conversion function from format %s to %s.\n",
2692 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2693 return NULL;
2696 /* FIXME: Multisampled conversion? */
2697 wined3d_resource_get_desc(&source->resource, &desc);
2698 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2699 desc.format = to_fmt;
2700 desc.usage = 0;
2701 desc.pool = WINED3D_POOL_SCRATCH;
2702 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2703 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, &wined3d_null_parent_ops, &ret)))
2705 ERR("Failed to create a destination surface for conversion.\n");
2706 return NULL;
2708 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2710 memset(&src_map, 0, sizeof(src_map));
2711 memset(&dst_map, 0, sizeof(dst_map));
2713 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2715 ERR("Failed to lock the source surface.\n");
2716 wined3d_texture_decref(ret);
2717 return NULL;
2719 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2721 ERR("Failed to lock the destination surface.\n");
2722 wined3d_surface_unmap(source);
2723 wined3d_texture_decref(ret);
2724 return NULL;
2727 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2728 source->resource.width, source->resource.height);
2730 wined3d_surface_unmap(dst);
2731 wined3d_surface_unmap(source);
2733 return ret;
2736 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2737 unsigned int bpp, UINT pitch, DWORD color)
2739 BYTE *first;
2740 unsigned int x, y;
2742 /* Do first row */
2744 #define COLORFILL_ROW(type) \
2745 do { \
2746 type *d = (type *)buf; \
2747 for (x = 0; x < width; ++x) \
2748 d[x] = (type)color; \
2749 } while(0)
2751 switch (bpp)
2753 case 1:
2754 COLORFILL_ROW(BYTE);
2755 break;
2757 case 2:
2758 COLORFILL_ROW(WORD);
2759 break;
2761 case 3:
2763 BYTE *d = buf;
2764 for (x = 0; x < width; ++x, d += 3)
2766 d[0] = (color ) & 0xff;
2767 d[1] = (color >> 8) & 0xff;
2768 d[2] = (color >> 16) & 0xff;
2770 break;
2772 case 4:
2773 COLORFILL_ROW(DWORD);
2774 break;
2776 default:
2777 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2778 return WINED3DERR_NOTAVAILABLE;
2781 #undef COLORFILL_ROW
2783 /* Now copy first row. */
2784 first = buf;
2785 for (y = 1; y < height; ++y)
2787 buf += pitch;
2788 memcpy(buf, first, width * bpp);
2791 return WINED3D_OK;
2794 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2796 return surface_from_resource(resource);
2799 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2801 TRACE("surface %p.\n", surface);
2803 if (!surface->resource.map_count)
2805 WARN("Trying to unmap unmapped surface.\n");
2806 return WINEDDERR_NOTLOCKED;
2808 --surface->resource.map_count;
2810 surface->surface_ops->surface_unmap(surface);
2812 return WINED3D_OK;
2815 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2816 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
2818 const struct wined3d_format *format = surface->resource.format;
2819 struct wined3d_device *device = surface->resource.device;
2820 struct wined3d_context *context;
2821 const struct wined3d_gl_info *gl_info;
2822 BYTE *base_memory;
2824 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
2825 surface, map_desc, wine_dbgstr_rect(rect), flags);
2827 if (surface->resource.map_count)
2829 WARN("Surface is already mapped.\n");
2830 return WINED3DERR_INVALIDCALL;
2833 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
2834 && !surface_check_block_align(surface, rect))
2836 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
2837 wine_dbgstr_rect(rect), format->block_width, format->block_height);
2839 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2840 return WINED3DERR_INVALIDCALL;
2843 ++surface->resource.map_count;
2845 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2846 WARN("Trying to lock unlockable surface.\n");
2848 /* Performance optimization: Count how often a surface is mapped, if it is
2849 * mapped regularly do not throw away the system memory copy. This avoids
2850 * the need to download the surface from OpenGL all the time. The surface
2851 * is still downloaded if the OpenGL texture is changed. */
2852 if (!(surface->flags & SFLAG_DYNLOCK) && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2854 if (++surface->lockCount > MAXLOCKCOUNT)
2856 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2857 surface->flags |= SFLAG_DYNLOCK;
2861 surface_prepare_map_memory(surface);
2862 if (flags & WINED3D_MAP_DISCARD)
2864 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2865 wined3d_debug_location(surface->resource.map_binding));
2866 surface_validate_location(surface, surface->resource.map_binding);
2868 else
2870 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2871 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2873 surface_load_location(surface, surface->resource.map_binding);
2876 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2877 surface_invalidate_location(surface, ~surface->resource.map_binding);
2879 switch (surface->resource.map_binding)
2881 case WINED3D_LOCATION_SYSMEM:
2882 base_memory = surface->resource.heap_memory;
2883 break;
2885 case WINED3D_LOCATION_USER_MEMORY:
2886 base_memory = surface->user_memory;
2887 break;
2889 case WINED3D_LOCATION_DIB:
2890 base_memory = surface->dib.bitmap_data;
2891 break;
2893 case WINED3D_LOCATION_BUFFER:
2894 context = context_acquire(device, NULL);
2895 gl_info = context->gl_info;
2897 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
2898 base_memory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
2899 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2900 checkGLcall("map PBO");
2902 context_release(context);
2903 break;
2905 default:
2906 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2907 base_memory = NULL;
2910 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2911 map_desc->row_pitch = surface->resource.width * format->byte_count;
2912 else
2913 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2914 map_desc->slice_pitch = 0;
2916 if (!rect)
2918 map_desc->data = base_memory;
2919 surface->lockedRect.left = 0;
2920 surface->lockedRect.top = 0;
2921 surface->lockedRect.right = surface->resource.width;
2922 surface->lockedRect.bottom = surface->resource.height;
2924 else
2926 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2928 /* Compressed textures are block based, so calculate the offset of
2929 * the block that contains the top-left pixel of the locked rectangle. */
2930 map_desc->data = base_memory
2931 + ((rect->top / format->block_height) * map_desc->row_pitch)
2932 + ((rect->left / format->block_width) * format->block_byte_count);
2934 else
2936 map_desc->data = base_memory
2937 + (map_desc->row_pitch * rect->top)
2938 + (rect->left * format->byte_count);
2940 surface->lockedRect.left = rect->left;
2941 surface->lockedRect.top = rect->top;
2942 surface->lockedRect.right = rect->right;
2943 surface->lockedRect.bottom = rect->bottom;
2946 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2947 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2949 return WINED3D_OK;
2952 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
2954 HRESULT hr;
2956 TRACE("surface %p, dc %p.\n", surface, dc);
2958 /* Give more detailed info for ddraw. */
2959 if (surface->flags & SFLAG_DCINUSE)
2960 return WINEDDERR_DCALREADYCREATED;
2962 /* Can't GetDC if the surface is locked. */
2963 if (surface->resource.map_count)
2964 return WINED3DERR_INVALIDCALL;
2966 /* Create a DIB section if there isn't a dc yet. */
2967 if (!surface->hDC)
2969 if (surface->flags & SFLAG_CLIENT)
2971 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
2972 surface_release_client_storage(surface);
2974 hr = surface_create_dib_section(surface);
2975 if (FAILED(hr))
2976 return WINED3DERR_INVALIDCALL;
2977 if (!(surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2978 || surface->flags & SFLAG_PIN_SYSMEM
2979 || surface->pbo))
2980 surface->resource.map_binding = WINED3D_LOCATION_DIB;
2983 surface_load_location(surface, WINED3D_LOCATION_DIB);
2984 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
2986 surface->flags |= SFLAG_DCINUSE;
2987 surface->resource.map_count++;
2989 *dc = surface->hDC;
2990 TRACE("Returning dc %p.\n", *dc);
2992 return WINED3D_OK;
2995 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
2997 TRACE("surface %p, dc %p.\n", surface, dc);
2999 if (!(surface->flags & SFLAG_DCINUSE))
3000 return WINEDDERR_NODC;
3002 if (surface->hDC != dc)
3004 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3005 dc, surface->hDC);
3006 return WINEDDERR_NODC;
3009 surface->resource.map_count--;
3010 surface->flags &= ~SFLAG_DCINUSE;
3012 if (surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY || (surface->flags & SFLAG_PIN_SYSMEM
3013 && surface->resource.map_binding != WINED3D_LOCATION_DIB))
3015 /* The game Salammbo modifies the surface contents without mapping the surface between
3016 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
3017 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
3018 * Do not only copy the DIB to the map location, but also make sure the map location is
3019 * copied back to the DIB in the next getdc call.
3021 * The same consideration applies to user memory surfaces. */
3022 surface_load_location(surface, surface->resource.map_binding);
3023 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
3026 return WINED3D_OK;
3029 static void read_from_framebuffer(struct wined3d_surface *surface, DWORD dst_location)
3031 struct wined3d_device *device = surface->resource.device;
3032 const struct wined3d_gl_info *gl_info;
3033 struct wined3d_context *context;
3034 BYTE *mem;
3035 BYTE *row, *top, *bottom;
3036 int i;
3037 BOOL srcIsUpsideDown;
3038 struct wined3d_bo_address data;
3040 surface_get_memory(surface, &data, dst_location);
3042 context = context_acquire(device, surface);
3043 context_apply_blit_state(context, device);
3044 gl_info = context->gl_info;
3046 /* Select the correct read buffer, and give some debug output.
3047 * There is no need to keep track of the current read buffer or reset it, every part of the code
3048 * that reads sets the read buffer as desired.
3050 if (wined3d_resource_is_offscreen(&surface->container->resource))
3052 /* Mapping the primary render target which is not on a swapchain.
3053 * Read from the back buffer. */
3054 TRACE("Mapping offscreen render target.\n");
3055 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3056 srcIsUpsideDown = TRUE;
3058 else
3060 /* Onscreen surfaces are always part of a swapchain */
3061 GLenum buffer = surface_get_gl_buffer(surface);
3062 TRACE("Mapping %#x buffer.\n", buffer);
3063 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
3064 checkGLcall("glReadBuffer");
3065 srcIsUpsideDown = FALSE;
3068 if (data.buffer_object)
3070 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
3071 checkGLcall("glBindBufferARB");
3074 /* Setup pixel store pack state -- to glReadPixels into the correct place */
3075 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
3076 checkGLcall("glPixelStorei");
3078 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
3079 surface->resource.width, surface->resource.height,
3080 surface->resource.format->glFormat,
3081 surface->resource.format->glType, data.addr);
3082 checkGLcall("glReadPixels");
3084 /* Reset previous pixel store pack state */
3085 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
3086 checkGLcall("glPixelStorei");
3088 if (!srcIsUpsideDown)
3090 /* glReadPixels returns the image upside down, and there is no way to prevent this.
3091 * Flip the lines in software. */
3092 UINT pitch = wined3d_surface_get_pitch(surface);
3094 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
3095 goto error;
3097 if (data.buffer_object)
3099 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3100 checkGLcall("glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB)");
3102 else
3103 mem = data.addr;
3105 top = mem;
3106 bottom = mem + pitch * (surface->resource.height - 1);
3107 for (i = 0; i < surface->resource.height / 2; i++)
3109 memcpy(row, top, pitch);
3110 memcpy(top, bottom, pitch);
3111 memcpy(bottom, row, pitch);
3112 top += pitch;
3113 bottom -= pitch;
3115 HeapFree(GetProcessHeap(), 0, row);
3117 if (data.buffer_object)
3118 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB));
3121 error:
3122 if (data.buffer_object)
3124 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
3125 checkGLcall("glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0)");
3128 context_release(context);
3131 /* Read the framebuffer contents into a texture. Note that this function
3132 * doesn't do any kind of flipping. Using this on an onscreen surface will
3133 * result in a flipped D3D texture. */
3134 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
3136 struct wined3d_device *device = surface->resource.device;
3137 const struct wined3d_gl_info *gl_info;
3138 struct wined3d_context *context;
3140 context = context_acquire(device, surface);
3141 gl_info = context->gl_info;
3142 device_invalidate_state(device, STATE_FRAMEBUFFER);
3144 surface_prepare_texture(surface, context, srgb);
3145 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3147 TRACE("Reading back offscreen render target %p.\n", surface);
3149 if (wined3d_resource_is_offscreen(&surface->container->resource))
3150 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3151 else
3152 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
3153 checkGLcall("glReadBuffer");
3155 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
3156 0, 0, 0, 0, surface->resource.width, surface->resource.height);
3157 checkGLcall("glCopyTexSubImage2D");
3159 context_release(context);
3162 /* Context activation is done by the caller. */
3163 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
3164 struct wined3d_context *context, BOOL srgb)
3166 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
3167 enum wined3d_conversion_type convert;
3168 struct wined3d_format format;
3170 if (surface->flags & alloc_flag) return;
3172 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
3173 if (convert != WINED3D_CT_NONE || format.convert)
3174 surface->flags |= SFLAG_CONVERTED;
3175 else surface->flags &= ~SFLAG_CONVERTED;
3177 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3178 surface_allocate_surface(surface, context->gl_info, &format, srgb);
3179 surface->flags |= alloc_flag;
3182 /* Context activation is done by the caller. */
3183 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
3185 struct wined3d_texture *texture = surface->container;
3186 UINT sub_count = texture->level_count * texture->layer_count;
3187 UINT i;
3189 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
3191 for (i = 0; i < sub_count; ++i)
3193 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
3194 surface_prepare_texture_internal(s, context, srgb);
3197 return;
3200 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
3202 if (multisample)
3204 if (surface->rb_multisample)
3205 return;
3207 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
3208 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
3209 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
3210 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
3211 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
3213 else
3215 if (surface->rb_resolved)
3216 return;
3218 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
3219 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
3220 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
3221 surface->pow2Width, surface->pow2Height);
3222 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
3226 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
3228 /* FIXME: Is this really how color keys are supposed to work? I think it
3229 * makes more sense to compare the individual channels. */
3230 return color >= color_key->color_space_low_value
3231 && color <= color_key->color_space_high_value;
3234 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
3235 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
3237 const BYTE *source;
3238 BYTE *dest;
3240 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
3241 src, dst, pitch, width, height, outpitch, conversion_type, surface);
3243 switch (conversion_type)
3245 case WINED3D_CT_NONE:
3247 memcpy(dst, src, pitch * height);
3248 break;
3251 case WINED3D_CT_PALETTED:
3252 if (surface->container->swapchain && surface->container->swapchain->palette)
3254 unsigned int x, y;
3255 const struct wined3d_palette *palette = surface->container->swapchain->palette;
3256 for (y = 0; y < height; y++)
3258 source = src + pitch * y;
3259 dest = dst + outpitch * y;
3260 for (x = 0; x < width; x++)
3262 BYTE color = *source++;
3263 *dest++ = palette->colors[color].rgbRed;
3264 *dest++ = palette->colors[color].rgbGreen;
3265 *dest++ = palette->colors[color].rgbBlue;
3266 *dest++ = 0;
3270 else
3272 /* This should probably use the system palette, but unless
3273 * the X server is running in P8 mode there is no such thing.
3274 * The probably best solution is to set the fixed 20 colors
3275 * from the default windows palette and set the rest to black,
3276 * white, or some ugly pink. For now use black for the entire
3277 * palette. Don't use pink everywhere. Age of Empires 2 draws
3278 * a front buffer filled with zeroes without a palette when
3279 * starting and we don't want the screen to flash in an ugly
3280 * color. */
3281 FIXME("P8 surface loaded without a palette.\n");
3282 memset(dst, 0, height * outpitch);
3285 break;
3287 case WINED3D_CT_CK_565:
3289 /* Converting the 565 format in 5551 packed to emulate color-keying.
3291 Note : in all these conversion, it would be best to average the averaging
3292 pixels to get the color of the pixel that will be color-keyed to
3293 prevent 'color bleeding'. This will be done later on if ever it is
3294 too visible.
3296 Note2: Nvidia documents say that their driver does not support alpha + color keying
3297 on the same surface and disables color keying in such a case
3299 unsigned int x, y;
3300 const WORD *Source;
3301 WORD *Dest;
3303 TRACE("Color keyed 565\n");
3305 for (y = 0; y < height; y++) {
3306 Source = (const WORD *)(src + y * pitch);
3307 Dest = (WORD *) (dst + y * outpitch);
3308 for (x = 0; x < width; x++ ) {
3309 WORD color = *Source++;
3310 *Dest = ((color & 0xffc0) | ((color & 0x1f) << 1));
3311 if (!color_in_range(&surface->container->src_blt_color_key, color))
3312 *Dest |= 0x0001;
3313 Dest++;
3317 break;
3319 case WINED3D_CT_CK_5551:
3321 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
3322 unsigned int x, y;
3323 const WORD *Source;
3324 WORD *Dest;
3325 TRACE("Color keyed 5551\n");
3326 for (y = 0; y < height; y++) {
3327 Source = (const WORD *)(src + y * pitch);
3328 Dest = (WORD *) (dst + y * outpitch);
3329 for (x = 0; x < width; x++ ) {
3330 WORD color = *Source++;
3331 *Dest = color;
3332 if (!color_in_range(&surface->container->src_blt_color_key, color))
3333 *Dest |= (1 << 15);
3334 else
3335 *Dest &= ~(1 << 15);
3336 Dest++;
3340 break;
3342 case WINED3D_CT_CK_RGB24:
3344 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
3345 unsigned int x, y;
3346 for (y = 0; y < height; y++)
3348 source = src + pitch * y;
3349 dest = dst + outpitch * y;
3350 for (x = 0; x < width; x++) {
3351 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
3352 DWORD dstcolor = color << 8;
3353 if (!color_in_range(&surface->container->src_blt_color_key, color))
3354 dstcolor |= 0xff;
3355 *(DWORD*)dest = dstcolor;
3356 source += 3;
3357 dest += 4;
3361 break;
3363 case WINED3D_CT_RGB32_888:
3365 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
3366 unsigned int x, y;
3367 for (y = 0; y < height; y++)
3369 source = src + pitch * y;
3370 dest = dst + outpitch * y;
3371 for (x = 0; x < width; x++) {
3372 DWORD color = 0xffffff & *(const DWORD*)source;
3373 DWORD dstcolor = color << 8;
3374 if (!color_in_range(&surface->container->src_blt_color_key, color))
3375 dstcolor |= 0xff;
3376 *(DWORD*)dest = dstcolor;
3377 source += 4;
3378 dest += 4;
3382 break;
3384 case WINED3D_CT_CK_ARGB32:
3386 unsigned int x, y;
3387 for (y = 0; y < height; ++y)
3389 source = src + pitch * y;
3390 dest = dst + outpitch * y;
3391 for (x = 0; x < width; ++x)
3393 DWORD color = *(const DWORD *)source;
3394 if (color_in_range(&surface->container->src_blt_color_key, color))
3395 color &= ~0xff000000;
3396 *(DWORD*)dest = color;
3397 source += 4;
3398 dest += 4;
3402 break;
3404 default:
3405 ERR("Unsupported conversion type %#x.\n", conversion_type);
3407 return WINED3D_OK;
3410 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
3412 if (front->container->level_count != 1 || front->container->layer_count != 1
3413 || back->container->level_count != 1 || back->container->layer_count != 1)
3414 ERR("Flip between surfaces %p and %p not supported.\n", front, back);
3416 /* Flip the surface contents */
3417 /* Flip the DC */
3419 HDC tmp;
3420 tmp = front->hDC;
3421 front->hDC = back->hDC;
3422 back->hDC = tmp;
3425 /* Flip the DIBsection */
3427 HBITMAP tmp = front->dib.DIBsection;
3428 front->dib.DIBsection = back->dib.DIBsection;
3429 back->dib.DIBsection = tmp;
3432 /* Flip the surface data */
3434 void* tmp;
3436 tmp = front->dib.bitmap_data;
3437 front->dib.bitmap_data = back->dib.bitmap_data;
3438 back->dib.bitmap_data = tmp;
3440 tmp = front->resource.heap_memory;
3441 front->resource.heap_memory = back->resource.heap_memory;
3442 back->resource.heap_memory = tmp;
3445 /* Flip the PBO */
3447 GLuint tmp_pbo = front->pbo;
3448 front->pbo = back->pbo;
3449 back->pbo = tmp_pbo;
3452 /* Flip the opengl texture */
3454 GLuint tmp;
3456 tmp = back->container->texture_rgb.name;
3457 back->container->texture_rgb.name = front->container->texture_rgb.name;
3458 front->container->texture_rgb.name = tmp;
3460 tmp = back->container->texture_srgb.name;
3461 back->container->texture_srgb.name = front->container->texture_srgb.name;
3462 front->container->texture_srgb.name = tmp;
3464 tmp = back->rb_multisample;
3465 back->rb_multisample = front->rb_multisample;
3466 front->rb_multisample = tmp;
3468 tmp = back->rb_resolved;
3469 back->rb_resolved = front->rb_resolved;
3470 front->rb_resolved = tmp;
3472 resource_unload(&back->resource);
3473 resource_unload(&front->resource);
3477 DWORD tmp_flags = back->flags;
3478 back->flags = front->flags;
3479 front->flags = tmp_flags;
3481 tmp_flags = back->locations;
3482 back->locations = front->locations;
3483 front->locations = tmp_flags;
3487 /* Does a direct frame buffer -> texture copy. Stretching is done with single
3488 * pixel copy calls. */
3489 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3490 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3492 struct wined3d_device *device = dst_surface->resource.device;
3493 const struct wined3d_gl_info *gl_info;
3494 float xrel, yrel;
3495 struct wined3d_context *context;
3496 BOOL upsidedown = FALSE;
3497 RECT dst_rect = *dst_rect_in;
3499 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3500 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3502 if(dst_rect.top > dst_rect.bottom) {
3503 UINT tmp = dst_rect.bottom;
3504 dst_rect.bottom = dst_rect.top;
3505 dst_rect.top = tmp;
3506 upsidedown = TRUE;
3509 context = context_acquire(device, src_surface);
3510 gl_info = context->gl_info;
3511 context_apply_blit_state(context, device);
3512 wined3d_texture_load(dst_surface->container, context, FALSE);
3514 /* Bind the target texture */
3515 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
3516 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
3518 TRACE("Reading from an offscreen target\n");
3519 upsidedown = !upsidedown;
3520 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3522 else
3524 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3526 checkGLcall("glReadBuffer");
3528 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3529 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3531 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3533 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3535 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3536 ERR("Texture filtering not supported in direct blit.\n");
3538 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3539 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3541 ERR("Texture filtering not supported in direct blit\n");
3544 if (upsidedown
3545 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3546 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3548 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
3549 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3550 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3551 src_rect->left, src_surface->resource.height - src_rect->bottom,
3552 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3554 else
3556 LONG row;
3557 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
3558 /* I have to process this row by row to swap the image,
3559 * otherwise it would be upside down, so stretching in y direction
3560 * doesn't cost extra time
3562 * However, stretching in x direction can be avoided if not necessary
3564 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3565 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3567 /* Well, that stuff works, but it's very slow.
3568 * find a better way instead
3570 LONG col;
3572 for (col = dst_rect.left; col < dst_rect.right; ++col)
3574 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3575 dst_rect.left + col /* x offset */, row /* y offset */,
3576 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3579 else
3581 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3582 dst_rect.left /* x offset */, row /* y offset */,
3583 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3587 checkGLcall("glCopyTexSubImage2D");
3589 context_release(context);
3591 /* The texture is now most up to date - If the surface is a render target
3592 * and has a drawable, this path is never entered. */
3593 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3594 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3597 /* Uses the hardware to stretch and flip the image */
3598 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3599 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3601 struct wined3d_device *device = dst_surface->resource.device;
3602 GLuint src, backup = 0;
3603 float left, right, top, bottom; /* Texture coordinates */
3604 UINT fbwidth = src_surface->resource.width;
3605 UINT fbheight = src_surface->resource.height;
3606 const struct wined3d_gl_info *gl_info;
3607 struct wined3d_context *context;
3608 GLenum drawBuffer = GL_BACK;
3609 GLenum texture_target;
3610 BOOL noBackBufferBackup;
3611 BOOL src_offscreen;
3612 BOOL upsidedown = FALSE;
3613 RECT dst_rect = *dst_rect_in;
3615 TRACE("Using hwstretch blit\n");
3616 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3617 context = context_acquire(device, src_surface);
3618 gl_info = context->gl_info;
3619 context_apply_blit_state(context, device);
3620 wined3d_texture_load(dst_surface->container, context, FALSE);
3622 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
3623 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3624 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3626 /* Get it a description */
3627 wined3d_texture_load(src_surface->container, context, FALSE);
3630 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3631 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3633 if (context->aux_buffers >= 2)
3635 /* Got more than one aux buffer? Use the 2nd aux buffer */
3636 drawBuffer = GL_AUX1;
3638 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3640 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3641 drawBuffer = GL_AUX0;
3644 if (noBackBufferBackup)
3646 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3647 checkGLcall("glGenTextures");
3648 context_bind_texture(context, GL_TEXTURE_2D, backup);
3649 texture_target = GL_TEXTURE_2D;
3651 else
3653 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3654 * we are reading from the back buffer, the backup can be used as source texture
3656 texture_target = src_surface->texture_target;
3657 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3658 gl_info->gl_ops.gl.p_glEnable(texture_target);
3659 checkGLcall("glEnable(texture_target)");
3661 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3662 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3665 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3666 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3668 if(dst_rect.top > dst_rect.bottom) {
3669 UINT tmp = dst_rect.bottom;
3670 dst_rect.bottom = dst_rect.top;
3671 dst_rect.top = tmp;
3672 upsidedown = TRUE;
3675 if (src_offscreen)
3677 TRACE("Reading from an offscreen target\n");
3678 upsidedown = !upsidedown;
3679 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3681 else
3683 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3686 /* TODO: Only back up the part that will be overwritten */
3687 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3689 checkGLcall("glCopyTexSubImage2D");
3691 /* No issue with overriding these - the sampler is dirty due to blit usage */
3692 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3693 wined3d_gl_mag_filter(magLookup, filter));
3694 checkGLcall("glTexParameteri");
3695 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3696 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
3697 checkGLcall("glTexParameteri");
3699 if (!src_surface->container->swapchain
3700 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
3702 src = backup ? backup : src_surface->container->texture_rgb.name;
3704 else
3706 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3707 checkGLcall("glReadBuffer(GL_FRONT)");
3709 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3710 checkGLcall("glGenTextures(1, &src)");
3711 context_bind_texture(context, GL_TEXTURE_2D, src);
3713 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3714 * out for power of 2 sizes
3716 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3717 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3718 checkGLcall("glTexImage2D");
3719 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3721 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3722 checkGLcall("glTexParameteri");
3723 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3724 checkGLcall("glTexParameteri");
3726 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3727 checkGLcall("glReadBuffer(GL_BACK)");
3729 if (texture_target != GL_TEXTURE_2D)
3731 gl_info->gl_ops.gl.p_glDisable(texture_target);
3732 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3733 texture_target = GL_TEXTURE_2D;
3736 checkGLcall("glEnd and previous");
3738 left = src_rect->left;
3739 right = src_rect->right;
3741 if (!upsidedown)
3743 top = src_surface->resource.height - src_rect->top;
3744 bottom = src_surface->resource.height - src_rect->bottom;
3746 else
3748 top = src_surface->resource.height - src_rect->bottom;
3749 bottom = src_surface->resource.height - src_rect->top;
3752 if (src_surface->flags & SFLAG_NORMCOORD)
3754 left /= src_surface->pow2Width;
3755 right /= src_surface->pow2Width;
3756 top /= src_surface->pow2Height;
3757 bottom /= src_surface->pow2Height;
3760 /* draw the source texture stretched and upside down. The correct surface is bound already */
3761 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3762 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3764 context_set_draw_buffer(context, drawBuffer);
3765 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3767 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3768 /* bottom left */
3769 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3770 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3772 /* top left */
3773 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3774 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3776 /* top right */
3777 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3778 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3780 /* bottom right */
3781 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3782 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3783 gl_info->gl_ops.gl.p_glEnd();
3784 checkGLcall("glEnd and previous");
3786 if (texture_target != dst_surface->texture_target)
3788 gl_info->gl_ops.gl.p_glDisable(texture_target);
3789 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3790 texture_target = dst_surface->texture_target;
3793 /* Now read the stretched and upside down image into the destination texture */
3794 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
3795 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
3797 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3798 0, 0, /* We blitted the image to the origin */
3799 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3800 checkGLcall("glCopyTexSubImage2D");
3802 if (drawBuffer == GL_BACK)
3804 /* Write the back buffer backup back. */
3805 if (backup)
3807 if (texture_target != GL_TEXTURE_2D)
3809 gl_info->gl_ops.gl.p_glDisable(texture_target);
3810 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3811 texture_target = GL_TEXTURE_2D;
3813 context_bind_texture(context, GL_TEXTURE_2D, backup);
3815 else
3817 if (texture_target != src_surface->texture_target)
3819 gl_info->gl_ops.gl.p_glDisable(texture_target);
3820 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3821 texture_target = src_surface->texture_target;
3823 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3826 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3827 /* top left */
3828 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3829 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3831 /* bottom left */
3832 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3833 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3835 /* bottom right */
3836 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3837 (float)fbheight / (float)src_surface->pow2Height);
3838 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3840 /* top right */
3841 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3842 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3843 gl_info->gl_ops.gl.p_glEnd();
3845 gl_info->gl_ops.gl.p_glDisable(texture_target);
3846 checkGLcall("glDisable(texture_target)");
3848 /* Cleanup */
3849 if (src != src_surface->container->texture_rgb.name && src != backup)
3851 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3852 checkGLcall("glDeleteTextures(1, &src)");
3854 if (backup)
3856 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3857 checkGLcall("glDeleteTextures(1, &backup)");
3860 if (wined3d_settings.strict_draw_ordering)
3861 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3863 context_release(context);
3865 /* The texture is now most up to date - If the surface is a render target
3866 * and has a drawable, this path is never entered. */
3867 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3868 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3871 /* Front buffer coordinates are always full screen coordinates, but our GL
3872 * drawable is limited to the window's client area. The sysmem and texture
3873 * copies do have the full screen size. Note that GL has a bottom-left
3874 * origin, while D3D has a top-left origin. */
3875 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3877 UINT drawable_height;
3879 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3881 POINT offset = {0, 0};
3882 RECT windowsize;
3884 ScreenToClient(window, &offset);
3885 OffsetRect(rect, offset.x, offset.y);
3887 GetClientRect(window, &windowsize);
3888 drawable_height = windowsize.bottom - windowsize.top;
3890 else
3892 drawable_height = surface->resource.height;
3895 rect->top = drawable_height - rect->top;
3896 rect->bottom = drawable_height - rect->bottom;
3899 static void surface_blt_to_drawable(const struct wined3d_device *device,
3900 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3901 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3902 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3904 const struct wined3d_gl_info *gl_info;
3905 struct wined3d_context *context;
3906 RECT src_rect, dst_rect;
3908 src_rect = *src_rect_in;
3909 dst_rect = *dst_rect_in;
3911 context = context_acquire(device, dst_surface);
3912 gl_info = context->gl_info;
3914 /* Make sure the surface is up-to-date. This should probably use
3915 * surface_load_location() and worry about the destination surface too,
3916 * unless we're overwriting it completely. */
3917 wined3d_texture_load(src_surface->container, context, FALSE);
3919 /* Activate the destination context, set it up for blitting */
3920 context_apply_blit_state(context, device);
3922 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3923 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3925 device->blitter->set_shader(device->blit_priv, context, src_surface);
3927 if (alpha_test)
3929 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3930 checkGLcall("glEnable(GL_ALPHA_TEST)");
3932 /* For P8 surfaces, the alpha component contains the palette index.
3933 * Which means that the colorkey is one of the palette entries. In
3934 * other cases pixels that should be masked away have alpha set to 0. */
3935 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3936 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3937 (float)src_surface->container->src_blt_color_key.color_space_low_value / 256.0f);
3938 else
3939 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3940 checkGLcall("glAlphaFunc");
3942 else
3944 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3945 checkGLcall("glDisable(GL_ALPHA_TEST)");
3948 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3950 if (alpha_test)
3952 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3953 checkGLcall("glDisable(GL_ALPHA_TEST)");
3956 /* Leave the opengl state valid for blitting */
3957 device->blitter->unset_shader(context->gl_info);
3959 if (wined3d_settings.strict_draw_ordering
3960 || (dst_surface->container->swapchain
3961 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3962 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3964 context_release(context);
3967 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3969 struct wined3d_device *device = s->resource.device;
3970 const struct blit_shader *blitter;
3972 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
3973 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
3974 if (!blitter)
3976 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3977 return WINED3DERR_INVALIDCALL;
3980 return blitter->color_fill(device, s, rect, color);
3983 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3984 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3985 enum wined3d_texture_filter_type filter)
3987 struct wined3d_device *device = dst_surface->resource.device;
3988 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3989 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3990 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3992 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3993 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3994 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3996 /* Get the swapchain. One of the surfaces has to be a primary surface */
3997 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3999 WARN("Destination is in sysmem, rejecting gl blt\n");
4000 return WINED3DERR_INVALIDCALL;
4003 dst_swapchain = dst_surface->container->swapchain;
4005 if (src_surface)
4007 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4009 WARN("Src is in sysmem, rejecting gl blt\n");
4010 return WINED3DERR_INVALIDCALL;
4013 src_swapchain = src_surface->container->swapchain;
4015 else
4017 src_swapchain = NULL;
4020 /* Early sort out of cases where no render target is used */
4021 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
4023 TRACE("No surface is render target, not using hardware blit.\n");
4024 return WINED3DERR_INVALIDCALL;
4027 /* No destination color keying supported */
4028 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
4030 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
4031 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
4032 return WINED3DERR_INVALIDCALL;
4035 if (dst_swapchain && dst_swapchain == src_swapchain)
4037 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
4038 return WINED3DERR_INVALIDCALL;
4041 if (dst_swapchain && src_swapchain)
4043 FIXME("Implement hardware blit between two different swapchains\n");
4044 return WINED3DERR_INVALIDCALL;
4047 if (dst_swapchain)
4049 /* Handled with regular texture -> swapchain blit */
4050 if (src_surface == rt)
4051 TRACE("Blit from active render target to a swapchain\n");
4053 else if (src_swapchain && dst_surface == rt)
4055 FIXME("Implement blit from a swapchain to the active render target\n");
4056 return WINED3DERR_INVALIDCALL;
4059 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
4061 /* Blit from render target to texture */
4062 BOOL stretchx;
4064 /* P8 read back is not implemented */
4065 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
4066 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
4068 TRACE("P8 read back not supported by frame buffer to texture blit\n");
4069 return WINED3DERR_INVALIDCALL;
4072 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
4074 TRACE("Color keying not supported by frame buffer to texture blit\n");
4075 return WINED3DERR_INVALIDCALL;
4076 /* Destination color key is checked above */
4079 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
4080 stretchx = TRUE;
4081 else
4082 stretchx = FALSE;
4084 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
4085 * flip the image nor scale it.
4087 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
4088 * -> If the app wants an image width an unscaled width, copy it line per line
4089 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
4090 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
4091 * back buffer. This is slower than reading line per line, thus not used for flipping
4092 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
4093 * pixel by pixel. */
4094 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
4095 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
4097 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
4098 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
4100 else
4102 TRACE("Using hardware stretching to flip / stretch the texture.\n");
4103 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
4106 surface_evict_sysmem(dst_surface);
4108 return WINED3D_OK;
4110 else if (src_surface)
4112 /* Blit from offscreen surface to render target */
4113 struct wined3d_color_key old_blt_key = src_surface->container->src_blt_color_key;
4114 DWORD old_color_key_flags = src_surface->container->color_key_flags;
4116 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4118 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4119 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
4120 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
4122 FIXME("Unsupported blit operation falling back to software\n");
4123 return WINED3DERR_INVALIDCALL;
4126 /* Color keying: Check if we have to do a color keyed blt,
4127 * and if not check if a color key is activated.
4129 * Just modify the color keying parameters in the surface and restore them afterwards
4130 * The surface keeps track of the color key last used to load the opengl surface.
4131 * PreLoad will catch the change to the flags and color key and reload if necessary.
4133 if (flags & WINEDDBLT_KEYSRC)
4135 /* Use color key from surface */
4137 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4139 /* Use color key from DDBltFx */
4140 src_surface->container->color_key_flags |= WINEDDSD_CKSRCBLT;
4141 src_surface->container->src_blt_color_key = DDBltFx->ddckSrcColorkey;
4143 else
4145 /* Do not use color key */
4146 src_surface->container->color_key_flags &= ~WINEDDSD_CKSRCBLT;
4149 surface_blt_to_drawable(device, filter,
4150 flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_ALPHATEST),
4151 src_surface, src_rect, dst_surface, dst_rect);
4153 /* Restore the color key parameters */
4154 src_surface->container->color_key_flags = old_color_key_flags;
4155 src_surface->container->src_blt_color_key = old_blt_key;
4157 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
4158 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
4160 return WINED3D_OK;
4163 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
4164 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
4165 return WINED3DERR_INVALIDCALL;
4168 /* Context activation is done by the caller. */
4169 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
4170 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
4172 struct wined3d_device *device = surface->resource.device;
4173 const struct wined3d_gl_info *gl_info = context->gl_info;
4174 GLint compare_mode = GL_NONE;
4175 struct blt_info info;
4176 GLint old_binding = 0;
4177 RECT rect;
4179 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4181 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
4182 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
4183 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4184 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
4185 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
4186 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
4187 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
4188 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
4189 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4190 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
4191 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
4193 SetRect(&rect, 0, h, w, 0);
4194 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
4195 context_active_texture(context, context->gl_info, 0);
4196 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
4197 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
4198 if (gl_info->supported[ARB_SHADOW])
4200 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
4201 if (compare_mode != GL_NONE)
4202 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
4205 device->shader_backend->shader_select_depth_blt(device->shader_priv,
4206 gl_info, info.tex_type, &surface->ds_current_size);
4208 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
4209 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
4210 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
4211 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
4212 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
4213 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
4214 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
4215 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
4216 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
4217 gl_info->gl_ops.gl.p_glEnd();
4219 if (compare_mode != GL_NONE)
4220 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
4221 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
4223 gl_info->gl_ops.gl.p_glPopAttrib();
4225 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
4228 void surface_modify_ds_location(struct wined3d_surface *surface,
4229 DWORD location, UINT w, UINT h)
4231 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
4233 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
4234 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
4235 wined3d_texture_set_dirty(surface->container);
4237 surface->ds_current_size.cx = w;
4238 surface->ds_current_size.cy = h;
4239 surface->locations = location;
4242 /* Context activation is done by the caller. */
4243 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4245 const struct wined3d_gl_info *gl_info = context->gl_info;
4246 struct wined3d_device *device = surface->resource.device;
4247 GLsizei w, h;
4249 TRACE("surface %p, new location %#x.\n", surface, location);
4251 /* TODO: Make this work for modes other than FBO */
4252 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4254 if (!(surface->locations & location))
4256 w = surface->ds_current_size.cx;
4257 h = surface->ds_current_size.cy;
4258 surface->ds_current_size.cx = 0;
4259 surface->ds_current_size.cy = 0;
4261 else
4263 w = surface->resource.width;
4264 h = surface->resource.height;
4267 if (surface->ds_current_size.cx == surface->resource.width
4268 && surface->ds_current_size.cy == surface->resource.height)
4270 TRACE("Location (%#x) is already up to date.\n", location);
4271 return;
4274 if (surface->current_renderbuffer)
4276 FIXME("Not supported with fixed up depth stencil.\n");
4277 return;
4280 if (surface->locations & WINED3D_LOCATION_DISCARDED)
4282 TRACE("Surface was discarded, no need copy data.\n");
4283 switch (location)
4285 case WINED3D_LOCATION_TEXTURE_RGB:
4286 surface_prepare_texture(surface, context, FALSE);
4287 break;
4288 case WINED3D_LOCATION_RB_MULTISAMPLE:
4289 surface_prepare_rb(surface, gl_info, TRUE);
4290 break;
4291 case WINED3D_LOCATION_DRAWABLE:
4292 /* Nothing to do */
4293 break;
4294 default:
4295 FIXME("Unhandled location %#x\n", location);
4297 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
4298 surface->locations |= location;
4299 surface->ds_current_size.cx = surface->resource.width;
4300 surface->ds_current_size.cy = surface->resource.height;
4301 return;
4304 if (!surface->locations)
4306 FIXME("No up to date depth stencil location.\n");
4307 surface->locations |= location;
4308 surface->ds_current_size.cx = surface->resource.width;
4309 surface->ds_current_size.cy = surface->resource.height;
4310 return;
4313 if (location == WINED3D_LOCATION_TEXTURE_RGB)
4315 GLint old_binding = 0;
4316 GLenum bind_target;
4318 /* The render target is allowed to be smaller than the depth/stencil
4319 * buffer, so the onscreen depth/stencil buffer is potentially smaller
4320 * than the offscreen surface. Don't overwrite the offscreen surface
4321 * with undefined data. */
4322 w = min(w, context->swapchain->desc.backbuffer_width);
4323 h = min(h, context->swapchain->desc.backbuffer_height);
4325 TRACE("Copying onscreen depth buffer to depth texture.\n");
4327 if (!device->depth_blt_texture)
4328 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
4330 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4331 * directly on the FBO texture. That's because we need to flip. */
4332 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4333 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
4334 NULL, WINED3D_LOCATION_DRAWABLE);
4335 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4337 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4338 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4340 else
4342 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4343 bind_target = GL_TEXTURE_2D;
4345 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
4346 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
4347 * internal format, because the internal format might include stencil
4348 * data. In principle we should copy stencil data as well, but unless
4349 * the driver supports stencil export it's hard to do, and doesn't
4350 * seem to be needed in practice. If the hardware doesn't support
4351 * writing stencil data, the glCopyTexImage2D() call might trigger
4352 * software fallbacks. */
4353 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
4354 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4355 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4356 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4357 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4358 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4359 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4360 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
4362 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4363 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
4364 context_set_draw_buffer(context, GL_NONE);
4366 /* Do the actual blit */
4367 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
4368 checkGLcall("depth_blt");
4370 context_invalidate_state(context, STATE_FRAMEBUFFER);
4372 if (wined3d_settings.strict_draw_ordering)
4373 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4375 else if (location == WINED3D_LOCATION_DRAWABLE)
4377 TRACE("Copying depth texture to onscreen depth buffer.\n");
4379 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4380 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
4381 NULL, WINED3D_LOCATION_DRAWABLE);
4382 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
4383 0, surface->pow2Height - h, w, h, surface->texture_target);
4384 checkGLcall("depth_blt");
4386 context_invalidate_state(context, STATE_FRAMEBUFFER);
4388 if (wined3d_settings.strict_draw_ordering)
4389 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4391 else
4393 ERR("Invalid location (%#x) specified.\n", location);
4396 surface->locations |= location;
4397 surface->ds_current_size.cx = surface->resource.width;
4398 surface->ds_current_size.cy = surface->resource.height;
4401 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
4403 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4405 surface->locations |= location;
4408 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
4410 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4412 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4413 wined3d_texture_set_dirty(surface->container);
4414 surface->locations &= ~location;
4416 if (!surface->locations)
4417 ERR("Surface %p does not have any up to date location.\n", surface);
4420 static DWORD resource_access_from_location(DWORD location)
4422 switch (location)
4424 case WINED3D_LOCATION_SYSMEM:
4425 case WINED3D_LOCATION_USER_MEMORY:
4426 case WINED3D_LOCATION_DIB:
4427 case WINED3D_LOCATION_BUFFER:
4428 return WINED3D_RESOURCE_ACCESS_CPU;
4430 case WINED3D_LOCATION_DRAWABLE:
4431 case WINED3D_LOCATION_TEXTURE_SRGB:
4432 case WINED3D_LOCATION_TEXTURE_RGB:
4433 case WINED3D_LOCATION_RB_MULTISAMPLE:
4434 case WINED3D_LOCATION_RB_RESOLVED:
4435 return WINED3D_RESOURCE_ACCESS_GPU;
4437 default:
4438 FIXME("Unhandled location %#x.\n", location);
4439 return 0;
4443 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
4445 struct wined3d_device *device = surface->resource.device;
4446 struct wined3d_context *context;
4447 const struct wined3d_gl_info *gl_info;
4448 struct wined3d_bo_address dst, src;
4449 UINT size = surface->resource.size;
4451 surface_get_memory(surface, &dst, location);
4452 surface_get_memory(surface, &src, surface->locations);
4454 if (dst.buffer_object)
4456 context = context_acquire(device, NULL);
4457 gl_info = context->gl_info;
4458 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, dst.buffer_object));
4459 GL_EXTCALL(glBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, size, src.addr));
4460 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4461 checkGLcall("Upload PBO");
4462 context_release(context);
4463 return;
4465 if (src.buffer_object)
4467 context = context_acquire(device, NULL);
4468 gl_info = context->gl_info;
4469 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, src.buffer_object));
4470 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_PACK_BUFFER_ARB, 0, size, dst.addr));
4471 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4472 checkGLcall("Download PBO");
4473 context_release(context);
4474 return;
4476 memcpy(dst.addr, src.addr, size);
4479 static void surface_load_sysmem(struct wined3d_surface *surface,
4480 const struct wined3d_gl_info *gl_info, DWORD dst_location)
4482 if (surface->locations & surface_simple_locations)
4484 surface_copy_simple_location(surface, dst_location);
4485 return;
4488 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
4489 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4491 /* Download the surface to system memory. */
4492 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4494 struct wined3d_device *device = surface->resource.device;
4495 struct wined3d_context *context;
4497 /* TODO: Use already acquired context when possible. */
4498 context = context_acquire(device, NULL);
4500 wined3d_texture_bind_and_dirtify(surface->container, context,
4501 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
4502 surface_download_data(surface, gl_info, dst_location);
4504 context_release(context);
4506 return;
4509 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
4511 read_from_framebuffer(surface, dst_location);
4512 return;
4515 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
4516 surface, wined3d_debug_location(surface->locations));
4519 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
4520 const struct wined3d_gl_info *gl_info)
4522 RECT r;
4524 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
4525 && wined3d_resource_is_offscreen(&surface->container->resource))
4527 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
4528 return WINED3DERR_INVALIDCALL;
4531 surface_get_rect(surface, NULL, &r);
4532 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4533 surface_blt_to_drawable(surface->resource.device,
4534 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
4536 return WINED3D_OK;
4539 static HRESULT surface_load_texture(struct wined3d_surface *surface,
4540 const struct wined3d_gl_info *gl_info, BOOL srgb)
4542 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
4543 struct wined3d_device *device = surface->resource.device;
4544 enum wined3d_conversion_type convert;
4545 struct wined3d_context *context;
4546 UINT width, src_pitch, dst_pitch;
4547 struct wined3d_bo_address data;
4548 struct wined3d_format format;
4549 POINT dst_point = {0, 0};
4550 BYTE *mem = NULL;
4552 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
4553 && wined3d_resource_is_offscreen(&surface->container->resource)
4554 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
4556 surface_load_fb_texture(surface, srgb);
4558 return WINED3D_OK;
4561 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
4562 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
4563 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4564 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4565 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4567 if (srgb)
4568 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
4569 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
4570 else
4571 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
4572 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
4574 return WINED3D_OK;
4577 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
4578 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
4579 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4580 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4581 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4583 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
4584 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
4585 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
4586 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4588 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
4589 &rect, surface, dst_location, &rect);
4591 return WINED3D_OK;
4594 /* Upload from system memory */
4596 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
4597 TRUE /* We will use textures */, &format, &convert);
4599 if (srgb)
4601 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
4602 == WINED3D_LOCATION_TEXTURE_RGB)
4604 /* Performance warning... */
4605 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
4606 surface_prepare_map_memory(surface);
4607 surface_load_location(surface, surface->resource.map_binding);
4610 else
4612 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
4613 == WINED3D_LOCATION_TEXTURE_SRGB)
4615 /* Performance warning... */
4616 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
4617 surface_prepare_map_memory(surface);
4618 surface_load_location(surface, surface->resource.map_binding);
4622 if (!(surface->locations & surface_simple_locations))
4624 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
4625 /* Lets hope we get it from somewhere... */
4626 surface_prepare_system_memory(surface);
4627 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
4630 /* TODO: Use already acquired context when possible. */
4631 context = context_acquire(device, NULL);
4633 surface_prepare_texture(surface, context, srgb);
4634 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
4636 if (surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
4638 surface->flags |= SFLAG_GLCKEY;
4639 surface->gl_color_key = surface->container->src_blt_color_key;
4641 else surface->flags &= ~SFLAG_GLCKEY;
4643 width = surface->resource.width;
4644 src_pitch = wined3d_surface_get_pitch(surface);
4646 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
4647 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
4648 * called. */
4649 if ((convert != WINED3D_CT_NONE || format.convert) && surface->pbo)
4651 TRACE("Removing the pbo attached to surface %p.\n", surface);
4653 if (surface->flags & SFLAG_DIBSECTION)
4654 surface->resource.map_binding = WINED3D_LOCATION_DIB;
4655 else
4656 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
4658 surface_prepare_map_memory(surface);
4659 surface_load_location(surface, surface->resource.map_binding);
4660 surface_remove_pbo(surface, gl_info);
4663 surface_get_memory(surface, &data, surface->locations);
4664 if (format.convert)
4666 /* This code is entered for texture formats which need a fixup. */
4667 UINT height = surface->resource.height;
4669 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4670 dst_pitch = width * format.conv_byte_count;
4671 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4673 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4675 ERR("Out of memory (%u).\n", dst_pitch * height);
4676 context_release(context);
4677 return E_OUTOFMEMORY;
4679 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4680 dst_pitch, dst_pitch * height, width, height, 1);
4681 format.byte_count = format.conv_byte_count;
4682 src_pitch = dst_pitch;
4683 data.addr = mem;
4685 else if (convert != WINED3D_CT_NONE)
4687 /* This code is only entered for color keying fixups */
4688 UINT height = surface->resource.height;
4690 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4691 dst_pitch = width * format.conv_byte_count;
4692 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4694 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4696 ERR("Out of memory (%u).\n", dst_pitch * height);
4697 context_release(context);
4698 return E_OUTOFMEMORY;
4700 d3dfmt_convert_surface(data.addr, mem, src_pitch,
4701 width, height, dst_pitch, convert, surface);
4702 format.byte_count = format.conv_byte_count;
4703 src_pitch = dst_pitch;
4704 data.addr = mem;
4707 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
4709 context_release(context);
4711 HeapFree(GetProcessHeap(), 0, mem);
4713 return WINED3D_OK;
4716 static void surface_multisample_resolve(struct wined3d_surface *surface)
4718 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4720 if (!(surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE))
4721 ERR("Trying to resolve multisampled surface %p, but location WINED3D_LOCATION_RB_MULTISAMPLE not current.\n",
4722 surface);
4724 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
4725 surface, WINED3D_LOCATION_RB_MULTISAMPLE, &rect, surface, WINED3D_LOCATION_RB_RESOLVED, &rect);
4728 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
4730 struct wined3d_device *device = surface->resource.device;
4731 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4732 HRESULT hr;
4734 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4736 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4738 if (location == WINED3D_LOCATION_TEXTURE_RGB
4739 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4741 struct wined3d_context *context = context_acquire(device, NULL);
4742 surface_load_ds_location(surface, context, location);
4743 context_release(context);
4744 return WINED3D_OK;
4746 else if (location & surface->locations
4747 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
4749 /* Already up to date, nothing to do. */
4750 return WINED3D_OK;
4752 else
4754 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4755 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4756 return WINED3DERR_INVALIDCALL;
4760 if (surface->locations & location)
4762 TRACE("Location already up to date.\n");
4763 return WINED3D_OK;
4766 if (WARN_ON(d3d_surface))
4768 DWORD required_access = resource_access_from_location(location);
4769 if ((surface->resource.access_flags & required_access) != required_access)
4770 WARN("Operation requires %#x access, but surface only has %#x.\n",
4771 required_access, surface->resource.access_flags);
4774 if (!surface->locations)
4776 ERR("Surface %p does not have any up to date location.\n", surface);
4777 surface->flags |= SFLAG_LOST;
4778 return WINED3DERR_DEVICELOST;
4781 switch (location)
4783 case WINED3D_LOCATION_DIB:
4784 case WINED3D_LOCATION_USER_MEMORY:
4785 case WINED3D_LOCATION_SYSMEM:
4786 case WINED3D_LOCATION_BUFFER:
4787 surface_load_sysmem(surface, gl_info, location);
4788 break;
4790 case WINED3D_LOCATION_DRAWABLE:
4791 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
4792 return hr;
4793 break;
4795 case WINED3D_LOCATION_RB_RESOLVED:
4796 surface_multisample_resolve(surface);
4797 break;
4799 case WINED3D_LOCATION_TEXTURE_RGB:
4800 case WINED3D_LOCATION_TEXTURE_SRGB:
4801 if (FAILED(hr = surface_load_texture(surface, gl_info, location == WINED3D_LOCATION_TEXTURE_SRGB)))
4802 return hr;
4803 break;
4805 default:
4806 ERR("Don't know how to handle location %#x.\n", location);
4807 break;
4810 surface_validate_location(surface, location);
4812 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
4813 surface_evict_sysmem(surface);
4815 return WINED3D_OK;
4818 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
4819 /* Context activation is done by the caller. */
4820 static void ffp_blit_free(struct wined3d_device *device) { }
4822 /* Context activation is done by the caller. */
4823 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4825 const struct wined3d_gl_info *gl_info = context->gl_info;
4827 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
4828 checkGLcall("glEnable(target)");
4830 return WINED3D_OK;
4833 /* Context activation is done by the caller. */
4834 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
4836 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
4837 checkGLcall("glDisable(GL_TEXTURE_2D)");
4838 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4840 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4841 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4843 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4845 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
4846 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4850 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4851 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4852 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4854 switch (blit_op)
4856 case WINED3D_BLIT_OP_COLOR_BLIT:
4857 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4858 return FALSE;
4860 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4862 TRACE("Checking support for fixup:\n");
4863 dump_color_fixup_desc(src_format->color_fixup);
4866 /* We only support identity conversions. */
4867 if (!is_identity_fixup(src_format->color_fixup)
4868 || !is_identity_fixup(dst_format->color_fixup))
4870 TRACE("Fixups are not supported.\n");
4871 return FALSE;
4874 return TRUE;
4876 case WINED3D_BLIT_OP_COLOR_FILL:
4877 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
4878 return FALSE;
4880 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4882 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4883 return FALSE;
4885 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4887 TRACE("Color fill not supported\n");
4888 return FALSE;
4891 /* FIXME: We should reject color fills on formats with fixups,
4892 * but this would break P8 color fills for example. */
4894 return TRUE;
4896 case WINED3D_BLIT_OP_DEPTH_FILL:
4897 return TRUE;
4899 default:
4900 TRACE("Unsupported blit_op=%d\n", blit_op);
4901 return FALSE;
4905 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4906 const RECT *dst_rect, const struct wined3d_color *color)
4908 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4909 struct wined3d_rendertarget_view *view;
4910 struct wined3d_fb_state fb = {&view, NULL};
4911 HRESULT hr;
4913 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4914 NULL, &wined3d_null_parent_ops, &view)))
4916 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4917 return hr;
4920 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4921 wined3d_rendertarget_view_decref(view);
4923 return WINED3D_OK;
4926 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4927 const RECT *dst_rect, float depth)
4929 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4930 struct wined3d_fb_state fb = {NULL, NULL};
4931 HRESULT hr;
4933 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4934 NULL, &wined3d_null_parent_ops, &fb.depth_stencil)))
4936 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4937 return hr;
4940 device_clear_render_targets(device, 0, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4941 wined3d_rendertarget_view_decref(fb.depth_stencil);
4943 return WINED3D_OK;
4946 const struct blit_shader ffp_blit = {
4947 ffp_blit_alloc,
4948 ffp_blit_free,
4949 ffp_blit_set,
4950 ffp_blit_unset,
4951 ffp_blit_supported,
4952 ffp_blit_color_fill,
4953 ffp_blit_depth_fill,
4956 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4958 return WINED3D_OK;
4961 /* Context activation is done by the caller. */
4962 static void cpu_blit_free(struct wined3d_device *device)
4966 /* Context activation is done by the caller. */
4967 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4969 return WINED3D_OK;
4972 /* Context activation is done by the caller. */
4973 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4977 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4978 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4979 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4981 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4983 return TRUE;
4986 return FALSE;
4989 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4990 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4991 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4993 UINT row_block_count;
4994 const BYTE *src_row;
4995 BYTE *dst_row;
4996 UINT x, y;
4998 src_row = src_data;
4999 dst_row = dst_data;
5001 row_block_count = (update_w + format->block_width - 1) / format->block_width;
5003 if (!flags)
5005 for (y = 0; y < update_h; y += format->block_height)
5007 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
5008 src_row += src_pitch;
5009 dst_row += dst_pitch;
5012 return WINED3D_OK;
5015 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
5017 src_row += (((update_h / format->block_height) - 1) * src_pitch);
5019 switch (format->id)
5021 case WINED3DFMT_DXT1:
5022 for (y = 0; y < update_h; y += format->block_height)
5024 struct block
5026 WORD color[2];
5027 BYTE control_row[4];
5030 const struct block *s = (const struct block *)src_row;
5031 struct block *d = (struct block *)dst_row;
5033 for (x = 0; x < row_block_count; ++x)
5035 d[x].color[0] = s[x].color[0];
5036 d[x].color[1] = s[x].color[1];
5037 d[x].control_row[0] = s[x].control_row[3];
5038 d[x].control_row[1] = s[x].control_row[2];
5039 d[x].control_row[2] = s[x].control_row[1];
5040 d[x].control_row[3] = s[x].control_row[0];
5042 src_row -= src_pitch;
5043 dst_row += dst_pitch;
5045 return WINED3D_OK;
5047 case WINED3DFMT_DXT2:
5048 case WINED3DFMT_DXT3:
5049 for (y = 0; y < update_h; y += format->block_height)
5051 struct block
5053 WORD alpha_row[4];
5054 WORD color[2];
5055 BYTE control_row[4];
5058 const struct block *s = (const struct block *)src_row;
5059 struct block *d = (struct block *)dst_row;
5061 for (x = 0; x < row_block_count; ++x)
5063 d[x].alpha_row[0] = s[x].alpha_row[3];
5064 d[x].alpha_row[1] = s[x].alpha_row[2];
5065 d[x].alpha_row[2] = s[x].alpha_row[1];
5066 d[x].alpha_row[3] = s[x].alpha_row[0];
5067 d[x].color[0] = s[x].color[0];
5068 d[x].color[1] = s[x].color[1];
5069 d[x].control_row[0] = s[x].control_row[3];
5070 d[x].control_row[1] = s[x].control_row[2];
5071 d[x].control_row[2] = s[x].control_row[1];
5072 d[x].control_row[3] = s[x].control_row[0];
5074 src_row -= src_pitch;
5075 dst_row += dst_pitch;
5077 return WINED3D_OK;
5079 default:
5080 FIXME("Compressed flip not implemented for format %s.\n",
5081 debug_d3dformat(format->id));
5082 return E_NOTIMPL;
5086 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
5087 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
5089 return E_NOTIMPL;
5092 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5093 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
5094 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5096 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
5097 const struct wined3d_format *src_format, *dst_format;
5098 struct wined3d_texture *src_texture = NULL;
5099 struct wined3d_map_desc dst_map, src_map;
5100 const BYTE *sbase = NULL;
5101 HRESULT hr = WINED3D_OK;
5102 const BYTE *sbuf;
5103 BYTE *dbuf;
5104 int x, y;
5106 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5107 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5108 flags, fx, debug_d3dtexturefiltertype(filter));
5110 if (src_surface == dst_surface)
5112 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
5113 src_map = dst_map;
5114 src_format = dst_surface->resource.format;
5115 dst_format = src_format;
5117 else
5119 dst_format = dst_surface->resource.format;
5120 if (src_surface)
5122 if (dst_surface->resource.format->id != src_surface->resource.format->id)
5124 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
5126 /* The conv function writes a FIXME */
5127 WARN("Cannot convert source surface format to dest format.\n");
5128 goto release;
5130 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
5132 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
5133 src_format = src_surface->resource.format;
5135 else
5137 src_format = dst_format;
5140 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
5143 bpp = dst_surface->resource.format->byte_count;
5144 srcheight = src_rect->bottom - src_rect->top;
5145 srcwidth = src_rect->right - src_rect->left;
5146 dstheight = dst_rect->bottom - dst_rect->top;
5147 dstwidth = dst_rect->right - dst_rect->left;
5148 width = (dst_rect->right - dst_rect->left) * bpp;
5150 if (src_surface)
5151 sbase = (BYTE *)src_map.data
5152 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
5153 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
5154 if (src_surface != dst_surface)
5155 dbuf = dst_map.data;
5156 else
5157 dbuf = (BYTE *)dst_map.data
5158 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
5159 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
5161 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
5163 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
5165 if (src_surface == dst_surface)
5167 FIXME("Only plain blits supported on compressed surfaces.\n");
5168 hr = E_NOTIMPL;
5169 goto release;
5172 if (srcheight != dstheight || srcwidth != dstwidth)
5174 WARN("Stretching not supported on compressed surfaces.\n");
5175 hr = WINED3DERR_INVALIDCALL;
5176 goto release;
5179 if (!surface_check_block_align(src_surface, src_rect))
5181 WARN("Source rectangle not block-aligned.\n");
5182 hr = WINED3DERR_INVALIDCALL;
5183 goto release;
5186 if (!surface_check_block_align(dst_surface, dst_rect))
5188 WARN("Destination rectangle not block-aligned.\n");
5189 hr = WINED3DERR_INVALIDCALL;
5190 goto release;
5193 hr = surface_cpu_blt_compressed(sbase, dbuf,
5194 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
5195 src_format, flags, fx);
5196 goto release;
5199 /* First, all the 'source-less' blits */
5200 if (flags & WINEDDBLT_COLORFILL)
5202 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
5203 flags &= ~WINEDDBLT_COLORFILL;
5206 if (flags & WINEDDBLT_DEPTHFILL)
5208 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
5210 if (flags & WINEDDBLT_ROP)
5212 /* Catch some degenerate cases here. */
5213 switch (fx->dwROP)
5215 case BLACKNESS:
5216 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
5217 break;
5218 case 0xaa0029: /* No-op */
5219 break;
5220 case WHITENESS:
5221 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
5222 break;
5223 case SRCCOPY: /* Well, we do that below? */
5224 break;
5225 default:
5226 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
5227 goto error;
5229 flags &= ~WINEDDBLT_ROP;
5231 if (flags & WINEDDBLT_DDROPS)
5233 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
5235 /* Now the 'with source' blits. */
5236 if (src_surface)
5238 int sx, xinc, sy, yinc;
5240 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
5241 goto release;
5243 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
5244 && (srcwidth != dstwidth || srcheight != dstheight))
5246 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
5247 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
5250 xinc = (srcwidth << 16) / dstwidth;
5251 yinc = (srcheight << 16) / dstheight;
5253 if (!flags)
5255 /* No effects, we can cheat here. */
5256 if (dstwidth == srcwidth)
5258 if (dstheight == srcheight)
5260 /* No stretching in either direction. This needs to be as
5261 * fast as possible. */
5262 sbuf = sbase;
5264 /* Check for overlapping surfaces. */
5265 if (src_surface != dst_surface || dst_rect->top < src_rect->top
5266 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
5268 /* No overlap, or dst above src, so copy from top downwards. */
5269 for (y = 0; y < dstheight; ++y)
5271 memcpy(dbuf, sbuf, width);
5272 sbuf += src_map.row_pitch;
5273 dbuf += dst_map.row_pitch;
5276 else if (dst_rect->top > src_rect->top)
5278 /* Copy from bottom upwards. */
5279 sbuf += src_map.row_pitch * dstheight;
5280 dbuf += dst_map.row_pitch * dstheight;
5281 for (y = 0; y < dstheight; ++y)
5283 sbuf -= src_map.row_pitch;
5284 dbuf -= dst_map.row_pitch;
5285 memcpy(dbuf, sbuf, width);
5288 else
5290 /* Src and dst overlapping on the same line, use memmove. */
5291 for (y = 0; y < dstheight; ++y)
5293 memmove(dbuf, sbuf, width);
5294 sbuf += src_map.row_pitch;
5295 dbuf += dst_map.row_pitch;
5299 else
5301 /* Stretching in y direction only. */
5302 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5304 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5305 memcpy(dbuf, sbuf, width);
5306 dbuf += dst_map.row_pitch;
5310 else
5312 /* Stretching in X direction. */
5313 int last_sy = -1;
5314 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5316 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5318 if ((sy >> 16) == (last_sy >> 16))
5320 /* This source row is the same as last source row -
5321 * Copy the already stretched row. */
5322 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
5324 else
5326 #define STRETCH_ROW(type) \
5327 do { \
5328 const type *s = (const type *)sbuf; \
5329 type *d = (type *)dbuf; \
5330 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5331 d[x] = s[sx >> 16]; \
5332 } while(0)
5334 switch(bpp)
5336 case 1:
5337 STRETCH_ROW(BYTE);
5338 break;
5339 case 2:
5340 STRETCH_ROW(WORD);
5341 break;
5342 case 4:
5343 STRETCH_ROW(DWORD);
5344 break;
5345 case 3:
5347 const BYTE *s;
5348 BYTE *d = dbuf;
5349 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
5351 DWORD pixel;
5353 s = sbuf + 3 * (sx >> 16);
5354 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5355 d[0] = (pixel ) & 0xff;
5356 d[1] = (pixel >> 8) & 0xff;
5357 d[2] = (pixel >> 16) & 0xff;
5358 d += 3;
5360 break;
5362 default:
5363 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
5364 hr = WINED3DERR_NOTAVAILABLE;
5365 goto error;
5367 #undef STRETCH_ROW
5369 dbuf += dst_map.row_pitch;
5370 last_sy = sy;
5374 else
5376 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
5377 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
5378 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
5379 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
5381 /* The color keying flags are checked for correctness in ddraw */
5382 if (flags & WINEDDBLT_KEYSRC)
5384 keylow = src_surface->container->src_blt_color_key.color_space_low_value;
5385 keyhigh = src_surface->container->src_blt_color_key.color_space_high_value;
5387 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5389 keylow = fx->ddckSrcColorkey.color_space_low_value;
5390 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
5393 if (flags & WINEDDBLT_KEYDEST)
5395 /* Destination color keys are taken from the source surface! */
5396 destkeylow = src_surface->container->dst_blt_color_key.color_space_low_value;
5397 destkeyhigh = src_surface->container->dst_blt_color_key.color_space_high_value;
5399 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
5401 destkeylow = fx->ddckDestColorkey.color_space_low_value;
5402 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
5405 if (bpp == 1)
5407 keymask = 0xff;
5409 else
5411 DWORD masks[3];
5412 get_color_masks(src_format, masks);
5413 keymask = masks[0]
5414 | masks[1]
5415 | masks[2];
5417 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
5420 if (flags & WINEDDBLT_DDFX)
5422 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
5423 LONG tmpxy;
5424 dTopLeft = dbuf;
5425 dTopRight = dbuf + ((dstwidth - 1) * bpp);
5426 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
5427 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
5429 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
5431 /* I don't think we need to do anything about this flag */
5432 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
5434 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
5436 tmp = dTopRight;
5437 dTopRight = dTopLeft;
5438 dTopLeft = tmp;
5439 tmp = dBottomRight;
5440 dBottomRight = dBottomLeft;
5441 dBottomLeft = tmp;
5442 dstxinc = dstxinc * -1;
5444 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
5446 tmp = dTopLeft;
5447 dTopLeft = dBottomLeft;
5448 dBottomLeft = tmp;
5449 tmp = dTopRight;
5450 dTopRight = dBottomRight;
5451 dBottomRight = tmp;
5452 dstyinc = dstyinc * -1;
5454 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
5456 /* I don't think we need to do anything about this flag */
5457 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
5459 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
5461 tmp = dBottomRight;
5462 dBottomRight = dTopLeft;
5463 dTopLeft = tmp;
5464 tmp = dBottomLeft;
5465 dBottomLeft = dTopRight;
5466 dTopRight = tmp;
5467 dstxinc = dstxinc * -1;
5468 dstyinc = dstyinc * -1;
5470 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
5472 tmp = dTopLeft;
5473 dTopLeft = dBottomLeft;
5474 dBottomLeft = dBottomRight;
5475 dBottomRight = dTopRight;
5476 dTopRight = tmp;
5477 tmpxy = dstxinc;
5478 dstxinc = dstyinc;
5479 dstyinc = tmpxy;
5480 dstxinc = dstxinc * -1;
5482 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
5484 tmp = dTopLeft;
5485 dTopLeft = dTopRight;
5486 dTopRight = dBottomRight;
5487 dBottomRight = dBottomLeft;
5488 dBottomLeft = tmp;
5489 tmpxy = dstxinc;
5490 dstxinc = dstyinc;
5491 dstyinc = tmpxy;
5492 dstyinc = dstyinc * -1;
5494 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
5496 /* I don't think we need to do anything about this flag */
5497 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
5499 dbuf = dTopLeft;
5500 flags &= ~(WINEDDBLT_DDFX);
5503 #define COPY_COLORKEY_FX(type) \
5504 do { \
5505 const type *s; \
5506 type *d = (type *)dbuf, *dx, tmp; \
5507 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
5509 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
5510 dx = d; \
5511 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5513 tmp = s[sx >> 16]; \
5514 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
5515 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
5517 dx[0] = tmp; \
5519 dx = (type *)(((BYTE *)dx) + dstxinc); \
5521 d = (type *)(((BYTE *)d) + dstyinc); \
5523 } while(0)
5525 switch (bpp)
5527 case 1:
5528 COPY_COLORKEY_FX(BYTE);
5529 break;
5530 case 2:
5531 COPY_COLORKEY_FX(WORD);
5532 break;
5533 case 4:
5534 COPY_COLORKEY_FX(DWORD);
5535 break;
5536 case 3:
5538 const BYTE *s;
5539 BYTE *d = dbuf, *dx;
5540 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5542 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5543 dx = d;
5544 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
5546 DWORD pixel, dpixel = 0;
5547 s = sbuf + 3 * (sx>>16);
5548 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5549 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
5550 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
5551 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
5553 dx[0] = (pixel ) & 0xff;
5554 dx[1] = (pixel >> 8) & 0xff;
5555 dx[2] = (pixel >> 16) & 0xff;
5557 dx += dstxinc;
5559 d += dstyinc;
5561 break;
5563 default:
5564 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
5565 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
5566 hr = WINED3DERR_NOTAVAILABLE;
5567 goto error;
5568 #undef COPY_COLORKEY_FX
5573 error:
5574 if (flags && FIXME_ON(d3d_surface))
5576 FIXME("\tUnsupported flags: %#x.\n", flags);
5579 release:
5580 wined3d_surface_unmap(dst_surface);
5581 if (src_surface && src_surface != dst_surface)
5582 wined3d_surface_unmap(src_surface);
5583 /* Release the converted surface, if any. */
5584 if (src_texture)
5585 wined3d_texture_decref(src_texture);
5587 return hr;
5590 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5591 const RECT *dst_rect, const struct wined3d_color *color)
5593 static const RECT src_rect;
5594 WINEDDBLTFX BltFx;
5596 memset(&BltFx, 0, sizeof(BltFx));
5597 BltFx.dwSize = sizeof(BltFx);
5598 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
5599 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
5600 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
5603 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
5604 struct wined3d_surface *surface, const RECT *rect, float depth)
5606 FIXME("Depth filling not implemented by cpu_blit.\n");
5607 return WINED3DERR_INVALIDCALL;
5610 const struct blit_shader cpu_blit = {
5611 cpu_blit_alloc,
5612 cpu_blit_free,
5613 cpu_blit_set,
5614 cpu_blit_unset,
5615 cpu_blit_supported,
5616 cpu_blit_color_fill,
5617 cpu_blit_depth_fill,
5620 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5621 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5622 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5624 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5625 struct wined3d_device *device = dst_surface->resource.device;
5626 DWORD src_ds_flags, dst_ds_flags;
5627 RECT src_rect, dst_rect;
5628 BOOL scale, convert;
5629 enum wined3d_conversion_type dst_convert_type;
5630 struct wined3d_format dst_conv_fmt;
5632 static const DWORD simple_blit = WINEDDBLT_ASYNC
5633 | WINEDDBLT_COLORFILL
5634 | WINEDDBLT_WAIT
5635 | WINEDDBLT_DEPTHFILL
5636 | WINEDDBLT_DONOTWAIT;
5638 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5639 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
5640 flags, fx, debug_d3dtexturefiltertype(filter));
5641 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5643 if (fx)
5645 TRACE("dwSize %#x.\n", fx->dwSize);
5646 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5647 TRACE("dwROP %#x.\n", fx->dwROP);
5648 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5649 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5650 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5651 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5652 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5653 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5654 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5655 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5656 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5657 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5658 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5659 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5660 TRACE("dwReserved %#x.\n", fx->dwReserved);
5661 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5662 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5663 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5664 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5665 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5666 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5667 fx->ddckDestColorkey.color_space_low_value,
5668 fx->ddckDestColorkey.color_space_high_value);
5669 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5670 fx->ddckSrcColorkey.color_space_low_value,
5671 fx->ddckSrcColorkey.color_space_high_value);
5674 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5676 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5677 return WINEDDERR_SURFACEBUSY;
5680 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5682 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5683 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5684 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5685 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5686 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5688 WARN("The application gave us a bad destination rectangle.\n");
5689 return WINEDDERR_INVALIDRECT;
5692 if (src_surface)
5694 surface_get_rect(src_surface, src_rect_in, &src_rect);
5696 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5697 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5698 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5699 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5700 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5702 WARN("Application gave us bad source rectangle for Blt.\n");
5703 return WINEDDERR_INVALIDRECT;
5706 else
5708 memset(&src_rect, 0, sizeof(src_rect));
5711 if (!fx || !(fx->dwDDFX))
5712 flags &= ~WINEDDBLT_DDFX;
5714 if (flags & WINEDDBLT_WAIT)
5715 flags &= ~WINEDDBLT_WAIT;
5717 if (flags & WINEDDBLT_ASYNC)
5719 static unsigned int once;
5721 if (!once++)
5722 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5723 flags &= ~WINEDDBLT_ASYNC;
5726 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5727 if (flags & WINEDDBLT_DONOTWAIT)
5729 static unsigned int once;
5731 if (!once++)
5732 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5733 flags &= ~WINEDDBLT_DONOTWAIT;
5736 if (!device->d3d_initialized)
5738 WARN("D3D not initialized, using fallback.\n");
5739 goto cpu;
5742 /* We want to avoid invalidating the sysmem location for converted
5743 * surfaces, since otherwise we'd have to convert the data back when
5744 * locking them. */
5745 d3dfmt_get_conv(dst_surface, TRUE, TRUE, &dst_conv_fmt, &dst_convert_type);
5746 if (dst_convert_type != WINED3D_CT_NONE || dst_conv_fmt.convert || dst_surface->flags & SFLAG_CONVERTED)
5748 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5749 goto cpu;
5752 if (flags & ~simple_blit)
5754 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5755 goto fallback;
5758 if (src_surface)
5759 src_swapchain = src_surface->container->swapchain;
5760 else
5761 src_swapchain = NULL;
5763 dst_swapchain = dst_surface->container->swapchain;
5765 /* This isn't strictly needed. FBO blits for example could deal with
5766 * cross-swapchain blits by first downloading the source to a texture
5767 * before switching to the destination context. We just have this here to
5768 * not have to deal with the issue, since cross-swapchain blits should be
5769 * rare. */
5770 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5772 FIXME("Using fallback for cross-swapchain blit.\n");
5773 goto fallback;
5776 scale = src_surface
5777 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5778 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5779 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5781 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5782 if (src_surface)
5783 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5784 else
5785 src_ds_flags = 0;
5787 if (src_ds_flags || dst_ds_flags)
5789 if (flags & WINEDDBLT_DEPTHFILL)
5791 float depth;
5793 TRACE("Depth fill.\n");
5795 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5796 return WINED3DERR_INVALIDCALL;
5798 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
5799 return WINED3D_OK;
5801 else
5803 if (src_ds_flags != dst_ds_flags)
5805 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
5806 return WINED3DERR_INVALIDCALL;
5809 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
5810 &src_rect, dst_surface, dst_surface->container->resource.draw_binding, &dst_rect)))
5811 return WINED3D_OK;
5814 else
5816 /* In principle this would apply to depth blits as well, but we don't
5817 * implement those in the CPU blitter at the moment. */
5818 if ((dst_surface->locations & dst_surface->resource.map_binding)
5819 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
5821 if (scale)
5822 TRACE("Not doing sysmem blit because of scaling.\n");
5823 else if (convert)
5824 TRACE("Not doing sysmem blit because of format conversion.\n");
5825 else
5826 goto cpu;
5829 if (flags & WINEDDBLT_COLORFILL)
5831 struct wined3d_color color;
5833 TRACE("Color fill.\n");
5835 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
5836 goto fallback;
5838 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
5839 return WINED3D_OK;
5841 else
5843 TRACE("Color blit.\n");
5845 /* Upload */
5846 if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5847 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5849 if (scale)
5850 TRACE("Not doing upload because of scaling.\n");
5851 else if (convert)
5852 TRACE("Not doing upload because of format conversion.\n");
5853 else
5855 POINT dst_point = {dst_rect.left, dst_rect.top};
5857 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
5859 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
5860 surface_load_location(dst_surface, dst_surface->container->resource.draw_binding);
5861 return WINED3D_OK;
5866 /* Use present for back -> front blits. The idea behind this is
5867 * that present is potentially faster than a blit, in particular
5868 * when FBO blits aren't available. Some ddraw applications like
5869 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5870 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5871 * applications can't blit directly to the frontbuffer. */
5872 if (dst_swapchain && dst_swapchain->back_buffers
5873 && dst_surface->container == dst_swapchain->front_buffer
5874 && src_surface->container == dst_swapchain->back_buffers[0])
5876 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5878 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5880 /* Set the swap effect to COPY, we don't want the backbuffer
5881 * to become undefined. */
5882 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5883 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5884 dst_swapchain->desc.swap_effect = swap_effect;
5886 return WINED3D_OK;
5889 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5890 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5891 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5893 TRACE("Using FBO blit.\n");
5895 surface_blt_fbo(device, filter,
5896 src_surface, src_surface->container->resource.draw_binding, &src_rect,
5897 dst_surface, dst_surface->container->resource.draw_binding, &dst_rect);
5898 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5899 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5901 return WINED3D_OK;
5904 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5905 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5906 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5908 TRACE("Using arbfp blit.\n");
5910 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
5911 return WINED3D_OK;
5916 fallback:
5917 /* Special cases for render targets. */
5918 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
5919 return WINED3D_OK;
5921 cpu:
5923 /* For the rest call the X11 surface implementation. For render targets
5924 * this should be implemented OpenGL accelerated in surface_blt_special(),
5925 * other blits are rather rare. */
5926 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
5929 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5930 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5932 struct wined3d_device *device = container->resource.device;
5933 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5934 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5935 UINT multisample_quality = desc->multisample_quality;
5936 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
5937 unsigned int resource_size;
5938 HRESULT hr;
5940 if (multisample_quality > 0)
5942 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
5943 multisample_quality = 0;
5946 /* Quick lockable sanity check.
5947 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5948 * this function is too deep to need to care about things like this.
5949 * Levels need to be checked too, since they all affect what can be done. */
5950 switch (desc->pool)
5952 case WINED3D_POOL_MANAGED:
5953 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5954 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5955 break;
5957 case WINED3D_POOL_DEFAULT:
5958 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5959 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5960 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5961 break;
5963 case WINED3D_POOL_SCRATCH:
5964 case WINED3D_POOL_SYSTEM_MEM:
5965 break;
5967 default:
5968 FIXME("Unknown pool %#x.\n", desc->pool);
5969 break;
5972 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5973 FIXME("Trying to create a render target that isn't in the default pool.\n");
5975 /* FIXME: Check that the format is supported by the device. */
5977 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5978 if (!resource_size)
5979 return WINED3DERR_INVALIDCALL;
5981 if (device->wined3d->flags & WINED3D_NO3D)
5982 surface->surface_ops = &gdi_surface_ops;
5983 else
5984 surface->surface_ops = &surface_ops;
5986 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
5987 desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height, 1,
5988 resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5990 WARN("Failed to initialize resource, returning %#x.\n", hr);
5991 return hr;
5994 surface->container = container;
5995 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5996 list_init(&surface->renderbuffers);
5997 list_init(&surface->overlays);
5999 /* Flags */
6000 if (target != GL_TEXTURE_RECTANGLE_ARB)
6001 surface->flags |= SFLAG_NORMCOORD;
6002 if (flags & WINED3D_SURFACE_DISCARD)
6003 surface->flags |= SFLAG_DISCARD;
6004 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
6005 surface->flags |= SFLAG_PIN_SYSMEM;
6006 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
6007 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
6009 surface->texture_target = target;
6010 surface->texture_level = level;
6011 surface->texture_layer = layer;
6013 /* Call the private setup routine */
6014 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
6016 ERR("Private setup failed, hr %#x.\n", hr);
6017 surface_cleanup(surface);
6018 return hr;
6021 /* Similar to lockable rendertargets above, creating the DIB section
6022 * during surface initialization prevents the sysmem pointer from changing
6023 * after a wined3d_surface_getdc() call. */
6024 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
6025 && SUCCEEDED(surface_create_dib_section(surface)))
6026 surface->resource.map_binding = WINED3D_LOCATION_DIB;
6028 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
6030 wined3d_resource_free_sysmem(&surface->resource);
6031 surface_validate_location(surface, WINED3D_LOCATION_DIB);
6032 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
6035 return hr;
6038 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
6039 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
6041 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
6042 const struct wined3d_parent_ops *parent_ops;
6043 struct wined3d_surface *object;
6044 void *parent;
6045 HRESULT hr;
6047 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
6048 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
6049 container, desc->width, desc->height, debug_d3dformat(desc->format),
6050 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
6051 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
6053 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
6054 return E_OUTOFMEMORY;
6056 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
6058 WARN("Failed to initialize surface, returning %#x.\n", hr);
6059 HeapFree(GetProcessHeap(), 0, object);
6060 return hr;
6063 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
6064 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
6066 WARN("Failed to create surface parent, hr %#x.\n", hr);
6067 wined3d_surface_destroy(object);
6068 return hr;
6071 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
6073 object->resource.parent = parent;
6074 object->resource.parent_ops = parent_ops;
6075 *surface = object;
6077 return hr;