xaudio2: Explicitly version the 2.7 coclasses.
[wine.git] / dlls / wined3d / surface.c
blob6b5f33edfe4441264bf4c7172f743fe775892391
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2011, 2013-2014 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d);
37 #define MAXLOCKCOUNT 50 /* After this amount of locks do not free the sysmem copy. */
39 static const DWORD surface_simple_locations =
40 WINED3D_LOCATION_SYSMEM | WINED3D_LOCATION_USER_MEMORY
41 | WINED3D_LOCATION_DIB | WINED3D_LOCATION_BUFFER;
43 static void surface_cleanup(struct wined3d_surface *surface)
45 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->pbo || surface->rb_multisample
50 || surface->rb_resolved || !list_empty(&surface->renderbuffers))
52 struct wined3d_renderbuffer_entry *entry, *entry2;
53 const struct wined3d_gl_info *gl_info;
54 struct wined3d_context *context;
56 context = context_acquire(surface->resource.device, NULL);
57 gl_info = context->gl_info;
59 if (surface->pbo)
61 TRACE("Deleting PBO %u.\n", surface->pbo);
62 GL_EXTCALL(glDeleteBuffers(1, &surface->pbo));
65 if (surface->rb_multisample)
67 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
68 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
71 if (surface->rb_resolved)
73 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
74 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
77 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
79 TRACE("Deleting renderbuffer %u.\n", entry->id);
80 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
81 HeapFree(GetProcessHeap(), 0, entry);
84 context_release(context);
87 if (surface->flags & SFLAG_DIBSECTION)
89 DeleteDC(surface->hDC);
90 DeleteObject(surface->dib.DIBsection);
91 surface->dib.bitmap_data = NULL;
94 if (surface->overlay_dest)
95 list_remove(&surface->overlay_entry);
97 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
99 list_remove(&overlay->overlay_entry);
100 overlay->overlay_dest = NULL;
103 resource_cleanup(&surface->resource);
106 void wined3d_surface_destroy(struct wined3d_surface *surface)
108 TRACE("surface %p.\n", surface);
110 surface_cleanup(surface);
111 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
112 HeapFree(GetProcessHeap(), 0, surface);
115 void surface_get_drawable_size(const struct wined3d_surface *surface, const struct wined3d_context *context,
116 unsigned int *width, unsigned int *height)
118 if (surface->container->swapchain)
120 /* The drawable size of an onscreen drawable is the surface size.
121 * (Actually: The window size, but the surface is created in window
122 * size.) */
123 *width = context->current_rt->resource.width;
124 *height = context->current_rt->resource.height;
126 else if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER)
128 const struct wined3d_swapchain *swapchain = context->swapchain;
130 /* The drawable size of a backbuffer / aux buffer offscreen target is
131 * the size of the current context's drawable, which is the size of
132 * the back buffer of the swapchain the active context belongs to. */
133 *width = swapchain->desc.backbuffer_width;
134 *height = swapchain->desc.backbuffer_height;
136 else
138 /* The drawable size of an FBO target is the OpenGL texture size,
139 * which is the power of two size. */
140 *width = context->current_rt->pow2Width;
141 *height = context->current_rt->pow2Height;
145 struct blt_info
147 GLenum binding;
148 GLenum bind_target;
149 enum wined3d_gl_resource_type tex_type;
150 GLfloat coords[4][3];
153 struct float_rect
155 float l;
156 float t;
157 float r;
158 float b;
161 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
163 f->l = ((r->left * 2.0f) / w) - 1.0f;
164 f->t = ((r->top * 2.0f) / h) - 1.0f;
165 f->r = ((r->right * 2.0f) / w) - 1.0f;
166 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
169 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
171 GLfloat (*coords)[3] = info->coords;
172 struct float_rect f;
174 switch (target)
176 default:
177 FIXME("Unsupported texture target %#x\n", target);
178 /* Fall back to GL_TEXTURE_2D */
179 case GL_TEXTURE_2D:
180 info->binding = GL_TEXTURE_BINDING_2D;
181 info->bind_target = GL_TEXTURE_2D;
182 info->tex_type = WINED3D_GL_RES_TYPE_TEX_2D;
183 coords[0][0] = (float)rect->left / w;
184 coords[0][1] = (float)rect->top / h;
185 coords[0][2] = 0.0f;
187 coords[1][0] = (float)rect->right / w;
188 coords[1][1] = (float)rect->top / h;
189 coords[1][2] = 0.0f;
191 coords[2][0] = (float)rect->left / w;
192 coords[2][1] = (float)rect->bottom / h;
193 coords[2][2] = 0.0f;
195 coords[3][0] = (float)rect->right / w;
196 coords[3][1] = (float)rect->bottom / h;
197 coords[3][2] = 0.0f;
198 break;
200 case GL_TEXTURE_RECTANGLE_ARB:
201 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
202 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
203 info->tex_type = WINED3D_GL_RES_TYPE_TEX_RECT;
204 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
205 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
206 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
207 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
208 break;
210 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
211 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
212 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
213 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
214 cube_coords_float(rect, w, h, &f);
216 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
217 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
218 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
219 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
220 break;
222 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
223 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
224 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
225 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
226 cube_coords_float(rect, w, h, &f);
228 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
229 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
230 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
231 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
232 break;
234 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
235 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
236 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
237 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
238 cube_coords_float(rect, w, h, &f);
240 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
241 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
242 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
243 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
244 break;
246 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
247 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
248 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
249 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
250 cube_coords_float(rect, w, h, &f);
252 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
253 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
254 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
255 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
256 break;
258 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
259 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
260 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
261 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
262 cube_coords_float(rect, w, h, &f);
264 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
265 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
266 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
267 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
268 break;
270 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
271 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
272 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
273 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
274 cube_coords_float(rect, w, h, &f);
276 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
277 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
278 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
279 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
280 break;
284 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
286 if (rect_in)
287 *rect_out = *rect_in;
288 else
290 rect_out->left = 0;
291 rect_out->top = 0;
292 rect_out->right = surface->resource.width;
293 rect_out->bottom = surface->resource.height;
297 /* Context activation is done by the caller. */
298 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
299 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
301 const struct wined3d_gl_info *gl_info = context->gl_info;
302 struct wined3d_texture *texture = src_surface->container;
303 struct blt_info info;
305 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
307 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
308 checkGLcall("glEnable(bind_target)");
310 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
312 /* Filtering for StretchRect */
313 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
314 checkGLcall("glTexParameteri");
315 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
316 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
317 checkGLcall("glTexParameteri");
318 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
319 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
320 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
321 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
322 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
323 checkGLcall("glTexEnvi");
325 /* Draw a quad */
326 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
327 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
328 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
330 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
331 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
333 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
334 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
336 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
337 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
338 gl_info->gl_ops.gl.p_glEnd();
340 /* Unbind the texture */
341 context_bind_texture(context, info.bind_target, 0);
343 /* We changed the filtering settings on the texture. Inform the
344 * container about this to get the filters reset properly next draw. */
345 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
346 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
347 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
348 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
351 /* Works correctly only for <= 4 bpp formats. */
352 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
354 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
355 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
356 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
359 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
361 const struct wined3d_format *format = surface->resource.format;
362 unsigned int format_flags = surface->container->resource.format_flags;
363 SYSTEM_INFO sysInfo;
364 BITMAPINFO *b_info;
365 int extraline = 0;
366 DWORD *masks;
368 TRACE("surface %p.\n", surface);
370 if (!(format_flags & WINED3DFMT_FLAG_GETDC))
372 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
373 return WINED3DERR_INVALIDCALL;
376 switch (format->byte_count)
378 case 2:
379 case 4:
380 /* Allocate extra space to store the RGB bit masks. */
381 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
382 break;
384 case 3:
385 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
386 break;
388 default:
389 /* Allocate extra space for a palette. */
390 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
391 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1u << (format->byte_count * 8)));
392 break;
395 if (!b_info)
396 return E_OUTOFMEMORY;
398 /* Some applications access the surface in via DWORDs, and do not take
399 * the necessary care at the end of the surface. So we need at least
400 * 4 extra bytes at the end of the surface. Check against the page size,
401 * if the last page used for the surface has at least 4 spare bytes we're
402 * safe, otherwise add an extra line to the DIB section. */
403 GetSystemInfo(&sysInfo);
404 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
406 extraline = 1;
407 TRACE("Adding an extra line to the DIB section.\n");
410 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
411 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
412 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
413 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
414 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
415 * wined3d_surface_get_pitch(surface);
416 b_info->bmiHeader.biPlanes = 1;
417 b_info->bmiHeader.biBitCount = format->byte_count * 8;
419 b_info->bmiHeader.biXPelsPerMeter = 0;
420 b_info->bmiHeader.biYPelsPerMeter = 0;
421 b_info->bmiHeader.biClrUsed = 0;
422 b_info->bmiHeader.biClrImportant = 0;
424 /* Get the bit masks */
425 masks = (DWORD *)b_info->bmiColors;
426 switch (surface->resource.format->id)
428 case WINED3DFMT_B8G8R8_UNORM:
429 b_info->bmiHeader.biCompression = BI_RGB;
430 break;
432 case WINED3DFMT_B5G5R5X1_UNORM:
433 case WINED3DFMT_B5G5R5A1_UNORM:
434 case WINED3DFMT_B4G4R4A4_UNORM:
435 case WINED3DFMT_B4G4R4X4_UNORM:
436 case WINED3DFMT_B2G3R3_UNORM:
437 case WINED3DFMT_B2G3R3A8_UNORM:
438 case WINED3DFMT_R10G10B10A2_UNORM:
439 case WINED3DFMT_R8G8B8A8_UNORM:
440 case WINED3DFMT_R8G8B8X8_UNORM:
441 case WINED3DFMT_B10G10R10A2_UNORM:
442 case WINED3DFMT_B5G6R5_UNORM:
443 case WINED3DFMT_R16G16B16A16_UNORM:
444 b_info->bmiHeader.biCompression = BI_BITFIELDS;
445 get_color_masks(format, masks);
446 break;
448 default:
449 /* Don't know palette */
450 b_info->bmiHeader.biCompression = BI_RGB;
451 break;
454 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
455 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
456 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
457 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
459 if (!surface->dib.DIBsection)
461 ERR("Failed to create DIB section.\n");
462 HeapFree(GetProcessHeap(), 0, b_info);
463 return HRESULT_FROM_WIN32(GetLastError());
466 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
467 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
469 HeapFree(GetProcessHeap(), 0, b_info);
471 /* Now allocate a DC. */
472 surface->hDC = CreateCompatibleDC(0);
473 SelectObject(surface->hDC, surface->dib.DIBsection);
475 surface->flags |= SFLAG_DIBSECTION;
477 return WINED3D_OK;
480 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
481 DWORD location)
483 if (location & WINED3D_LOCATION_BUFFER)
485 data->addr = NULL;
486 data->buffer_object = surface->pbo;
487 return;
489 if (location & WINED3D_LOCATION_USER_MEMORY)
491 data->addr = surface->user_memory;
492 data->buffer_object = 0;
493 return;
495 if (location & WINED3D_LOCATION_DIB)
497 data->addr = surface->dib.bitmap_data;
498 data->buffer_object = 0;
499 return;
501 if (location & WINED3D_LOCATION_SYSMEM)
503 data->addr = surface->resource.heap_memory;
504 data->buffer_object = 0;
505 return;
508 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
509 data->addr = NULL;
510 data->buffer_object = 0;
513 static void surface_prepare_buffer(struct wined3d_surface *surface)
515 struct wined3d_context *context;
516 GLenum error;
517 const struct wined3d_gl_info *gl_info;
519 if (surface->pbo)
520 return;
522 context = context_acquire(surface->resource.device, NULL);
523 gl_info = context->gl_info;
525 GL_EXTCALL(glGenBuffers(1, &surface->pbo));
526 error = gl_info->gl_ops.gl.p_glGetError();
527 if (!surface->pbo || error != GL_NO_ERROR)
528 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
530 TRACE("Binding PBO %u.\n", surface->pbo);
532 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
533 checkGLcall("glBindBuffer");
535 GL_EXTCALL(glBufferData(GL_PIXEL_UNPACK_BUFFER, surface->resource.size + 4,
536 NULL, GL_STREAM_DRAW));
537 checkGLcall("glBufferData");
539 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
540 checkGLcall("glBindBuffer");
542 context_release(context);
545 static void surface_prepare_system_memory(struct wined3d_surface *surface)
547 TRACE("surface %p.\n", surface);
549 if (surface->resource.heap_memory)
550 return;
552 /* Whatever surface we have, make sure that there is memory allocated
553 * for the downloaded copy, or a PBO to map. */
554 if (!wined3d_resource_allocate_sysmem(&surface->resource))
555 ERR("Failed to allocate system memory.\n");
557 if (surface->locations & WINED3D_LOCATION_SYSMEM)
558 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
561 void surface_prepare_map_memory(struct wined3d_surface *surface)
563 switch (surface->resource.map_binding)
565 case WINED3D_LOCATION_SYSMEM:
566 surface_prepare_system_memory(surface);
567 break;
569 case WINED3D_LOCATION_USER_MEMORY:
570 if (!surface->user_memory)
571 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
572 break;
574 case WINED3D_LOCATION_DIB:
575 if (!surface->dib.bitmap_data)
576 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
577 break;
579 case WINED3D_LOCATION_BUFFER:
580 surface_prepare_buffer(surface);
581 break;
583 default:
584 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
588 static void surface_evict_sysmem(struct wined3d_surface *surface)
590 /* In some conditions the surface memory must not be freed:
591 * WINED3D_TEXTURE_CONVERTED: Converting the data back would take too long
592 * WINED3D_TEXTURE_DYNAMIC_MAP: Avoid freeing the data for performance */
593 if (surface->resource.map_count || surface->container->flags & (WINED3D_TEXTURE_CONVERTED
594 | WINED3D_TEXTURE_PIN_SYSMEM | WINED3D_TEXTURE_DYNAMIC_MAP))
595 return;
597 wined3d_resource_free_sysmem(&surface->resource);
598 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
601 static BOOL surface_use_pbo(const struct wined3d_surface *surface)
603 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
604 struct wined3d_texture *texture = surface->container;
606 return texture->resource.pool == WINED3D_POOL_DEFAULT
607 && surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU
608 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
609 && !texture->resource.format->convert
610 && !(texture->flags & WINED3D_TEXTURE_PIN_SYSMEM)
611 && !(surface->flags & SFLAG_NONPOW2);
614 static HRESULT surface_private_setup(struct wined3d_surface *surface)
616 /* TODO: Check against the maximum texture sizes supported by the video card. */
617 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
618 unsigned int pow2Width, pow2Height;
620 TRACE("surface %p.\n", surface);
622 /* Non-power2 support */
623 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
624 || gl_info->supported[ARB_TEXTURE_RECTANGLE])
626 pow2Width = surface->resource.width;
627 pow2Height = surface->resource.height;
629 else
631 /* Find the nearest pow2 match */
632 pow2Width = pow2Height = 1;
633 while (pow2Width < surface->resource.width)
634 pow2Width <<= 1;
635 while (pow2Height < surface->resource.height)
636 pow2Height <<= 1;
638 surface->pow2Width = pow2Width;
639 surface->pow2Height = pow2Height;
641 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
643 /* TODO: Add support for non power two compressed textures. */
644 if (surface->container->resource.format_flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE))
646 FIXME("(%p) Compressed or height scaled non-power-two textures are not supported w(%d) h(%d)\n",
647 surface, surface->resource.width, surface->resource.height);
648 return WINED3DERR_NOTAVAILABLE;
652 if (pow2Width != surface->resource.width
653 || pow2Height != surface->resource.height)
655 surface->flags |= SFLAG_NONPOW2;
658 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
659 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
661 /* One of three options:
662 * 1: Do the same as we do with NPOT and scale the texture, (any
663 * texture ops would require the texture to be scaled which is
664 * potentially slow)
665 * 2: Set the texture to the maximum size (bad idea).
666 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
667 * 4: Create the surface, but allow it to be used only for DirectDraw
668 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
669 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
670 * the render target. */
671 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
673 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
674 return WINED3DERR_NOTAVAILABLE;
677 /* We should never use this surface in combination with OpenGL! */
678 TRACE("Creating an oversized surface: %ux%u.\n",
679 surface->pow2Width, surface->pow2Height);
682 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
683 surface->locations = WINED3D_LOCATION_DISCARDED;
685 if (surface_use_pbo(surface))
686 surface->resource.map_binding = WINED3D_LOCATION_BUFFER;
688 return WINED3D_OK;
691 static void surface_unmap(struct wined3d_surface *surface)
693 struct wined3d_device *device = surface->resource.device;
694 const struct wined3d_gl_info *gl_info;
695 struct wined3d_context *context;
697 TRACE("surface %p.\n", surface);
699 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
701 switch (surface->resource.map_binding)
703 case WINED3D_LOCATION_SYSMEM:
704 case WINED3D_LOCATION_USER_MEMORY:
705 case WINED3D_LOCATION_DIB:
706 break;
708 case WINED3D_LOCATION_BUFFER:
709 context = context_acquire(device, NULL);
710 gl_info = context->gl_info;
712 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
713 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
714 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
715 checkGLcall("glUnmapBuffer");
716 context_release(context);
717 break;
719 default:
720 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
723 if (surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_TEXTURE_RGB))
725 TRACE("Not dirtified, nothing to do.\n");
726 return;
729 if (surface->container->swapchain && surface->container->swapchain->front_buffer == surface->container)
731 context = context_acquire(device, surface);
732 surface_load_location(surface, context, surface->container->resource.draw_binding);
733 context_release(context);
735 else if (surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
736 FIXME("Depth / stencil buffer locking is not implemented.\n");
739 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
741 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
742 return FALSE;
743 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
744 return FALSE;
745 return TRUE;
748 static void surface_depth_blt_fbo(const struct wined3d_device *device,
749 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
750 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
752 const struct wined3d_gl_info *gl_info;
753 struct wined3d_context *context;
754 DWORD src_mask, dst_mask;
755 GLbitfield gl_mask;
757 TRACE("device %p\n", device);
758 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
759 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
760 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
761 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
763 src_mask = src_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
764 dst_mask = dst_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
766 if (src_mask != dst_mask)
768 ERR("Incompatible formats %s and %s.\n",
769 debug_d3dformat(src_surface->resource.format->id),
770 debug_d3dformat(dst_surface->resource.format->id));
771 return;
774 if (!src_mask)
776 ERR("Not a depth / stencil format: %s.\n",
777 debug_d3dformat(src_surface->resource.format->id));
778 return;
781 gl_mask = 0;
782 if (src_mask & WINED3DFMT_FLAG_DEPTH)
783 gl_mask |= GL_DEPTH_BUFFER_BIT;
784 if (src_mask & WINED3DFMT_FLAG_STENCIL)
785 gl_mask |= GL_STENCIL_BUFFER_BIT;
787 context = context_acquire(device, NULL);
788 if (!context->valid)
790 context_release(context);
791 WARN("Invalid context, skipping blit.\n");
792 return;
795 /* Make sure the locations are up-to-date. Loading the destination
796 * surface isn't required if the entire surface is overwritten. */
797 surface_load_location(src_surface, context, src_location);
798 if (!surface_is_full_rect(dst_surface, dst_rect))
799 surface_load_location(dst_surface, context, dst_location);
800 else
801 wined3d_surface_prepare(dst_surface, context, dst_location);
803 gl_info = context->gl_info;
805 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
806 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
808 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
809 context_set_draw_buffer(context, GL_NONE);
810 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
811 context_invalidate_state(context, STATE_FRAMEBUFFER);
813 if (gl_mask & GL_DEPTH_BUFFER_BIT)
815 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
816 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
818 if (gl_mask & GL_STENCIL_BUFFER_BIT)
820 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
822 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
823 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
825 gl_info->gl_ops.gl.p_glStencilMask(~0U);
826 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
829 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
830 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
832 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
833 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
834 checkGLcall("glBlitFramebuffer()");
836 if (wined3d_settings.strict_draw_ordering)
837 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
839 context_release(context);
842 /* Blit between surface locations. Onscreen on different swapchains is not supported.
843 * Depth / stencil is not supported. Context activation is done by the caller. */
844 static void surface_blt_fbo(const struct wined3d_device *device,
845 struct wined3d_context *old_ctx, enum wined3d_texture_filter_type filter,
846 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
847 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
849 const struct wined3d_gl_info *gl_info;
850 struct wined3d_context *context = old_ctx;
851 struct wined3d_surface *required_rt, *restore_rt = NULL;
852 RECT src_rect, dst_rect;
853 GLenum gl_filter;
854 GLenum buffer;
856 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
857 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
858 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
859 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
860 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
862 src_rect = *src_rect_in;
863 dst_rect = *dst_rect_in;
865 switch (filter)
867 case WINED3D_TEXF_LINEAR:
868 gl_filter = GL_LINEAR;
869 break;
871 default:
872 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
873 case WINED3D_TEXF_NONE:
874 case WINED3D_TEXF_POINT:
875 gl_filter = GL_NEAREST;
876 break;
879 /* Resolve the source surface first if needed. */
880 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
881 && (src_surface->resource.format->id != dst_surface->resource.format->id
882 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
883 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
884 src_location = WINED3D_LOCATION_RB_RESOLVED;
886 /* Make sure the locations are up-to-date. Loading the destination
887 * surface isn't required if the entire surface is overwritten. (And is
888 * in fact harmful if we're being called by surface_load_location() with
889 * the purpose of loading the destination surface.) */
890 surface_load_location(src_surface, old_ctx, src_location);
891 if (!surface_is_full_rect(dst_surface, &dst_rect))
892 surface_load_location(dst_surface, old_ctx, dst_location);
893 else
894 wined3d_surface_prepare(dst_surface, old_ctx, dst_location);
897 if (src_location == WINED3D_LOCATION_DRAWABLE) required_rt = src_surface;
898 else if (dst_location == WINED3D_LOCATION_DRAWABLE) required_rt = dst_surface;
899 else required_rt = NULL;
901 if (required_rt && required_rt != old_ctx->current_rt)
903 restore_rt = old_ctx->current_rt;
904 context = context_acquire(device, required_rt);
907 if (!context->valid)
909 context_release(context);
910 WARN("Invalid context, skipping blit.\n");
911 return;
914 gl_info = context->gl_info;
916 if (src_location == WINED3D_LOCATION_DRAWABLE)
918 TRACE("Source surface %p is onscreen.\n", src_surface);
919 buffer = surface_get_gl_buffer(src_surface);
920 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
922 else
924 TRACE("Source surface %p is offscreen.\n", src_surface);
925 buffer = GL_COLOR_ATTACHMENT0;
928 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
929 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
930 checkGLcall("glReadBuffer()");
931 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
933 if (dst_location == WINED3D_LOCATION_DRAWABLE)
935 TRACE("Destination surface %p is onscreen.\n", dst_surface);
936 buffer = surface_get_gl_buffer(dst_surface);
937 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
939 else
941 TRACE("Destination surface %p is offscreen.\n", dst_surface);
942 buffer = GL_COLOR_ATTACHMENT0;
945 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
946 context_set_draw_buffer(context, buffer);
947 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
948 context_invalidate_state(context, STATE_FRAMEBUFFER);
950 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
951 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
952 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
953 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
954 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
956 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
957 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
959 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
960 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
961 checkGLcall("glBlitFramebuffer()");
963 if (wined3d_settings.strict_draw_ordering
964 || (dst_location == WINED3D_LOCATION_DRAWABLE
965 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
966 gl_info->gl_ops.gl.p_glFlush();
968 if (restore_rt)
969 context_restore(context, restore_rt);
972 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
973 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
974 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
976 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
977 return FALSE;
979 /* Source and/or destination need to be on the GL side */
980 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
981 return FALSE;
983 switch (blit_op)
985 case WINED3D_BLIT_OP_COLOR_BLIT:
986 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
987 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
988 return FALSE;
989 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
990 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
991 return FALSE;
992 if (!(src_format->id == dst_format->id
993 || (is_identity_fixup(src_format->color_fixup)
994 && is_identity_fixup(dst_format->color_fixup))))
995 return FALSE;
996 break;
998 case WINED3D_BLIT_OP_DEPTH_BLIT:
999 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1000 return FALSE;
1001 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1002 return FALSE;
1003 /* Accept pure swizzle fixups for depth formats. In general we
1004 * ignore the stencil component (if present) at the moment and the
1005 * swizzle is not relevant with just the depth component. */
1006 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
1007 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
1008 return FALSE;
1009 break;
1011 default:
1012 return FALSE;
1015 return TRUE;
1018 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1020 const struct wined3d_format *format = surface->resource.format;
1022 switch (format->id)
1024 case WINED3DFMT_S1_UINT_D15_UNORM:
1025 *float_depth = depth / (float)0x00007fff;
1026 break;
1028 case WINED3DFMT_D16_UNORM:
1029 *float_depth = depth / (float)0x0000ffff;
1030 break;
1032 case WINED3DFMT_D24_UNORM_S8_UINT:
1033 case WINED3DFMT_X8D24_UNORM:
1034 *float_depth = depth / (float)0x00ffffff;
1035 break;
1037 case WINED3DFMT_D32_UNORM:
1038 *float_depth = depth / (float)0xffffffff;
1039 break;
1041 default:
1042 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1043 return FALSE;
1046 return TRUE;
1049 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1051 const struct wined3d_resource *resource = &surface->container->resource;
1052 struct wined3d_device *device = resource->device;
1053 const struct blit_shader *blitter;
1055 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, WINED3D_BLIT_OP_DEPTH_FILL,
1056 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1057 if (!blitter)
1059 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1060 return WINED3DERR_INVALIDCALL;
1063 return blitter->depth_fill(device, surface, rect, depth);
1066 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1067 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1069 struct wined3d_device *device = src_surface->resource.device;
1071 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1072 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1073 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1074 return WINED3DERR_INVALIDCALL;
1076 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1078 surface_modify_ds_location(dst_surface, dst_location,
1079 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1081 return WINED3D_OK;
1084 /* Context activation is done by the caller. */
1085 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1087 GL_EXTCALL(glDeleteBuffers(1, &surface->pbo));
1088 checkGLcall("glDeleteBuffers(1, &surface->pbo)");
1090 surface->pbo = 0;
1091 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1094 static ULONG surface_resource_incref(struct wined3d_resource *resource)
1096 return wined3d_surface_incref(surface_from_resource(resource));
1099 static ULONG surface_resource_decref(struct wined3d_resource *resource)
1101 return wined3d_surface_decref(surface_from_resource(resource));
1104 static void surface_unload(struct wined3d_resource *resource)
1106 struct wined3d_surface *surface = surface_from_resource(resource);
1107 struct wined3d_renderbuffer_entry *entry, *entry2;
1108 struct wined3d_device *device = resource->device;
1109 const struct wined3d_gl_info *gl_info;
1110 struct wined3d_context *context;
1112 TRACE("surface %p.\n", surface);
1114 context = context_acquire(device, NULL);
1115 gl_info = context->gl_info;
1117 if (resource->pool == WINED3D_POOL_DEFAULT)
1119 /* Default pool resources are supposed to be destroyed before Reset is called.
1120 * Implicit resources stay however. So this means we have an implicit render target
1121 * or depth stencil. The content may be destroyed, but we still have to tear down
1122 * opengl resources, so we cannot leave early.
1124 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1125 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1126 * or the depth stencil into an FBO the texture or render buffer will be removed
1127 * and all flags get lost */
1128 if (resource->usage & WINED3DUSAGE_DEPTHSTENCIL)
1130 surface_validate_location(surface, WINED3D_LOCATION_DISCARDED);
1131 surface_invalidate_location(surface, ~WINED3D_LOCATION_DISCARDED);
1133 else
1135 surface_prepare_system_memory(surface);
1136 memset(surface->resource.heap_memory, 0, surface->resource.size);
1137 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1138 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1141 else
1143 surface_prepare_map_memory(surface);
1144 surface_load_location(surface, context, surface->resource.map_binding);
1145 surface_invalidate_location(surface, ~surface->resource.map_binding);
1148 /* Destroy PBOs, but load them into real sysmem before */
1149 if (surface->pbo)
1150 surface_remove_pbo(surface, gl_info);
1152 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1153 * all application-created targets the application has to release the surface
1154 * before calling _Reset
1156 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1158 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1159 list_remove(&entry->entry);
1160 HeapFree(GetProcessHeap(), 0, entry);
1162 list_init(&surface->renderbuffers);
1163 surface->current_renderbuffer = NULL;
1165 if (surface->rb_multisample)
1167 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1168 surface->rb_multisample = 0;
1170 if (surface->rb_resolved)
1172 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1173 surface->rb_resolved = 0;
1176 context_release(context);
1178 resource_unload(resource);
1181 static HRESULT surface_resource_sub_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx,
1182 struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
1184 ERR("Not supported on sub-resources.\n");
1185 return WINED3DERR_INVALIDCALL;
1188 static HRESULT surface_resource_sub_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx)
1190 ERR("Not supported on sub-resources.\n");
1191 return WINED3DERR_INVALIDCALL;
1194 static const struct wined3d_resource_ops surface_resource_ops =
1196 surface_resource_incref,
1197 surface_resource_decref,
1198 surface_unload,
1199 surface_resource_sub_resource_map,
1200 surface_resource_sub_resource_unmap,
1203 static const struct wined3d_surface_ops surface_ops =
1205 surface_private_setup,
1206 surface_unmap,
1209 /*****************************************************************************
1210 * Initializes the GDI surface, aka creates the DIB section we render to
1211 * The DIB section creation is done by calling GetDC, which will create the
1212 * section and releasing the dc to allow the app to use it. The dib section
1213 * will stay until the surface is released
1215 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1216 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1217 * avoid confusion in the shared surface code.
1219 * Returns:
1220 * WINED3D_OK on success
1221 * The return values of called methods on failure
1223 *****************************************************************************/
1224 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1226 HRESULT hr;
1228 TRACE("surface %p.\n", surface);
1230 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1232 ERR("Overlays not yet supported by GDI surfaces.\n");
1233 return WINED3DERR_INVALIDCALL;
1236 /* Sysmem textures have memory already allocated - release it,
1237 * this avoids an unnecessary memcpy. */
1238 hr = surface_create_dib_section(surface);
1239 if (FAILED(hr))
1240 return hr;
1241 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1243 /* We don't mind the nonpow2 stuff in GDI. */
1244 surface->pow2Width = surface->resource.width;
1245 surface->pow2Height = surface->resource.height;
1247 return WINED3D_OK;
1250 static void gdi_surface_unmap(struct wined3d_surface *surface)
1252 TRACE("surface %p.\n", surface);
1254 /* Tell the swapchain to update the screen. */
1255 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
1256 x11_copy_to_screen(surface->container->swapchain, &surface->lockedRect);
1258 memset(&surface->lockedRect, 0, sizeof(RECT));
1261 static const struct wined3d_surface_ops gdi_surface_ops =
1263 gdi_surface_private_setup,
1264 gdi_surface_unmap,
1267 /* This call just downloads data, the caller is responsible for binding the
1268 * correct texture. */
1269 /* Context activation is done by the caller. */
1270 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1271 DWORD dst_location)
1273 const struct wined3d_format *format = surface->resource.format;
1274 struct wined3d_bo_address data;
1276 /* Only support read back of converted P8 surfaces. */
1277 if (surface->container->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1279 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1280 return;
1283 surface_get_memory(surface, &data, dst_location);
1285 if (surface->container->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
1287 TRACE("(%p) : Calling glGetCompressedTexImage level %d, format %#x, type %#x, data %p.\n",
1288 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1290 if (data.buffer_object)
1292 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1293 checkGLcall("glBindBuffer");
1294 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, NULL));
1295 checkGLcall("glGetCompressedTexImage");
1296 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1297 checkGLcall("glBindBuffer");
1299 else
1301 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target,
1302 surface->texture_level, data.addr));
1303 checkGLcall("glGetCompressedTexImage");
1306 else
1308 void *mem;
1309 GLenum gl_format = format->glFormat;
1310 GLenum gl_type = format->glType;
1311 int src_pitch = 0;
1312 int dst_pitch = 0;
1314 if (surface->flags & SFLAG_NONPOW2)
1316 unsigned char alignment = surface->resource.device->surface_alignment;
1317 src_pitch = format->byte_count * surface->pow2Width;
1318 dst_pitch = wined3d_surface_get_pitch(surface);
1319 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1320 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1322 else
1324 mem = data.addr;
1327 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1328 surface, surface->texture_level, gl_format, gl_type, mem);
1330 if (data.buffer_object)
1332 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1333 checkGLcall("glBindBuffer");
1335 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1336 gl_format, gl_type, NULL);
1337 checkGLcall("glGetTexImage");
1339 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1340 checkGLcall("glBindBuffer");
1342 else
1344 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1345 gl_format, gl_type, mem);
1346 checkGLcall("glGetTexImage");
1349 if (surface->flags & SFLAG_NONPOW2)
1351 const BYTE *src_data;
1352 BYTE *dst_data;
1353 UINT y;
1355 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1356 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1357 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1359 * We're doing this...
1361 * instead of boxing the texture :
1362 * |<-texture width ->| -->pow2width| /\
1363 * |111111111111111111| | |
1364 * |222 Texture 222222| boxed empty | texture height
1365 * |3333 Data 33333333| | |
1366 * |444444444444444444| | \/
1367 * ----------------------------------- |
1368 * | boxed empty | boxed empty | pow2height
1369 * | | | \/
1370 * -----------------------------------
1373 * we're repacking the data to the expected texture width
1375 * |<-texture width ->| -->pow2width| /\
1376 * |111111111111111111222222222222222| |
1377 * |222333333333333333333444444444444| texture height
1378 * |444444 | |
1379 * | | \/
1380 * | | |
1381 * | empty | pow2height
1382 * | | \/
1383 * -----------------------------------
1385 * == is the same as
1387 * |<-texture width ->| /\
1388 * |111111111111111111|
1389 * |222222222222222222|texture height
1390 * |333333333333333333|
1391 * |444444444444444444| \/
1392 * --------------------
1394 * This also means that any references to surface memory should work with the data as if it were a
1395 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1397 * internally the texture is still stored in a boxed format so any references to textureName will
1398 * get a boxed texture with width pow2width and not a texture of width resource.width.
1400 * Performance should not be an issue, because applications normally do not lock the surfaces when
1401 * rendering. If an app does, the WINED3D_TEXTURE_DYNAMIC_MAP flag will kick in and the memory copy
1402 * won't be released, and doesn't have to be re-read. */
1403 src_data = mem;
1404 dst_data = data.addr;
1405 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1406 for (y = 0; y < surface->resource.height; ++y)
1408 memcpy(dst_data, src_data, dst_pitch);
1409 src_data += src_pitch;
1410 dst_data += dst_pitch;
1413 HeapFree(GetProcessHeap(), 0, mem);
1418 /* This call just uploads data, the caller is responsible for binding the
1419 * correct texture. */
1420 /* Context activation is done by the caller. */
1421 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1422 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1423 BOOL srgb, const struct wined3d_const_bo_address *data)
1425 UINT update_w = src_rect->right - src_rect->left;
1426 UINT update_h = src_rect->bottom - src_rect->top;
1428 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1429 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1430 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1432 if (surface->resource.map_count)
1434 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
1435 surface->container->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
1438 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
1440 update_h *= format->height_scale.numerator;
1441 update_h /= format->height_scale.denominator;
1444 if (data->buffer_object)
1446 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
1447 checkGLcall("glBindBuffer");
1450 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
1452 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1453 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1454 const BYTE *addr = data->addr;
1455 GLenum internal;
1457 addr += (src_rect->top / format->block_height) * src_pitch;
1458 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1460 if (srgb)
1461 internal = format->glGammaInternal;
1462 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1463 && wined3d_resource_is_offscreen(&surface->container->resource))
1464 internal = format->rtInternal;
1465 else
1466 internal = format->glInternal;
1468 TRACE("glCompressedTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, "
1469 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1470 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1472 if (row_length == src_pitch)
1474 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1475 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1477 else
1479 UINT row, y;
1481 /* glCompressedTexSubImage2D() ignores pixel store state, so we
1482 * can't use the unpack row length like for glTexSubImage2D. */
1483 for (row = 0, y = dst_point->y; row < row_count; ++row)
1485 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1486 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1487 y += format->block_height;
1488 addr += src_pitch;
1491 checkGLcall("glCompressedTexSubImage2D");
1493 else
1495 const BYTE *addr = data->addr;
1497 addr += src_rect->top * src_pitch;
1498 addr += src_rect->left * format->byte_count;
1500 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1501 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1502 update_w, update_h, format->glFormat, format->glType, addr);
1504 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1505 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1506 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1507 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1508 checkGLcall("glTexSubImage2D");
1511 if (data->buffer_object)
1513 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1514 checkGLcall("glBindBuffer");
1517 if (wined3d_settings.strict_draw_ordering)
1518 gl_info->gl_ops.gl.p_glFlush();
1520 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1522 struct wined3d_device *device = surface->resource.device;
1523 unsigned int i;
1525 for (i = 0; i < device->context_count; ++i)
1527 context_surface_update(device->contexts[i], surface);
1532 static BOOL surface_check_block_align(struct wined3d_surface *surface, const struct wined3d_box *box)
1534 UINT width_mask, height_mask;
1536 if (!box->left && !box->top
1537 && box->right == surface->resource.width
1538 && box->bottom == surface->resource.height)
1539 return TRUE;
1541 /* This assumes power of two block sizes, but NPOT block sizes would be
1542 * silly anyway. */
1543 width_mask = surface->resource.format->block_width - 1;
1544 height_mask = surface->resource.format->block_height - 1;
1546 if (!(box->left & width_mask) && !(box->top & height_mask)
1547 && !(box->right & width_mask) && !(box->bottom & height_mask))
1548 return TRUE;
1550 return FALSE;
1553 static BOOL surface_check_block_align_rect(struct wined3d_surface *surface, const RECT *rect)
1555 struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
1557 return surface_check_block_align(surface, &box);
1560 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1561 struct wined3d_surface *src_surface, const RECT *src_rect)
1563 const struct wined3d_format *src_format;
1564 const struct wined3d_format *dst_format;
1565 unsigned int src_fmt_flags, dst_fmt_flags;
1566 const struct wined3d_gl_info *gl_info;
1567 struct wined3d_context *context;
1568 struct wined3d_bo_address data;
1569 UINT update_w, update_h;
1570 UINT dst_w, dst_h;
1571 RECT r, dst_rect;
1572 UINT src_pitch;
1573 POINT p;
1575 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1576 dst_surface, wine_dbgstr_point(dst_point),
1577 src_surface, wine_dbgstr_rect(src_rect));
1579 src_format = src_surface->resource.format;
1580 dst_format = dst_surface->resource.format;
1581 src_fmt_flags = src_surface->container->resource.format_flags;
1582 dst_fmt_flags = dst_surface->container->resource.format_flags;
1584 if (src_format->id != dst_format->id)
1586 WARN("Source and destination surfaces should have the same format.\n");
1587 return WINED3DERR_INVALIDCALL;
1590 if (!dst_point)
1592 p.x = 0;
1593 p.y = 0;
1594 dst_point = &p;
1596 else if (dst_point->x < 0 || dst_point->y < 0)
1598 WARN("Invalid destination point.\n");
1599 return WINED3DERR_INVALIDCALL;
1602 if (!src_rect)
1604 r.left = 0;
1605 r.top = 0;
1606 r.right = src_surface->resource.width;
1607 r.bottom = src_surface->resource.height;
1608 src_rect = &r;
1610 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1611 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1613 WARN("Invalid source rectangle.\n");
1614 return WINED3DERR_INVALIDCALL;
1617 dst_w = dst_surface->resource.width;
1618 dst_h = dst_surface->resource.height;
1620 update_w = src_rect->right - src_rect->left;
1621 update_h = src_rect->bottom - src_rect->top;
1623 if (update_w > dst_w || dst_point->x > dst_w - update_w
1624 || update_h > dst_h || dst_point->y > dst_h - update_h)
1626 WARN("Destination out of bounds.\n");
1627 return WINED3DERR_INVALIDCALL;
1630 if ((src_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(src_surface, src_rect))
1632 WARN("Source rectangle not block-aligned.\n");
1633 return WINED3DERR_INVALIDCALL;
1636 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1637 if ((dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(dst_surface, &dst_rect))
1639 WARN("Destination rectangle not block-aligned.\n");
1640 return WINED3DERR_INVALIDCALL;
1643 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1644 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_surface->container, FALSE))
1645 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1647 context = context_acquire(dst_surface->resource.device, NULL);
1648 gl_info = context->gl_info;
1650 /* Only load the surface for partial updates. For newly allocated texture
1651 * the texture wouldn't be the current location, and we'd upload zeroes
1652 * just to overwrite them again. */
1653 if (update_w == dst_w && update_h == dst_h)
1654 wined3d_texture_prepare_texture(dst_surface->container, context, FALSE);
1655 else
1656 surface_load_location(dst_surface, context, WINED3D_LOCATION_TEXTURE_RGB);
1657 wined3d_texture_bind_and_dirtify(dst_surface->container, context, FALSE);
1659 surface_get_memory(src_surface, &data, src_surface->locations);
1660 src_pitch = wined3d_surface_get_pitch(src_surface);
1662 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1663 src_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1665 context_release(context);
1667 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1668 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1670 return WINED3D_OK;
1673 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1674 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1675 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1676 /* Context activation is done by the caller. */
1677 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1679 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1680 struct wined3d_renderbuffer_entry *entry;
1681 GLuint renderbuffer = 0;
1682 unsigned int src_width, src_height;
1683 unsigned int width, height;
1685 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1687 width = rt->pow2Width;
1688 height = rt->pow2Height;
1690 else
1692 width = surface->pow2Width;
1693 height = surface->pow2Height;
1696 src_width = surface->pow2Width;
1697 src_height = surface->pow2Height;
1699 /* A depth stencil smaller than the render target is not valid */
1700 if (width > src_width || height > src_height) return;
1702 /* Remove any renderbuffer set if the sizes match */
1703 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1704 || (width == src_width && height == src_height))
1706 surface->current_renderbuffer = NULL;
1707 return;
1710 /* Look if we've already got a renderbuffer of the correct dimensions */
1711 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1713 if (entry->width == width && entry->height == height)
1715 renderbuffer = entry->id;
1716 surface->current_renderbuffer = entry;
1717 break;
1721 if (!renderbuffer)
1723 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1724 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1725 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1726 surface->resource.format->glInternal, width, height);
1728 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1729 entry->width = width;
1730 entry->height = height;
1731 entry->id = renderbuffer;
1732 list_add_head(&surface->renderbuffers, &entry->entry);
1734 surface->current_renderbuffer = entry;
1737 checkGLcall("set_compatible_renderbuffer");
1740 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1742 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1744 TRACE("surface %p.\n", surface);
1746 if (!swapchain)
1748 ERR("Surface %p is not on a swapchain.\n", surface);
1749 return GL_NONE;
1752 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
1754 if (swapchain->render_to_fbo)
1756 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1757 return GL_COLOR_ATTACHMENT0;
1759 TRACE("Returning GL_BACK\n");
1760 return GL_BACK;
1762 else if (surface->container == swapchain->front_buffer)
1764 TRACE("Returning GL_FRONT\n");
1765 return GL_FRONT;
1768 FIXME("Higher back buffer, returning GL_BACK\n");
1769 return GL_BACK;
1772 /* Context activation is done by the caller. */
1773 void surface_load(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
1775 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1777 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1779 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
1780 ERR("Not supported on scratch surfaces.\n");
1782 if (surface->locations & location)
1784 TRACE("surface is already in texture\n");
1785 return;
1787 TRACE("Reloading because surface is dirty.\n");
1789 surface_load_location(surface, context, location);
1790 surface_evict_sysmem(surface);
1793 /* See also float_16_to_32() in wined3d_private.h */
1794 static inline unsigned short float_32_to_16(const float *in)
1796 int exp = 0;
1797 float tmp = fabsf(*in);
1798 unsigned int mantissa;
1799 unsigned short ret;
1801 /* Deal with special numbers */
1802 if (*in == 0.0f)
1803 return 0x0000;
1804 if (isnan(*in))
1805 return 0x7c01;
1806 if (isinf(*in))
1807 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1809 if (tmp < (float)(1u << 10))
1813 tmp = tmp * 2.0f;
1814 exp--;
1815 } while (tmp < (float)(1u << 10));
1817 else if (tmp >= (float)(1u << 11))
1821 tmp /= 2.0f;
1822 exp++;
1823 } while (tmp >= (float)(1u << 11));
1826 mantissa = (unsigned int)tmp;
1827 if (tmp - mantissa >= 0.5f)
1828 ++mantissa; /* Round to nearest, away from zero. */
1830 exp += 10; /* Normalize the mantissa. */
1831 exp += 15; /* Exponent is encoded with excess 15. */
1833 if (exp > 30) /* too big */
1835 ret = 0x7c00; /* INF */
1837 else if (exp <= 0)
1839 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1840 while (exp <= 0)
1842 mantissa = mantissa >> 1;
1843 ++exp;
1845 ret = mantissa & 0x3ff;
1847 else
1849 ret = (exp << 10) | (mantissa & 0x3ff);
1852 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1853 return ret;
1856 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
1858 TRACE("surface %p, container %p.\n", surface, surface->container);
1860 return wined3d_texture_incref(surface->container);
1863 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
1865 TRACE("surface %p, container %p.\n", surface, surface->container);
1867 return wined3d_texture_decref(surface->container);
1870 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
1872 TRACE("surface %p.\n", surface);
1874 if (!surface->resource.device->d3d_initialized)
1876 ERR("D3D not initialized.\n");
1877 return;
1880 wined3d_texture_preload(surface->container);
1883 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
1885 TRACE("surface %p.\n", surface);
1887 return surface->resource.parent;
1890 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
1892 TRACE("surface %p.\n", surface);
1894 return &surface->resource;
1897 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
1899 unsigned int alignment;
1900 DWORD pitch;
1902 TRACE("surface %p.\n", surface);
1904 if (surface->pitch)
1905 return surface->pitch;
1907 alignment = surface->resource.device->surface_alignment;
1908 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
1909 pitch = (pitch + alignment - 1) & ~(alignment - 1);
1911 TRACE("Returning %u.\n", pitch);
1913 return pitch;
1916 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
1918 LONG w, h;
1920 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
1922 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1924 WARN("Not an overlay surface.\n");
1925 return WINEDDERR_NOTAOVERLAYSURFACE;
1928 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
1929 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
1930 surface->overlay_destrect.left = x;
1931 surface->overlay_destrect.top = y;
1932 surface->overlay_destrect.right = x + w;
1933 surface->overlay_destrect.bottom = y + h;
1935 return WINED3D_OK;
1938 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
1940 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
1942 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1944 TRACE("Not an overlay surface.\n");
1945 return WINEDDERR_NOTAOVERLAYSURFACE;
1948 if (!surface->overlay_dest)
1950 TRACE("Overlay not visible.\n");
1951 *x = 0;
1952 *y = 0;
1953 return WINEDDERR_OVERLAYNOTVISIBLE;
1956 *x = surface->overlay_destrect.left;
1957 *y = surface->overlay_destrect.top;
1959 TRACE("Returning position %d, %d.\n", *x, *y);
1961 return WINED3D_OK;
1964 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
1965 DWORD flags, struct wined3d_surface *ref)
1967 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
1969 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1971 TRACE("Not an overlay surface.\n");
1972 return WINEDDERR_NOTAOVERLAYSURFACE;
1975 return WINED3D_OK;
1978 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
1979 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
1981 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
1982 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
1984 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1986 WARN("Not an overlay surface.\n");
1987 return WINEDDERR_NOTAOVERLAYSURFACE;
1989 else if (!dst_surface)
1991 WARN("Dest surface is NULL.\n");
1992 return WINED3DERR_INVALIDCALL;
1995 surface_get_rect(surface, src_rect, &surface->overlay_srcrect);
1996 surface_get_rect(dst_surface, dst_rect, &surface->overlay_destrect);
1998 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2000 surface->overlay_dest = NULL;
2001 list_remove(&surface->overlay_entry);
2004 if (flags & WINEDDOVER_SHOW)
2006 if (surface->overlay_dest != dst_surface)
2008 surface->overlay_dest = dst_surface;
2009 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2012 else if (flags & WINEDDOVER_HIDE)
2014 /* tests show that the rectangles are erased on hide */
2015 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2016 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2017 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2018 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2019 surface->overlay_dest = NULL;
2022 return WINED3D_OK;
2025 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface,
2026 const struct wined3d_gl_info *gl_info, void *mem, unsigned int pitch)
2028 struct wined3d_resource *texture_resource = &surface->container->resource;
2029 unsigned int width, height;
2030 BOOL create_dib = FALSE;
2031 DWORD valid_location = 0;
2032 HRESULT hr;
2034 if (surface->flags & SFLAG_DIBSECTION)
2036 DeleteDC(surface->hDC);
2037 DeleteObject(surface->dib.DIBsection);
2038 surface->dib.bitmap_data = NULL;
2039 surface->flags &= ~SFLAG_DIBSECTION;
2040 create_dib = TRUE;
2043 surface->locations = 0;
2044 wined3d_resource_free_sysmem(&surface->resource);
2046 width = texture_resource->width;
2047 height = texture_resource->height;
2048 surface->resource.width = width;
2049 surface->resource.height = height;
2050 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2051 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2053 surface->pow2Width = width;
2054 surface->pow2Height = height;
2056 else
2058 surface->pow2Width = surface->pow2Height = 1;
2059 while (surface->pow2Width < width)
2060 surface->pow2Width <<= 1;
2061 while (surface->pow2Height < height)
2062 surface->pow2Height <<= 1;
2065 if (surface->pow2Width != width || surface->pow2Height != height)
2066 surface->flags |= SFLAG_NONPOW2;
2067 else
2068 surface->flags &= ~SFLAG_NONPOW2;
2070 if ((surface->user_memory = mem))
2072 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
2073 valid_location = WINED3D_LOCATION_USER_MEMORY;
2075 surface->pitch = pitch;
2076 surface->resource.format = texture_resource->format;
2077 surface->resource.multisample_type = texture_resource->multisample_type;
2078 surface->resource.multisample_quality = texture_resource->multisample_quality;
2079 if (surface->pitch)
2081 surface->resource.size = height * surface->pitch;
2083 else
2085 /* User memory surfaces don't have the regular surface alignment. */
2086 surface->resource.size = wined3d_format_calculate_size(texture_resource->format,
2087 1, width, height, 1);
2088 surface->pitch = wined3d_format_calculate_pitch(texture_resource->format, width);
2091 /* The format might be changed to a format that needs conversion.
2092 * If the surface didn't use PBOs previously but could now, don't
2093 * change it - whatever made us not use PBOs might come back, e.g.
2094 * color keys. */
2095 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2096 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2098 if (create_dib)
2100 if (FAILED(hr = surface_create_dib_section(surface)))
2102 ERR("Failed to create dib section, hr %#x.\n", hr);
2103 return hr;
2105 if (!valid_location)
2106 valid_location = WINED3D_LOCATION_DIB;
2109 if (!valid_location)
2111 surface_prepare_system_memory(surface);
2112 valid_location = WINED3D_LOCATION_SYSMEM;
2115 surface_validate_location(surface, valid_location);
2117 return WINED3D_OK;
2120 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2121 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2123 unsigned short *dst_s;
2124 const float *src_f;
2125 unsigned int x, y;
2127 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2129 for (y = 0; y < h; ++y)
2131 src_f = (const float *)(src + y * pitch_in);
2132 dst_s = (unsigned short *) (dst + y * pitch_out);
2133 for (x = 0; x < w; ++x)
2135 dst_s[x] = float_32_to_16(src_f + x);
2140 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2141 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2143 static const unsigned char convert_5to8[] =
2145 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2146 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2147 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2148 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2150 static const unsigned char convert_6to8[] =
2152 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2153 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2154 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2155 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2156 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2157 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2158 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2159 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2161 unsigned int x, y;
2163 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2165 for (y = 0; y < h; ++y)
2167 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2168 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2169 for (x = 0; x < w; ++x)
2171 WORD pixel = src_line[x];
2172 dst_line[x] = 0xff000000u
2173 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
2174 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
2175 | convert_5to8[(pixel & 0x001fu)];
2180 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2181 * in both cases we're just setting the X / Alpha channel to 0xff. */
2182 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2183 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2185 unsigned int x, y;
2187 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2189 for (y = 0; y < h; ++y)
2191 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2192 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2194 for (x = 0; x < w; ++x)
2196 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2201 static inline BYTE cliptobyte(int x)
2203 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2206 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2207 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2209 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2210 unsigned int x, y;
2212 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2214 for (y = 0; y < h; ++y)
2216 const BYTE *src_line = src + y * pitch_in;
2217 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2218 for (x = 0; x < w; ++x)
2220 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2221 * C = Y - 16; D = U - 128; E = V - 128;
2222 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2223 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2224 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2225 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2226 * U and V are shared between the pixels. */
2227 if (!(x & 1)) /* For every even pixel, read new U and V. */
2229 d = (int) src_line[1] - 128;
2230 e = (int) src_line[3] - 128;
2231 r2 = 409 * e + 128;
2232 g2 = - 100 * d - 208 * e + 128;
2233 b2 = 516 * d + 128;
2235 c2 = 298 * ((int) src_line[0] - 16);
2236 dst_line[x] = 0xff000000
2237 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2238 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2239 | cliptobyte((c2 + b2) >> 8); /* blue */
2240 /* Scale RGB values to 0..255 range,
2241 * then clip them if still not in range (may be negative),
2242 * then shift them within DWORD if necessary. */
2243 src_line += 2;
2248 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2249 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2251 unsigned int x, y;
2252 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2254 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2256 for (y = 0; y < h; ++y)
2258 const BYTE *src_line = src + y * pitch_in;
2259 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2260 for (x = 0; x < w; ++x)
2262 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2263 * C = Y - 16; D = U - 128; E = V - 128;
2264 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2265 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2266 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2267 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2268 * U and V are shared between the pixels. */
2269 if (!(x & 1)) /* For every even pixel, read new U and V. */
2271 d = (int) src_line[1] - 128;
2272 e = (int) src_line[3] - 128;
2273 r2 = 409 * e + 128;
2274 g2 = - 100 * d - 208 * e + 128;
2275 b2 = 516 * d + 128;
2277 c2 = 298 * ((int) src_line[0] - 16);
2278 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2279 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2280 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2281 /* Scale RGB values to 0..255 range,
2282 * then clip them if still not in range (may be negative),
2283 * then shift them within DWORD if necessary. */
2284 src_line += 2;
2289 struct d3dfmt_converter_desc
2291 enum wined3d_format_id from, to;
2292 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2295 static const struct d3dfmt_converter_desc converters[] =
2297 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2298 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2299 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2300 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2301 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2302 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2305 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2306 enum wined3d_format_id to)
2308 unsigned int i;
2310 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2312 if (converters[i].from == from && converters[i].to == to)
2313 return &converters[i];
2316 return NULL;
2319 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2321 struct wined3d_map_desc src_map, dst_map;
2322 const struct d3dfmt_converter_desc *conv;
2323 struct wined3d_texture *ret = NULL;
2324 struct wined3d_resource_desc desc;
2325 struct wined3d_surface *dst;
2327 conv = find_converter(source->resource.format->id, to_fmt);
2328 if (!conv)
2330 FIXME("Cannot find a conversion function from format %s to %s.\n",
2331 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2332 return NULL;
2335 /* FIXME: Multisampled conversion? */
2336 wined3d_resource_get_desc(&source->resource, &desc);
2337 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2338 desc.format = to_fmt;
2339 desc.usage = 0;
2340 desc.pool = WINED3D_POOL_SCRATCH;
2341 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2342 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, NULL, &wined3d_null_parent_ops, &ret)))
2344 ERR("Failed to create a destination surface for conversion.\n");
2345 return NULL;
2347 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2349 memset(&src_map, 0, sizeof(src_map));
2350 memset(&dst_map, 0, sizeof(dst_map));
2352 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2354 ERR("Failed to lock the source surface.\n");
2355 wined3d_texture_decref(ret);
2356 return NULL;
2358 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2360 ERR("Failed to lock the destination surface.\n");
2361 wined3d_surface_unmap(source);
2362 wined3d_texture_decref(ret);
2363 return NULL;
2366 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2367 source->resource.width, source->resource.height);
2369 wined3d_surface_unmap(dst);
2370 wined3d_surface_unmap(source);
2372 return ret;
2375 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2376 unsigned int bpp, UINT pitch, DWORD color)
2378 BYTE *first;
2379 unsigned int x, y;
2381 /* Do first row */
2383 #define COLORFILL_ROW(type) \
2384 do { \
2385 type *d = (type *)buf; \
2386 for (x = 0; x < width; ++x) \
2387 d[x] = (type)color; \
2388 } while(0)
2390 switch (bpp)
2392 case 1:
2393 COLORFILL_ROW(BYTE);
2394 break;
2396 case 2:
2397 COLORFILL_ROW(WORD);
2398 break;
2400 case 3:
2402 BYTE *d = buf;
2403 for (x = 0; x < width; ++x, d += 3)
2405 d[0] = (color ) & 0xff;
2406 d[1] = (color >> 8) & 0xff;
2407 d[2] = (color >> 16) & 0xff;
2409 break;
2411 case 4:
2412 COLORFILL_ROW(DWORD);
2413 break;
2415 default:
2416 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2417 return WINED3DERR_NOTAVAILABLE;
2420 #undef COLORFILL_ROW
2422 /* Now copy first row. */
2423 first = buf;
2424 for (y = 1; y < height; ++y)
2426 buf += pitch;
2427 memcpy(buf, first, width * bpp);
2430 return WINED3D_OK;
2433 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2435 return surface_from_resource(resource);
2438 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2440 TRACE("surface %p.\n", surface);
2442 if (!surface->resource.map_count)
2444 WARN("Trying to unmap unmapped surface.\n");
2445 return WINEDDERR_NOTLOCKED;
2447 --surface->resource.map_count;
2449 surface->surface_ops->surface_unmap(surface);
2451 return WINED3D_OK;
2454 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2455 struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
2457 const struct wined3d_format *format = surface->resource.format;
2458 unsigned int fmt_flags = surface->container->resource.format_flags;
2459 struct wined3d_device *device = surface->resource.device;
2460 struct wined3d_context *context;
2461 const struct wined3d_gl_info *gl_info;
2462 BYTE *base_memory;
2464 TRACE("surface %p, map_desc %p, box %p, flags %#x.\n",
2465 surface, map_desc, box, flags);
2467 if (surface->resource.map_count)
2469 WARN("Surface is already mapped.\n");
2470 return WINED3DERR_INVALIDCALL;
2473 if ((fmt_flags & WINED3DFMT_FLAG_BLOCKS) && box
2474 && !surface_check_block_align(surface, box))
2476 WARN("Map rect %p is misaligned for %ux%u blocks.\n",
2477 box, format->block_width, format->block_height);
2479 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2480 return WINED3DERR_INVALIDCALL;
2483 ++surface->resource.map_count;
2485 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2486 WARN("Trying to lock unlockable surface.\n");
2488 /* Performance optimization: Count how often a surface is mapped, if it is
2489 * mapped regularly do not throw away the system memory copy. This avoids
2490 * the need to download the surface from OpenGL all the time. The surface
2491 * is still downloaded if the OpenGL texture is changed. Note that this
2492 * only really makes sense for managed textures.*/
2493 if (!(surface->container->flags & WINED3D_TEXTURE_DYNAMIC_MAP)
2494 && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2496 if (++surface->lockCount > MAXLOCKCOUNT)
2498 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2499 surface->container->flags |= WINED3D_TEXTURE_DYNAMIC_MAP;
2503 surface_prepare_map_memory(surface);
2504 if (flags & WINED3D_MAP_DISCARD)
2506 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2507 wined3d_debug_location(surface->resource.map_binding));
2508 surface_validate_location(surface, surface->resource.map_binding);
2510 else
2512 struct wined3d_context *context = NULL;
2514 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2515 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2517 if (surface->resource.device->d3d_initialized)
2518 context = context_acquire(surface->resource.device, NULL);
2519 surface_load_location(surface, context, surface->resource.map_binding);
2520 if (context)
2521 context_release(context);
2524 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2525 surface_invalidate_location(surface, ~surface->resource.map_binding);
2527 switch (surface->resource.map_binding)
2529 case WINED3D_LOCATION_SYSMEM:
2530 base_memory = surface->resource.heap_memory;
2531 break;
2533 case WINED3D_LOCATION_USER_MEMORY:
2534 base_memory = surface->user_memory;
2535 break;
2537 case WINED3D_LOCATION_DIB:
2538 base_memory = surface->dib.bitmap_data;
2539 break;
2541 case WINED3D_LOCATION_BUFFER:
2542 context = context_acquire(device, NULL);
2543 gl_info = context->gl_info;
2545 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
2546 base_memory = GL_EXTCALL(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE));
2547 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
2548 checkGLcall("map PBO");
2550 context_release(context);
2551 break;
2553 default:
2554 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2555 base_memory = NULL;
2558 if (fmt_flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2559 map_desc->row_pitch = surface->resource.width * format->byte_count;
2560 else
2561 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2562 map_desc->slice_pitch = surface->resource.height * map_desc->row_pitch;
2564 if (!box)
2566 map_desc->data = base_memory;
2567 surface->lockedRect.left = 0;
2568 surface->lockedRect.top = 0;
2569 surface->lockedRect.right = surface->resource.width;
2570 surface->lockedRect.bottom = surface->resource.height;
2572 else
2574 if ((fmt_flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2576 /* Compressed textures are block based, so calculate the offset of
2577 * the block that contains the top-left pixel of the locked rectangle. */
2578 map_desc->data = base_memory
2579 + ((box->top / format->block_height) * map_desc->row_pitch)
2580 + ((box->left / format->block_width) * format->block_byte_count);
2582 else
2584 map_desc->data = base_memory
2585 + (map_desc->row_pitch * box->top)
2586 + (box->left * format->byte_count);
2588 surface->lockedRect.left = box->left;
2589 surface->lockedRect.top = box->top;
2590 surface->lockedRect.right = box->right;
2591 surface->lockedRect.bottom = box->bottom;
2594 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2595 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2597 return WINED3D_OK;
2600 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
2602 HRESULT hr;
2603 struct wined3d_device *device = surface->resource.device;
2604 struct wined3d_context *context = NULL;
2606 TRACE("surface %p, dc %p.\n", surface, dc);
2608 /* Give more detailed info for ddraw. */
2609 if (surface->flags & SFLAG_DCINUSE)
2610 return WINEDDERR_DCALREADYCREATED;
2612 /* Can't GetDC if the surface is locked. */
2613 if (surface->resource.map_count)
2614 return WINED3DERR_INVALIDCALL;
2616 if (device->d3d_initialized)
2617 context = context_acquire(surface->resource.device, NULL);
2619 /* Create a DIB section if there isn't a dc yet. */
2620 if (!surface->hDC)
2622 if (FAILED(hr = surface_create_dib_section(surface)))
2624 if (context)
2625 context_release(context);
2626 return WINED3DERR_INVALIDCALL;
2628 if (!(surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2629 || surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2630 || surface->pbo))
2631 surface->resource.map_binding = WINED3D_LOCATION_DIB;
2634 surface_load_location(surface, context, WINED3D_LOCATION_DIB);
2635 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
2637 if (context)
2638 context_release(context);
2640 surface->flags |= SFLAG_DCINUSE;
2641 surface->resource.map_count++;
2643 *dc = surface->hDC;
2644 TRACE("Returning dc %p.\n", *dc);
2646 return WINED3D_OK;
2649 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
2651 TRACE("surface %p, dc %p.\n", surface, dc);
2653 if (!(surface->flags & SFLAG_DCINUSE))
2654 return WINEDDERR_NODC;
2656 if (surface->hDC != dc)
2658 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
2659 dc, surface->hDC);
2660 return WINEDDERR_NODC;
2663 surface->resource.map_count--;
2664 surface->flags &= ~SFLAG_DCINUSE;
2666 if (surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2667 || (surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2668 && surface->resource.map_binding != WINED3D_LOCATION_DIB))
2670 /* The game Salammbo modifies the surface contents without mapping the surface between
2671 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
2672 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
2673 * Do not only copy the DIB to the map location, but also make sure the map location is
2674 * copied back to the DIB in the next getdc call.
2676 * The same consideration applies to user memory surfaces. */
2677 struct wined3d_device *device = surface->resource.device;
2678 struct wined3d_context *context = NULL;
2680 if (device->d3d_initialized)
2681 context = context_acquire(device, NULL);
2683 surface_load_location(surface, context, surface->resource.map_binding);
2684 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
2685 if (context)
2686 context_release(context);
2689 return WINED3D_OK;
2692 static void read_from_framebuffer(struct wined3d_surface *surface,
2693 struct wined3d_context *old_ctx, DWORD dst_location)
2695 struct wined3d_device *device = surface->resource.device;
2696 const struct wined3d_gl_info *gl_info;
2697 struct wined3d_context *context = old_ctx;
2698 struct wined3d_surface *restore_rt = NULL;
2699 BYTE *mem;
2700 BYTE *row, *top, *bottom;
2701 int i;
2702 BOOL srcIsUpsideDown;
2703 struct wined3d_bo_address data;
2705 surface_get_memory(surface, &data, dst_location);
2707 if (surface != old_ctx->current_rt)
2709 restore_rt = old_ctx->current_rt;
2710 context = context_acquire(device, surface);
2713 context_apply_blit_state(context, device);
2714 gl_info = context->gl_info;
2716 /* Select the correct read buffer, and give some debug output.
2717 * There is no need to keep track of the current read buffer or reset it, every part of the code
2718 * that reads sets the read buffer as desired.
2720 if (wined3d_resource_is_offscreen(&surface->container->resource))
2722 /* Mapping the primary render target which is not on a swapchain.
2723 * Read from the back buffer. */
2724 TRACE("Mapping offscreen render target.\n");
2725 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2726 srcIsUpsideDown = TRUE;
2728 else
2730 /* Onscreen surfaces are always part of a swapchain */
2731 GLenum buffer = surface_get_gl_buffer(surface);
2732 TRACE("Mapping %#x buffer.\n", buffer);
2733 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2734 checkGLcall("glReadBuffer");
2735 srcIsUpsideDown = FALSE;
2738 if (data.buffer_object)
2740 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
2741 checkGLcall("glBindBuffer");
2744 /* Setup pixel store pack state -- to glReadPixels into the correct place */
2745 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH,
2746 wined3d_surface_get_pitch(surface) / surface->resource.format->byte_count);
2747 checkGLcall("glPixelStorei");
2749 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
2750 surface->resource.width, surface->resource.height,
2751 surface->resource.format->glFormat,
2752 surface->resource.format->glType, data.addr);
2753 checkGLcall("glReadPixels");
2755 /* Reset previous pixel store pack state */
2756 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
2757 checkGLcall("glPixelStorei");
2759 if (!srcIsUpsideDown)
2761 /* glReadPixels returns the image upside down, and there is no way to prevent this.
2762 * Flip the lines in software. */
2763 UINT pitch = wined3d_surface_get_pitch(surface);
2765 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
2766 goto error;
2768 if (data.buffer_object)
2770 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
2771 checkGLcall("glMapBuffer");
2773 else
2774 mem = data.addr;
2776 top = mem;
2777 bottom = mem + pitch * (surface->resource.height - 1);
2778 for (i = 0; i < surface->resource.height / 2; i++)
2780 memcpy(row, top, pitch);
2781 memcpy(top, bottom, pitch);
2782 memcpy(bottom, row, pitch);
2783 top += pitch;
2784 bottom -= pitch;
2786 HeapFree(GetProcessHeap(), 0, row);
2788 if (data.buffer_object)
2789 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
2792 error:
2793 if (data.buffer_object)
2795 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2796 checkGLcall("glBindBuffer");
2799 if (restore_rt)
2800 context_restore(context, restore_rt);
2803 /* Read the framebuffer contents into a texture. Note that this function
2804 * doesn't do any kind of flipping. Using this on an onscreen surface will
2805 * result in a flipped D3D texture.
2807 * Context activation is done by the caller. This function may temporarily
2808 * switch to a different context and restore the original one before return. */
2809 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
2811 struct wined3d_device *device = surface->resource.device;
2812 const struct wined3d_gl_info *gl_info;
2813 struct wined3d_context *context = old_ctx;
2814 struct wined3d_surface *restore_rt = NULL;
2816 if (old_ctx->current_rt != surface)
2818 restore_rt = old_ctx->current_rt;
2819 context = context_acquire(device, surface);
2822 gl_info = context->gl_info;
2823 device_invalidate_state(device, STATE_FRAMEBUFFER);
2825 wined3d_texture_prepare_texture(surface->container, context, srgb);
2826 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
2828 TRACE("Reading back offscreen render target %p.\n", surface);
2830 if (wined3d_resource_is_offscreen(&surface->container->resource))
2831 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2832 else
2833 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
2834 checkGLcall("glReadBuffer");
2836 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
2837 0, 0, 0, 0, surface->resource.width, surface->resource.height);
2838 checkGLcall("glCopyTexSubImage2D");
2840 if (restore_rt)
2841 context_restore(context, restore_rt);
2844 static void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2846 if (multisample)
2848 DWORD samples;
2850 if (surface->rb_multisample)
2851 return;
2853 /* TODO: Nvidia exposes their Coverage Sample Anti-Aliasing (CSAA) feature
2854 * through type == MULTISAMPLE_XX and quality != 0. This could be mapped
2855 * to GL_NV_framebuffer_multisample_coverage.
2857 * AMD has a similar feature called Enhanced Quality Anti-Aliasing (EQAA),
2858 * but it does not have an equivalent OpenGL extension. */
2859 if (surface->resource.multisample_type == WINED3D_MULTISAMPLE_NON_MASKABLE)
2860 samples = surface->resource.multisample_quality;
2861 else
2862 samples = surface->resource.multisample_type;
2864 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2865 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2866 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
2867 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
2868 checkGLcall("glRenderbufferStorageMultisample()");
2869 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2871 else
2873 if (surface->rb_resolved)
2874 return;
2876 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2877 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2878 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
2879 surface->pow2Width, surface->pow2Height);
2880 checkGLcall("glRenderbufferStorage()");
2881 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2885 /* Does a direct frame buffer -> texture copy. Stretching is done with single
2886 * pixel copy calls. */
2887 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2888 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2890 struct wined3d_device *device = dst_surface->resource.device;
2891 const struct wined3d_gl_info *gl_info;
2892 float xrel, yrel;
2893 struct wined3d_context *context;
2894 BOOL upsidedown = FALSE;
2895 RECT dst_rect = *dst_rect_in;
2897 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2898 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2900 if(dst_rect.top > dst_rect.bottom) {
2901 UINT tmp = dst_rect.bottom;
2902 dst_rect.bottom = dst_rect.top;
2903 dst_rect.top = tmp;
2904 upsidedown = TRUE;
2907 context = context_acquire(device, src_surface);
2908 gl_info = context->gl_info;
2909 context_apply_blit_state(context, device);
2910 wined3d_texture_load(dst_surface->container, context, FALSE);
2912 /* Bind the target texture */
2913 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
2914 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
2916 TRACE("Reading from an offscreen target\n");
2917 upsidedown = !upsidedown;
2918 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2920 else
2922 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
2924 checkGLcall("glReadBuffer");
2926 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
2927 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
2929 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2931 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2933 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2934 ERR("Texture filtering not supported in direct blit.\n");
2936 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2937 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2939 ERR("Texture filtering not supported in direct blit\n");
2942 if (upsidedown
2943 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2944 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2946 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
2947 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2948 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
2949 src_rect->left, src_surface->resource.height - src_rect->bottom,
2950 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2952 else
2954 LONG row;
2955 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
2956 /* I have to process this row by row to swap the image,
2957 * otherwise it would be upside down, so stretching in y direction
2958 * doesn't cost extra time
2960 * However, stretching in x direction can be avoided if not necessary
2962 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
2963 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2965 /* Well, that stuff works, but it's very slow.
2966 * find a better way instead
2968 LONG col;
2970 for (col = dst_rect.left; col < dst_rect.right; ++col)
2972 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2973 dst_rect.left + col /* x offset */, row /* y offset */,
2974 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
2977 else
2979 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2980 dst_rect.left /* x offset */, row /* y offset */,
2981 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
2985 checkGLcall("glCopyTexSubImage2D");
2987 context_release(context);
2989 /* The texture is now most up to date - If the surface is a render target
2990 * and has a drawable, this path is never entered. */
2991 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
2992 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
2995 /* Uses the hardware to stretch and flip the image */
2996 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2997 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2999 struct wined3d_device *device = dst_surface->resource.device;
3000 GLuint src, backup = 0;
3001 float left, right, top, bottom; /* Texture coordinates */
3002 UINT fbwidth = src_surface->resource.width;
3003 UINT fbheight = src_surface->resource.height;
3004 const struct wined3d_gl_info *gl_info;
3005 struct wined3d_context *context;
3006 GLenum drawBuffer = GL_BACK;
3007 GLenum texture_target;
3008 BOOL noBackBufferBackup;
3009 BOOL src_offscreen;
3010 BOOL upsidedown = FALSE;
3011 RECT dst_rect = *dst_rect_in;
3013 TRACE("Using hwstretch blit\n");
3014 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3015 context = context_acquire(device, src_surface);
3016 gl_info = context->gl_info;
3017 context_apply_blit_state(context, device);
3018 wined3d_texture_load(dst_surface->container, context, FALSE);
3020 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
3021 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3022 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3024 /* Get it a description */
3025 wined3d_texture_load(src_surface->container, context, FALSE);
3028 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3029 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3031 if (context->aux_buffers >= 2)
3033 /* Got more than one aux buffer? Use the 2nd aux buffer */
3034 drawBuffer = GL_AUX1;
3036 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3038 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3039 drawBuffer = GL_AUX0;
3042 if (noBackBufferBackup)
3044 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3045 checkGLcall("glGenTextures");
3046 context_bind_texture(context, GL_TEXTURE_2D, backup);
3047 texture_target = GL_TEXTURE_2D;
3049 else
3051 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3052 * we are reading from the back buffer, the backup can be used as source texture
3054 texture_target = src_surface->texture_target;
3055 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3056 gl_info->gl_ops.gl.p_glEnable(texture_target);
3057 checkGLcall("glEnable(texture_target)");
3059 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3060 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3063 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3064 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3066 if(dst_rect.top > dst_rect.bottom) {
3067 UINT tmp = dst_rect.bottom;
3068 dst_rect.bottom = dst_rect.top;
3069 dst_rect.top = tmp;
3070 upsidedown = TRUE;
3073 if (src_offscreen)
3075 TRACE("Reading from an offscreen target\n");
3076 upsidedown = !upsidedown;
3077 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3079 else
3081 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3084 /* TODO: Only back up the part that will be overwritten */
3085 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3087 checkGLcall("glCopyTexSubImage2D");
3089 /* No issue with overriding these - the sampler is dirty due to blit usage */
3090 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
3091 checkGLcall("glTexParameteri");
3092 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3093 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
3094 checkGLcall("glTexParameteri");
3096 if (!src_surface->container->swapchain
3097 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
3099 src = backup ? backup : src_surface->container->texture_rgb.name;
3101 else
3103 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3104 checkGLcall("glReadBuffer(GL_FRONT)");
3106 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3107 checkGLcall("glGenTextures(1, &src)");
3108 context_bind_texture(context, GL_TEXTURE_2D, src);
3110 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3111 * out for power of 2 sizes
3113 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3114 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3115 checkGLcall("glTexImage2D");
3116 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3118 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3119 checkGLcall("glTexParameteri");
3120 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3121 checkGLcall("glTexParameteri");
3123 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3124 checkGLcall("glReadBuffer(GL_BACK)");
3126 if (texture_target != GL_TEXTURE_2D)
3128 gl_info->gl_ops.gl.p_glDisable(texture_target);
3129 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3130 texture_target = GL_TEXTURE_2D;
3133 checkGLcall("glEnd and previous");
3135 left = src_rect->left;
3136 right = src_rect->right;
3138 if (!upsidedown)
3140 top = src_surface->resource.height - src_rect->top;
3141 bottom = src_surface->resource.height - src_rect->bottom;
3143 else
3145 top = src_surface->resource.height - src_rect->bottom;
3146 bottom = src_surface->resource.height - src_rect->top;
3149 if (src_surface->container->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
3151 left /= src_surface->pow2Width;
3152 right /= src_surface->pow2Width;
3153 top /= src_surface->pow2Height;
3154 bottom /= src_surface->pow2Height;
3157 /* draw the source texture stretched and upside down. The correct surface is bound already */
3158 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3159 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3161 context_set_draw_buffer(context, drawBuffer);
3162 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3164 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3165 /* bottom left */
3166 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3167 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3169 /* top left */
3170 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3171 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3173 /* top right */
3174 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3175 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3177 /* bottom right */
3178 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3179 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3180 gl_info->gl_ops.gl.p_glEnd();
3181 checkGLcall("glEnd and previous");
3183 if (texture_target != dst_surface->texture_target)
3185 gl_info->gl_ops.gl.p_glDisable(texture_target);
3186 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3187 texture_target = dst_surface->texture_target;
3190 /* Now read the stretched and upside down image into the destination texture */
3191 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
3192 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
3194 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3195 0, 0, /* We blitted the image to the origin */
3196 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3197 checkGLcall("glCopyTexSubImage2D");
3199 if (drawBuffer == GL_BACK)
3201 /* Write the back buffer backup back. */
3202 if (backup)
3204 if (texture_target != GL_TEXTURE_2D)
3206 gl_info->gl_ops.gl.p_glDisable(texture_target);
3207 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3208 texture_target = GL_TEXTURE_2D;
3210 context_bind_texture(context, GL_TEXTURE_2D, backup);
3212 else
3214 if (texture_target != src_surface->texture_target)
3216 gl_info->gl_ops.gl.p_glDisable(texture_target);
3217 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3218 texture_target = src_surface->texture_target;
3220 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3223 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3224 /* top left */
3225 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3226 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3228 /* bottom left */
3229 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3230 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3232 /* bottom right */
3233 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3234 (float)fbheight / (float)src_surface->pow2Height);
3235 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3237 /* top right */
3238 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3239 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3240 gl_info->gl_ops.gl.p_glEnd();
3242 gl_info->gl_ops.gl.p_glDisable(texture_target);
3243 checkGLcall("glDisable(texture_target)");
3245 /* Cleanup */
3246 if (src != src_surface->container->texture_rgb.name && src != backup)
3248 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3249 checkGLcall("glDeleteTextures(1, &src)");
3251 if (backup)
3253 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3254 checkGLcall("glDeleteTextures(1, &backup)");
3257 if (wined3d_settings.strict_draw_ordering)
3258 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3260 context_release(context);
3262 /* The texture is now most up to date - If the surface is a render target
3263 * and has a drawable, this path is never entered. */
3264 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3265 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3268 /* Front buffer coordinates are always full screen coordinates, but our GL
3269 * drawable is limited to the window's client area. The sysmem and texture
3270 * copies do have the full screen size. Note that GL has a bottom-left
3271 * origin, while D3D has a top-left origin. */
3272 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3274 UINT drawable_height;
3276 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3278 POINT offset = {0, 0};
3279 RECT windowsize;
3281 ScreenToClient(window, &offset);
3282 OffsetRect(rect, offset.x, offset.y);
3284 GetClientRect(window, &windowsize);
3285 drawable_height = windowsize.bottom - windowsize.top;
3287 else
3289 drawable_height = surface->resource.height;
3292 rect->top = drawable_height - rect->top;
3293 rect->bottom = drawable_height - rect->bottom;
3296 /* Context activation is done by the caller. */
3297 static void surface_blt_to_drawable(const struct wined3d_device *device,
3298 struct wined3d_context *old_ctx,
3299 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3300 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3301 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3303 const struct wined3d_gl_info *gl_info;
3304 struct wined3d_context *context = old_ctx;
3305 struct wined3d_surface *restore_rt = NULL;
3306 RECT src_rect, dst_rect;
3308 src_rect = *src_rect_in;
3309 dst_rect = *dst_rect_in;
3312 if (old_ctx->current_rt != dst_surface)
3314 restore_rt = old_ctx->current_rt;
3315 context = context_acquire(device, dst_surface);
3318 gl_info = context->gl_info;
3320 /* Make sure the surface is up-to-date. This should probably use
3321 * surface_load_location() and worry about the destination surface too,
3322 * unless we're overwriting it completely. */
3323 wined3d_texture_load(src_surface->container, context, FALSE);
3325 /* Activate the destination context, set it up for blitting */
3326 context_apply_blit_state(context, device);
3328 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3329 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3331 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
3333 if (alpha_test)
3335 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3336 checkGLcall("glEnable(GL_ALPHA_TEST)");
3338 /* For P8 surfaces, the alpha component contains the palette index.
3339 * Which means that the colorkey is one of the palette entries. In
3340 * other cases pixels that should be masked away have alpha set to 0. */
3341 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3342 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3343 (float)src_surface->container->async.src_blt_color_key.color_space_low_value / 255.0f);
3344 else
3345 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3346 checkGLcall("glAlphaFunc");
3348 else
3350 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3351 checkGLcall("glDisable(GL_ALPHA_TEST)");
3354 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3356 if (alpha_test)
3358 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3359 checkGLcall("glDisable(GL_ALPHA_TEST)");
3362 /* Leave the opengl state valid for blitting */
3363 device->blitter->unset_shader(context->gl_info);
3365 if (wined3d_settings.strict_draw_ordering
3366 || (dst_surface->container->swapchain
3367 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3368 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3370 if (restore_rt)
3371 context_restore(context, restore_rt);
3374 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3376 struct wined3d_device *device = s->resource.device;
3377 const struct blit_shader *blitter;
3379 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, WINED3D_BLIT_OP_COLOR_FILL,
3380 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
3381 if (!blitter)
3383 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3384 return WINED3DERR_INVALIDCALL;
3387 return blitter->color_fill(device, s, rect, color);
3390 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3391 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3392 enum wined3d_texture_filter_type filter)
3394 struct wined3d_device *device = dst_surface->resource.device;
3395 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3396 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3398 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3399 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3400 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3402 /* Get the swapchain. One of the surfaces has to be a primary surface */
3403 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3405 WARN("Destination is in sysmem, rejecting gl blt\n");
3406 return WINED3DERR_INVALIDCALL;
3409 dst_swapchain = dst_surface->container->swapchain;
3411 if (src_surface)
3413 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3415 WARN("Src is in sysmem, rejecting gl blt\n");
3416 return WINED3DERR_INVALIDCALL;
3419 src_swapchain = src_surface->container->swapchain;
3421 else
3423 src_swapchain = NULL;
3426 /* Early sort out of cases where no render target is used */
3427 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
3429 TRACE("No surface is render target, not using hardware blit.\n");
3430 return WINED3DERR_INVALIDCALL;
3433 /* No destination color keying supported */
3434 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
3436 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3437 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3438 return WINED3DERR_INVALIDCALL;
3441 if (dst_swapchain && dst_swapchain == src_swapchain)
3443 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3444 return WINED3DERR_INVALIDCALL;
3447 if (dst_swapchain && src_swapchain)
3449 FIXME("Implement hardware blit between two different swapchains\n");
3450 return WINED3DERR_INVALIDCALL;
3453 if (dst_swapchain)
3455 /* Handled with regular texture -> swapchain blit */
3456 if (src_surface == rt)
3457 TRACE("Blit from active render target to a swapchain\n");
3459 else if (src_swapchain && dst_surface == rt)
3461 FIXME("Implement blit from a swapchain to the active render target\n");
3462 return WINED3DERR_INVALIDCALL;
3465 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
3467 /* Blit from render target to texture */
3468 BOOL stretchx;
3470 /* P8 read back is not implemented */
3471 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
3472 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
3474 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3475 return WINED3DERR_INVALIDCALL;
3478 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3480 TRACE("Color keying not supported by frame buffer to texture blit\n");
3481 return WINED3DERR_INVALIDCALL;
3482 /* Destination color key is checked above */
3485 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
3486 stretchx = TRUE;
3487 else
3488 stretchx = FALSE;
3490 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3491 * flip the image nor scale it.
3493 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3494 * -> If the app wants an image width an unscaled width, copy it line per line
3495 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
3496 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3497 * back buffer. This is slower than reading line per line, thus not used for flipping
3498 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3499 * pixel by pixel. */
3500 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
3501 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
3503 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
3504 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
3506 else
3508 TRACE("Using hardware stretching to flip / stretch the texture.\n");
3509 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
3512 surface_evict_sysmem(dst_surface);
3514 return WINED3D_OK;
3517 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3518 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3519 return WINED3DERR_INVALIDCALL;
3522 /* Context activation is done by the caller. */
3523 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
3524 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
3526 struct wined3d_device *device = surface->resource.device;
3527 const struct wined3d_gl_info *gl_info = context->gl_info;
3528 GLint compare_mode = GL_NONE;
3529 struct blt_info info;
3530 GLint old_binding = 0;
3531 RECT rect;
3533 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
3535 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
3536 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
3537 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3538 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
3539 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
3540 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
3541 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
3542 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
3543 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3544 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
3545 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
3547 SetRect(&rect, 0, h, w, 0);
3548 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
3549 context_active_texture(context, context->gl_info, 0);
3550 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
3551 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
3552 if (gl_info->supported[ARB_SHADOW])
3554 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
3555 if (compare_mode != GL_NONE)
3556 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
3559 device->shader_backend->shader_select_depth_blt(device->shader_priv,
3560 gl_info, info.tex_type, &surface->ds_current_size);
3562 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
3563 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
3564 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
3565 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
3566 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
3567 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
3568 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
3569 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
3570 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
3571 gl_info->gl_ops.gl.p_glEnd();
3573 if (compare_mode != GL_NONE)
3574 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
3575 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
3577 gl_info->gl_ops.gl.p_glPopAttrib();
3579 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
3582 void surface_modify_ds_location(struct wined3d_surface *surface,
3583 DWORD location, UINT w, UINT h)
3585 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
3587 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
3588 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
3589 wined3d_texture_set_dirty(surface->container);
3591 surface->ds_current_size.cx = w;
3592 surface->ds_current_size.cy = h;
3593 surface->locations = location;
3596 /* Context activation is done by the caller. */
3597 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3599 const struct wined3d_gl_info *gl_info = context->gl_info;
3600 struct wined3d_device *device = surface->resource.device;
3601 GLsizei w, h;
3603 TRACE("surface %p, context %p, new location %#x.\n", surface, context, location);
3605 /* TODO: Make this work for modes other than FBO */
3606 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3608 if (!(surface->locations & location))
3610 w = surface->ds_current_size.cx;
3611 h = surface->ds_current_size.cy;
3612 surface->ds_current_size.cx = 0;
3613 surface->ds_current_size.cy = 0;
3615 else
3617 w = surface->resource.width;
3618 h = surface->resource.height;
3621 if (surface->ds_current_size.cx == surface->resource.width
3622 && surface->ds_current_size.cy == surface->resource.height)
3624 TRACE("Location (%#x) is already up to date.\n", location);
3625 return;
3628 if (surface->current_renderbuffer)
3630 FIXME("Not supported with fixed up depth stencil.\n");
3631 return;
3634 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3636 TRACE("Surface was discarded, no need copy data.\n");
3637 wined3d_surface_prepare(surface, context, location);
3638 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
3639 surface->locations |= location;
3640 surface->ds_current_size.cx = surface->resource.width;
3641 surface->ds_current_size.cy = surface->resource.height;
3642 return;
3645 if (!surface->locations)
3647 FIXME("No up to date depth stencil location.\n");
3648 surface->locations |= location;
3649 surface->ds_current_size.cx = surface->resource.width;
3650 surface->ds_current_size.cy = surface->resource.height;
3651 return;
3654 if (location == WINED3D_LOCATION_TEXTURE_RGB)
3656 GLint old_binding = 0;
3657 GLenum bind_target;
3659 /* The render target is allowed to be smaller than the depth/stencil
3660 * buffer, so the onscreen depth/stencil buffer is potentially smaller
3661 * than the offscreen surface. Don't overwrite the offscreen surface
3662 * with undefined data. */
3663 w = min(w, context->swapchain->desc.backbuffer_width);
3664 h = min(h, context->swapchain->desc.backbuffer_height);
3666 TRACE("Copying onscreen depth buffer to depth texture.\n");
3668 if (!device->depth_blt_texture)
3669 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
3671 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3672 * directly on the FBO texture. That's because we need to flip. */
3673 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3674 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3675 NULL, WINED3D_LOCATION_DRAWABLE);
3676 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
3678 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
3679 bind_target = GL_TEXTURE_RECTANGLE_ARB;
3681 else
3683 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3684 bind_target = GL_TEXTURE_2D;
3686 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
3687 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
3688 * internal format, because the internal format might include stencil
3689 * data. In principle we should copy stencil data as well, but unless
3690 * the driver supports stencil export it's hard to do, and doesn't
3691 * seem to be needed in practice. If the hardware doesn't support
3692 * writing stencil data, the glCopyTexImage2D() call might trigger
3693 * software fallbacks. */
3694 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
3695 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3696 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3697 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3698 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3699 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
3700 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
3702 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3703 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
3704 context_set_draw_buffer(context, GL_NONE);
3706 /* Do the actual blit */
3707 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
3708 checkGLcall("depth_blt");
3710 context_invalidate_state(context, STATE_FRAMEBUFFER);
3712 if (wined3d_settings.strict_draw_ordering)
3713 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3715 else if (location == WINED3D_LOCATION_DRAWABLE)
3717 TRACE("Copying depth texture to onscreen depth buffer.\n");
3719 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3720 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3721 NULL, WINED3D_LOCATION_DRAWABLE);
3722 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
3723 0, surface->pow2Height - h, w, h, surface->texture_target);
3724 checkGLcall("depth_blt");
3726 context_invalidate_state(context, STATE_FRAMEBUFFER);
3728 if (wined3d_settings.strict_draw_ordering)
3729 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3731 else
3733 ERR("Invalid location (%#x) specified.\n", location);
3736 surface->locations |= location;
3737 surface->ds_current_size.cx = surface->resource.width;
3738 surface->ds_current_size.cy = surface->resource.height;
3741 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
3743 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3745 surface->locations |= location;
3748 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
3750 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3752 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3753 wined3d_texture_set_dirty(surface->container);
3754 surface->locations &= ~location;
3756 if (!surface->locations)
3757 ERR("Surface %p does not have any up to date location.\n", surface);
3760 static DWORD resource_access_from_location(DWORD location)
3762 switch (location)
3764 case WINED3D_LOCATION_SYSMEM:
3765 case WINED3D_LOCATION_USER_MEMORY:
3766 case WINED3D_LOCATION_DIB:
3767 case WINED3D_LOCATION_BUFFER:
3768 return WINED3D_RESOURCE_ACCESS_CPU;
3770 case WINED3D_LOCATION_DRAWABLE:
3771 case WINED3D_LOCATION_TEXTURE_SRGB:
3772 case WINED3D_LOCATION_TEXTURE_RGB:
3773 case WINED3D_LOCATION_RB_MULTISAMPLE:
3774 case WINED3D_LOCATION_RB_RESOLVED:
3775 return WINED3D_RESOURCE_ACCESS_GPU;
3777 default:
3778 FIXME("Unhandled location %#x.\n", location);
3779 return 0;
3783 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
3785 struct wined3d_device *device = surface->resource.device;
3786 struct wined3d_context *context;
3787 const struct wined3d_gl_info *gl_info;
3788 struct wined3d_bo_address dst, src;
3789 UINT size = surface->resource.size;
3791 surface_get_memory(surface, &dst, location);
3792 surface_get_memory(surface, &src, surface->locations);
3794 if (dst.buffer_object)
3796 context = context_acquire(device, NULL);
3797 gl_info = context->gl_info;
3798 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
3799 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr));
3800 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
3801 checkGLcall("Upload PBO");
3802 context_release(context);
3803 return;
3805 if (src.buffer_object)
3807 context = context_acquire(device, NULL);
3808 gl_info = context->gl_info;
3809 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
3810 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr));
3811 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
3812 checkGLcall("Download PBO");
3813 context_release(context);
3814 return;
3816 memcpy(dst.addr, src.addr, size);
3819 /* Context activation is done by the caller. */
3820 static void surface_load_sysmem(struct wined3d_surface *surface,
3821 struct wined3d_context *context, DWORD dst_location)
3823 const struct wined3d_gl_info *gl_info = context->gl_info;
3825 if (surface->locations & surface_simple_locations)
3827 surface_copy_simple_location(surface, dst_location);
3828 return;
3831 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
3832 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3834 /* Download the surface to system memory. */
3835 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3837 wined3d_texture_bind_and_dirtify(surface->container, context,
3838 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
3839 surface_download_data(surface, gl_info, dst_location);
3841 return;
3844 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
3846 read_from_framebuffer(surface, context, dst_location);
3847 return;
3850 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
3851 surface, wined3d_debug_location(surface->locations));
3854 /* Context activation is done by the caller. */
3855 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
3856 struct wined3d_context *context)
3858 RECT r;
3860 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3861 && wined3d_resource_is_offscreen(&surface->container->resource))
3863 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
3864 return WINED3DERR_INVALIDCALL;
3867 surface_get_rect(surface, NULL, &r);
3868 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3869 surface_blt_to_drawable(surface->resource.device, context,
3870 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
3872 return WINED3D_OK;
3875 static HRESULT surface_load_texture(struct wined3d_surface *surface,
3876 struct wined3d_context *context, BOOL srgb)
3878 const struct wined3d_gl_info *gl_info = context->gl_info;
3879 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
3880 struct wined3d_device *device = surface->resource.device;
3881 const struct wined3d_color_key_conversion *conversion;
3882 struct wined3d_texture *texture = surface->container;
3883 UINT width, src_pitch, dst_pitch;
3884 struct wined3d_bo_address data;
3885 struct wined3d_format format;
3886 POINT dst_point = {0, 0};
3887 BYTE *mem = NULL;
3889 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
3890 && wined3d_resource_is_offscreen(&texture->resource)
3891 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
3893 surface_load_fb_texture(surface, srgb, context);
3895 return WINED3D_OK;
3898 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
3899 && (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
3900 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3901 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3902 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3904 if (srgb)
3905 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
3906 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
3907 else
3908 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
3909 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
3911 return WINED3D_OK;
3914 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
3915 && (!srgb || (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
3916 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3917 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3918 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3920 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
3921 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
3922 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
3923 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3925 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
3926 &rect, surface, dst_location, &rect);
3928 return WINED3D_OK;
3931 /* Upload from system memory */
3933 if (srgb)
3935 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
3936 == WINED3D_LOCATION_TEXTURE_RGB)
3938 /* Performance warning... */
3939 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
3940 surface_prepare_map_memory(surface);
3941 surface_load_location(surface, context, surface->resource.map_binding);
3944 else
3946 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
3947 == WINED3D_LOCATION_TEXTURE_SRGB)
3949 /* Performance warning... */
3950 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
3951 surface_prepare_map_memory(surface);
3952 surface_load_location(surface, context, surface->resource.map_binding);
3956 if (!(surface->locations & surface_simple_locations))
3958 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
3959 /* Lets hope we get it from somewhere... */
3960 surface_prepare_system_memory(surface);
3961 surface_load_location(surface, context, WINED3D_LOCATION_SYSMEM);
3964 wined3d_texture_prepare_texture(texture, context, srgb);
3965 wined3d_texture_bind_and_dirtify(texture, context, srgb);
3967 width = surface->resource.width;
3968 src_pitch = wined3d_surface_get_pitch(surface);
3970 format = *texture->resource.format;
3971 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
3972 format = *wined3d_get_format(gl_info, conversion->dst_format);
3974 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
3975 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
3976 * getting called. */
3977 if ((format.convert || conversion) && surface->pbo)
3979 TRACE("Removing the pbo attached to surface %p.\n", surface);
3981 if (surface->flags & SFLAG_DIBSECTION)
3982 surface->resource.map_binding = WINED3D_LOCATION_DIB;
3983 else
3984 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
3986 surface_prepare_map_memory(surface);
3987 surface_load_location(surface, context, surface->resource.map_binding);
3988 surface_remove_pbo(surface, gl_info);
3991 surface_get_memory(surface, &data, surface->locations);
3992 if (format.convert)
3994 /* This code is entered for texture formats which need a fixup. */
3995 UINT height = surface->resource.height;
3997 format.byte_count = format.conv_byte_count;
3998 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4000 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4002 ERR("Out of memory (%u).\n", dst_pitch * height);
4003 context_release(context);
4004 return E_OUTOFMEMORY;
4006 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4007 dst_pitch, dst_pitch * height, width, height, 1);
4008 src_pitch = dst_pitch;
4009 data.addr = mem;
4011 else if (conversion)
4013 /* This code is only entered for color keying fixups */
4014 struct wined3d_palette *palette = NULL;
4015 UINT height = surface->resource.height;
4017 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4018 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4020 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4022 ERR("Out of memory (%u).\n", dst_pitch * height);
4023 context_release(context);
4024 return E_OUTOFMEMORY;
4026 if (texture->swapchain && texture->swapchain->palette)
4027 palette = texture->swapchain->palette;
4028 conversion->convert(data.addr, src_pitch, mem, dst_pitch,
4029 width, height, palette, &texture->async.gl_color_key);
4030 src_pitch = dst_pitch;
4031 data.addr = mem;
4034 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
4035 src_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
4037 HeapFree(GetProcessHeap(), 0, mem);
4039 return WINED3D_OK;
4042 /* Context activation is done by the caller. */
4043 static void surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
4044 DWORD dst_location)
4046 const RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4047 DWORD src_location;
4049 if (surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE)
4050 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
4051 else if (surface->locations & WINED3D_LOCATION_RB_RESOLVED)
4052 src_location = WINED3D_LOCATION_RB_RESOLVED;
4053 else if (surface->locations & WINED3D_LOCATION_TEXTURE_SRGB)
4054 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
4055 else /* surface_blt_fbo will load the source location if necessary. */
4056 src_location = WINED3D_LOCATION_TEXTURE_RGB;
4058 surface_blt_fbo(surface->resource.device, context, WINED3D_TEXF_POINT,
4059 surface, src_location, &rect, surface, dst_location, &rect);
4062 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
4063 HRESULT surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4065 HRESULT hr;
4067 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4069 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4071 if (location == WINED3D_LOCATION_TEXTURE_RGB
4072 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4074 surface_load_ds_location(surface, context, location);
4075 return WINED3D_OK;
4077 else if (location & surface->locations
4078 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
4080 /* Already up to date, nothing to do. */
4081 return WINED3D_OK;
4083 else
4085 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4086 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4087 return WINED3DERR_INVALIDCALL;
4091 if (surface->locations & location)
4093 TRACE("Location already up to date.\n");
4094 return WINED3D_OK;
4097 if (WARN_ON(d3d_surface))
4099 DWORD required_access = resource_access_from_location(location);
4100 if ((surface->resource.access_flags & required_access) != required_access)
4101 WARN("Operation requires %#x access, but surface only has %#x.\n",
4102 required_access, surface->resource.access_flags);
4105 if (!surface->locations)
4107 ERR("Surface %p does not have any up to date location.\n", surface);
4108 return WINED3DERR_INVALIDCALL;
4111 switch (location)
4113 case WINED3D_LOCATION_DIB:
4114 case WINED3D_LOCATION_USER_MEMORY:
4115 case WINED3D_LOCATION_SYSMEM:
4116 case WINED3D_LOCATION_BUFFER:
4117 surface_load_sysmem(surface, context, location);
4118 break;
4120 case WINED3D_LOCATION_DRAWABLE:
4121 if (FAILED(hr = surface_load_drawable(surface, context)))
4122 return hr;
4123 break;
4125 case WINED3D_LOCATION_RB_RESOLVED:
4126 case WINED3D_LOCATION_RB_MULTISAMPLE:
4127 surface_load_renderbuffer(surface, context, location);
4128 break;
4130 case WINED3D_LOCATION_TEXTURE_RGB:
4131 case WINED3D_LOCATION_TEXTURE_SRGB:
4132 if (FAILED(hr = surface_load_texture(surface, context,
4133 location == WINED3D_LOCATION_TEXTURE_SRGB)))
4134 return hr;
4135 break;
4137 default:
4138 ERR("Don't know how to handle location %#x.\n", location);
4139 break;
4142 surface_validate_location(surface, location);
4144 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
4145 surface_evict_sysmem(surface);
4147 return WINED3D_OK;
4150 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
4151 /* Context activation is done by the caller. */
4152 static void ffp_blit_free(struct wined3d_device *device) { }
4154 /* Context activation is done by the caller. */
4155 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4156 const struct wined3d_color_key *color_key)
4158 const struct wined3d_gl_info *gl_info = context->gl_info;
4160 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
4161 checkGLcall("glEnable(target)");
4163 return WINED3D_OK;
4166 /* Context activation is done by the caller. */
4167 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
4169 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
4170 checkGLcall("glDisable(GL_TEXTURE_2D)");
4171 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4173 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4174 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4176 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4178 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
4179 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4183 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
4184 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4185 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4186 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4188 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4190 TRACE("Source or destination is in system memory.\n");
4191 return FALSE;
4194 switch (blit_op)
4196 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
4197 if (d3d_info->shader_color_key)
4199 TRACE("Color keying requires converted textures.\n");
4200 return FALSE;
4202 case WINED3D_BLIT_OP_COLOR_BLIT:
4203 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
4204 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4206 TRACE("Checking support for fixup:\n");
4207 dump_color_fixup_desc(src_format->color_fixup);
4210 /* We only support identity conversions. */
4211 if (!is_identity_fixup(src_format->color_fixup)
4212 || !is_identity_fixup(dst_format->color_fixup))
4214 TRACE("Fixups are not supported.\n");
4215 return FALSE;
4218 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4220 TRACE("Can only blit to render targets.\n");
4221 return FALSE;
4223 return TRUE;
4225 case WINED3D_BLIT_OP_COLOR_FILL:
4226 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4228 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
4229 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4230 return FALSE;
4232 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4234 TRACE("Color fill not supported\n");
4235 return FALSE;
4238 /* FIXME: We should reject color fills on formats with fixups,
4239 * but this would break P8 color fills for example. */
4241 return TRUE;
4243 case WINED3D_BLIT_OP_DEPTH_FILL:
4244 return TRUE;
4246 default:
4247 TRACE("Unsupported blit_op=%d\n", blit_op);
4248 return FALSE;
4252 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4253 const RECT *dst_rect, const struct wined3d_color *color)
4255 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4256 struct wined3d_rendertarget_view *view;
4257 struct wined3d_fb_state fb = {&view, NULL};
4258 HRESULT hr;
4260 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4261 NULL, &wined3d_null_parent_ops, &view)))
4263 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4264 return hr;
4267 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4268 wined3d_rendertarget_view_decref(view);
4270 return WINED3D_OK;
4273 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4274 const RECT *dst_rect, float depth)
4276 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4277 struct wined3d_fb_state fb = {NULL, NULL};
4278 HRESULT hr;
4280 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4281 NULL, &wined3d_null_parent_ops, &fb.depth_stencil)))
4283 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4284 return hr;
4287 device_clear_render_targets(device, 0, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4288 wined3d_rendertarget_view_decref(fb.depth_stencil);
4290 return WINED3D_OK;
4293 static void ffp_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
4294 struct wined3d_surface *src_surface, const RECT *src_rect,
4295 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4296 const struct wined3d_color_key *color_key)
4298 struct wined3d_context *context;
4300 /* Blit from offscreen surface to render target */
4301 struct wined3d_color_key old_blt_key = src_surface->container->async.src_blt_color_key;
4302 DWORD old_color_key_flags = src_surface->container->async.color_key_flags;
4304 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4306 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT, color_key);
4308 context = context_acquire(device, dst_surface);
4310 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
4311 glEnable(GL_ALPHA_TEST);
4313 surface_blt_to_drawable(device, context, filter,
4314 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
4316 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
4317 glDisable(GL_ALPHA_TEST);
4319 context_release(context);
4321 /* Restore the color key parameters */
4322 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT,
4323 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
4325 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
4326 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
4329 const struct blit_shader ffp_blit = {
4330 ffp_blit_alloc,
4331 ffp_blit_free,
4332 ffp_blit_set,
4333 ffp_blit_unset,
4334 ffp_blit_supported,
4335 ffp_blit_color_fill,
4336 ffp_blit_depth_fill,
4337 ffp_blit_blit_surface,
4340 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4342 return WINED3D_OK;
4345 /* Context activation is done by the caller. */
4346 static void cpu_blit_free(struct wined3d_device *device)
4350 /* Context activation is done by the caller. */
4351 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4352 const struct wined3d_color_key *color_key)
4354 return WINED3D_OK;
4357 /* Context activation is done by the caller. */
4358 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4362 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
4363 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4364 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4365 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4367 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4369 return TRUE;
4372 return FALSE;
4375 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4376 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4377 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4379 UINT row_block_count;
4380 const BYTE *src_row;
4381 BYTE *dst_row;
4382 UINT x, y;
4384 src_row = src_data;
4385 dst_row = dst_data;
4387 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4389 if (!flags)
4391 for (y = 0; y < update_h; y += format->block_height)
4393 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4394 src_row += src_pitch;
4395 dst_row += dst_pitch;
4398 return WINED3D_OK;
4401 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4403 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4405 switch (format->id)
4407 case WINED3DFMT_DXT1:
4408 for (y = 0; y < update_h; y += format->block_height)
4410 struct block
4412 WORD color[2];
4413 BYTE control_row[4];
4416 const struct block *s = (const struct block *)src_row;
4417 struct block *d = (struct block *)dst_row;
4419 for (x = 0; x < row_block_count; ++x)
4421 d[x].color[0] = s[x].color[0];
4422 d[x].color[1] = s[x].color[1];
4423 d[x].control_row[0] = s[x].control_row[3];
4424 d[x].control_row[1] = s[x].control_row[2];
4425 d[x].control_row[2] = s[x].control_row[1];
4426 d[x].control_row[3] = s[x].control_row[0];
4428 src_row -= src_pitch;
4429 dst_row += dst_pitch;
4431 return WINED3D_OK;
4433 case WINED3DFMT_DXT2:
4434 case WINED3DFMT_DXT3:
4435 for (y = 0; y < update_h; y += format->block_height)
4437 struct block
4439 WORD alpha_row[4];
4440 WORD color[2];
4441 BYTE control_row[4];
4444 const struct block *s = (const struct block *)src_row;
4445 struct block *d = (struct block *)dst_row;
4447 for (x = 0; x < row_block_count; ++x)
4449 d[x].alpha_row[0] = s[x].alpha_row[3];
4450 d[x].alpha_row[1] = s[x].alpha_row[2];
4451 d[x].alpha_row[2] = s[x].alpha_row[1];
4452 d[x].alpha_row[3] = s[x].alpha_row[0];
4453 d[x].color[0] = s[x].color[0];
4454 d[x].color[1] = s[x].color[1];
4455 d[x].control_row[0] = s[x].control_row[3];
4456 d[x].control_row[1] = s[x].control_row[2];
4457 d[x].control_row[2] = s[x].control_row[1];
4458 d[x].control_row[3] = s[x].control_row[0];
4460 src_row -= src_pitch;
4461 dst_row += dst_pitch;
4463 return WINED3D_OK;
4465 default:
4466 FIXME("Compressed flip not implemented for format %s.\n",
4467 debug_d3dformat(format->id));
4468 return E_NOTIMPL;
4472 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
4473 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
4475 return E_NOTIMPL;
4478 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4479 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4480 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4482 const struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
4483 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
4484 const struct wined3d_format *src_format, *dst_format;
4485 unsigned int src_fmt_flags, dst_fmt_flags;
4486 struct wined3d_texture *src_texture = NULL;
4487 struct wined3d_map_desc dst_map, src_map;
4488 const BYTE *sbase = NULL;
4489 HRESULT hr = WINED3D_OK;
4490 const BYTE *sbuf;
4491 BYTE *dbuf;
4492 int x, y;
4494 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4495 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4496 flags, fx, debug_d3dtexturefiltertype(filter));
4498 if (src_surface == dst_surface)
4500 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
4501 src_map = dst_map;
4502 src_format = dst_surface->resource.format;
4503 dst_format = src_format;
4504 dst_fmt_flags = dst_surface->container->resource.format_flags;
4505 src_fmt_flags = dst_fmt_flags;
4507 else
4509 dst_format = dst_surface->resource.format;
4510 dst_fmt_flags = dst_surface->container->resource.format_flags;
4511 if (src_surface)
4513 if (dst_surface->resource.format->id != src_surface->resource.format->id)
4515 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
4517 /* The conv function writes a FIXME */
4518 WARN("Cannot convert source surface format to dest format.\n");
4519 goto release;
4521 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
4523 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
4524 src_format = src_surface->resource.format;
4525 src_fmt_flags = src_surface->container->resource.format_flags;
4527 else
4529 src_format = dst_format;
4530 src_fmt_flags = dst_fmt_flags;
4533 wined3d_surface_map(dst_surface, &dst_map, &dst_box, 0);
4536 bpp = dst_surface->resource.format->byte_count;
4537 srcheight = src_rect->bottom - src_rect->top;
4538 srcwidth = src_rect->right - src_rect->left;
4539 dstheight = dst_rect->bottom - dst_rect->top;
4540 dstwidth = dst_rect->right - dst_rect->left;
4541 width = (dst_rect->right - dst_rect->left) * bpp;
4543 if (src_surface)
4544 sbase = (BYTE *)src_map.data
4545 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
4546 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
4547 if (src_surface != dst_surface)
4548 dbuf = dst_map.data;
4549 else
4550 dbuf = (BYTE *)dst_map.data
4551 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
4552 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
4554 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
4556 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
4558 if (src_surface == dst_surface)
4560 FIXME("Only plain blits supported on compressed surfaces.\n");
4561 hr = E_NOTIMPL;
4562 goto release;
4565 if (srcheight != dstheight || srcwidth != dstwidth)
4567 WARN("Stretching not supported on compressed surfaces.\n");
4568 hr = WINED3DERR_INVALIDCALL;
4569 goto release;
4572 if (!surface_check_block_align_rect(src_surface, src_rect))
4574 WARN("Source rectangle not block-aligned.\n");
4575 hr = WINED3DERR_INVALIDCALL;
4576 goto release;
4579 if (!surface_check_block_align_rect(dst_surface, dst_rect))
4581 WARN("Destination rectangle not block-aligned.\n");
4582 hr = WINED3DERR_INVALIDCALL;
4583 goto release;
4586 hr = surface_cpu_blt_compressed(sbase, dbuf,
4587 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
4588 src_format, flags, fx);
4589 goto release;
4592 /* First, all the 'source-less' blits */
4593 if (flags & WINEDDBLT_COLORFILL)
4595 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
4596 flags &= ~WINEDDBLT_COLORFILL;
4599 if (flags & WINEDDBLT_DEPTHFILL)
4601 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
4603 if (flags & WINEDDBLT_DDROPS)
4605 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
4607 /* Now the 'with source' blits. */
4608 if (src_surface)
4610 int sx, xinc, sy, yinc;
4612 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
4613 goto release;
4615 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
4616 && (srcwidth != dstwidth || srcheight != dstheight))
4618 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
4619 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
4622 xinc = (srcwidth << 16) / dstwidth;
4623 yinc = (srcheight << 16) / dstheight;
4625 if (!flags)
4627 /* No effects, we can cheat here. */
4628 if (dstwidth == srcwidth)
4630 if (dstheight == srcheight)
4632 /* No stretching in either direction. This needs to be as
4633 * fast as possible. */
4634 sbuf = sbase;
4636 /* Check for overlapping surfaces. */
4637 if (src_surface != dst_surface || dst_rect->top < src_rect->top
4638 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
4640 /* No overlap, or dst above src, so copy from top downwards. */
4641 for (y = 0; y < dstheight; ++y)
4643 memcpy(dbuf, sbuf, width);
4644 sbuf += src_map.row_pitch;
4645 dbuf += dst_map.row_pitch;
4648 else if (dst_rect->top > src_rect->top)
4650 /* Copy from bottom upwards. */
4651 sbuf += src_map.row_pitch * dstheight;
4652 dbuf += dst_map.row_pitch * dstheight;
4653 for (y = 0; y < dstheight; ++y)
4655 sbuf -= src_map.row_pitch;
4656 dbuf -= dst_map.row_pitch;
4657 memcpy(dbuf, sbuf, width);
4660 else
4662 /* Src and dst overlapping on the same line, use memmove. */
4663 for (y = 0; y < dstheight; ++y)
4665 memmove(dbuf, sbuf, width);
4666 sbuf += src_map.row_pitch;
4667 dbuf += dst_map.row_pitch;
4671 else
4673 /* Stretching in y direction only. */
4674 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4676 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4677 memcpy(dbuf, sbuf, width);
4678 dbuf += dst_map.row_pitch;
4682 else
4684 /* Stretching in X direction. */
4685 int last_sy = -1;
4686 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4688 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4690 if ((sy >> 16) == (last_sy >> 16))
4692 /* This source row is the same as last source row -
4693 * Copy the already stretched row. */
4694 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
4696 else
4698 #define STRETCH_ROW(type) \
4699 do { \
4700 const type *s = (const type *)sbuf; \
4701 type *d = (type *)dbuf; \
4702 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4703 d[x] = s[sx >> 16]; \
4704 } while(0)
4706 switch(bpp)
4708 case 1:
4709 STRETCH_ROW(BYTE);
4710 break;
4711 case 2:
4712 STRETCH_ROW(WORD);
4713 break;
4714 case 4:
4715 STRETCH_ROW(DWORD);
4716 break;
4717 case 3:
4719 const BYTE *s;
4720 BYTE *d = dbuf;
4721 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
4723 DWORD pixel;
4725 s = sbuf + 3 * (sx >> 16);
4726 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4727 d[0] = (pixel ) & 0xff;
4728 d[1] = (pixel >> 8) & 0xff;
4729 d[2] = (pixel >> 16) & 0xff;
4730 d += 3;
4732 break;
4734 default:
4735 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
4736 hr = WINED3DERR_NOTAVAILABLE;
4737 goto error;
4739 #undef STRETCH_ROW
4741 dbuf += dst_map.row_pitch;
4742 last_sy = sy;
4746 else
4748 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
4749 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
4750 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
4751 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
4753 /* The color keying flags are checked for correctness in ddraw */
4754 if (flags & WINEDDBLT_KEYSRC)
4756 keylow = src_surface->container->async.src_blt_color_key.color_space_low_value;
4757 keyhigh = src_surface->container->async.src_blt_color_key.color_space_high_value;
4759 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4761 keylow = fx->ddckSrcColorkey.color_space_low_value;
4762 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
4765 if (flags & WINEDDBLT_KEYDEST)
4767 /* Destination color keys are taken from the source surface! */
4768 destkeylow = src_surface->container->async.dst_blt_color_key.color_space_low_value;
4769 destkeyhigh = src_surface->container->async.dst_blt_color_key.color_space_high_value;
4771 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
4773 destkeylow = fx->ddckDestColorkey.color_space_low_value;
4774 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
4777 if (bpp == 1)
4779 keymask = 0xff;
4781 else
4783 DWORD masks[3];
4784 get_color_masks(src_format, masks);
4785 keymask = masks[0]
4786 | masks[1]
4787 | masks[2];
4789 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
4792 if (flags & WINEDDBLT_DDFX)
4794 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
4795 LONG tmpxy;
4796 dTopLeft = dbuf;
4797 dTopRight = dbuf + ((dstwidth - 1) * bpp);
4798 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
4799 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
4801 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
4803 /* I don't think we need to do anything about this flag */
4804 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
4806 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
4808 tmp = dTopRight;
4809 dTopRight = dTopLeft;
4810 dTopLeft = tmp;
4811 tmp = dBottomRight;
4812 dBottomRight = dBottomLeft;
4813 dBottomLeft = tmp;
4814 dstxinc = dstxinc * -1;
4816 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
4818 tmp = dTopLeft;
4819 dTopLeft = dBottomLeft;
4820 dBottomLeft = tmp;
4821 tmp = dTopRight;
4822 dTopRight = dBottomRight;
4823 dBottomRight = tmp;
4824 dstyinc = dstyinc * -1;
4826 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
4828 /* I don't think we need to do anything about this flag */
4829 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
4831 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
4833 tmp = dBottomRight;
4834 dBottomRight = dTopLeft;
4835 dTopLeft = tmp;
4836 tmp = dBottomLeft;
4837 dBottomLeft = dTopRight;
4838 dTopRight = tmp;
4839 dstxinc = dstxinc * -1;
4840 dstyinc = dstyinc * -1;
4842 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
4844 tmp = dTopLeft;
4845 dTopLeft = dBottomLeft;
4846 dBottomLeft = dBottomRight;
4847 dBottomRight = dTopRight;
4848 dTopRight = tmp;
4849 tmpxy = dstxinc;
4850 dstxinc = dstyinc;
4851 dstyinc = tmpxy;
4852 dstxinc = dstxinc * -1;
4854 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
4856 tmp = dTopLeft;
4857 dTopLeft = dTopRight;
4858 dTopRight = dBottomRight;
4859 dBottomRight = dBottomLeft;
4860 dBottomLeft = tmp;
4861 tmpxy = dstxinc;
4862 dstxinc = dstyinc;
4863 dstyinc = tmpxy;
4864 dstyinc = dstyinc * -1;
4866 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
4868 /* I don't think we need to do anything about this flag */
4869 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
4871 dbuf = dTopLeft;
4872 flags &= ~(WINEDDBLT_DDFX);
4875 #define COPY_COLORKEY_FX(type) \
4876 do { \
4877 const type *s; \
4878 type *d = (type *)dbuf, *dx, tmp; \
4879 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
4881 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
4882 dx = d; \
4883 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4885 tmp = s[sx >> 16]; \
4886 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
4887 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
4889 dx[0] = tmp; \
4891 dx = (type *)(((BYTE *)dx) + dstxinc); \
4893 d = (type *)(((BYTE *)d) + dstyinc); \
4895 } while(0)
4897 switch (bpp)
4899 case 1:
4900 COPY_COLORKEY_FX(BYTE);
4901 break;
4902 case 2:
4903 COPY_COLORKEY_FX(WORD);
4904 break;
4905 case 4:
4906 COPY_COLORKEY_FX(DWORD);
4907 break;
4908 case 3:
4910 const BYTE *s;
4911 BYTE *d = dbuf, *dx;
4912 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4914 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4915 dx = d;
4916 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
4918 DWORD pixel, dpixel = 0;
4919 s = sbuf + 3 * (sx>>16);
4920 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4921 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
4922 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
4923 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
4925 dx[0] = (pixel ) & 0xff;
4926 dx[1] = (pixel >> 8) & 0xff;
4927 dx[2] = (pixel >> 16) & 0xff;
4929 dx += dstxinc;
4931 d += dstyinc;
4933 break;
4935 default:
4936 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
4937 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
4938 hr = WINED3DERR_NOTAVAILABLE;
4939 goto error;
4940 #undef COPY_COLORKEY_FX
4945 error:
4946 if (flags && FIXME_ON(d3d_surface))
4948 FIXME("\tUnsupported flags: %#x.\n", flags);
4951 release:
4952 wined3d_surface_unmap(dst_surface);
4953 if (src_surface && src_surface != dst_surface)
4954 wined3d_surface_unmap(src_surface);
4955 /* Release the converted surface, if any. */
4956 if (src_texture)
4957 wined3d_texture_decref(src_texture);
4959 return hr;
4962 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4963 const RECT *dst_rect, const struct wined3d_color *color)
4965 static const RECT src_rect;
4966 WINEDDBLTFX BltFx;
4968 memset(&BltFx, 0, sizeof(BltFx));
4969 BltFx.dwSize = sizeof(BltFx);
4970 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
4971 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
4972 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
4975 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
4976 struct wined3d_surface *surface, const RECT *rect, float depth)
4978 FIXME("Depth filling not implemented by cpu_blit.\n");
4979 return WINED3DERR_INVALIDCALL;
4982 static void cpu_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
4983 struct wined3d_surface *src_surface, const RECT *src_rect,
4984 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4985 const struct wined3d_color_key *color_key)
4987 /* FIXME: Remove error returns from surface_blt_cpu. */
4988 ERR("Blit method not implemented by cpu_blit.\n");
4991 const struct blit_shader cpu_blit = {
4992 cpu_blit_alloc,
4993 cpu_blit_free,
4994 cpu_blit_set,
4995 cpu_blit_unset,
4996 cpu_blit_supported,
4997 cpu_blit_color_fill,
4998 cpu_blit_depth_fill,
4999 cpu_blit_blit_surface,
5002 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5003 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5004 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5006 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5007 struct wined3d_device *device = dst_surface->resource.device;
5008 DWORD src_ds_flags, dst_ds_flags;
5009 RECT src_rect, dst_rect;
5010 BOOL scale, convert;
5012 static const DWORD simple_blit = WINEDDBLT_ASYNC
5013 | WINEDDBLT_COLORFILL
5014 | WINEDDBLT_KEYSRC
5015 | WINEDDBLT_KEYSRCOVERRIDE
5016 | WINEDDBLT_WAIT
5017 | WINEDDBLT_DEPTHFILL
5018 | WINEDDBLT_DONOTWAIT
5019 | WINEDDBLT_ALPHATEST;
5021 TRACE("dst_surface %p, dst_rect_in %s, src_surface %p, src_rect_in %s, flags %#x, fx %p, filter %s.\n",
5022 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
5023 flags, fx, debug_d3dtexturefiltertype(filter));
5024 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5026 if (fx)
5028 TRACE("dwSize %#x.\n", fx->dwSize);
5029 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5030 TRACE("dwROP %#x.\n", fx->dwROP);
5031 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5032 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5033 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5034 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5035 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5036 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5037 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5038 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5039 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5040 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5041 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5042 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5043 TRACE("dwReserved %#x.\n", fx->dwReserved);
5044 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5045 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5046 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5047 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5048 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5049 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5050 fx->ddckDestColorkey.color_space_low_value,
5051 fx->ddckDestColorkey.color_space_high_value);
5052 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5053 fx->ddckSrcColorkey.color_space_low_value,
5054 fx->ddckSrcColorkey.color_space_high_value);
5057 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5059 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5060 return WINEDDERR_SURFACEBUSY;
5063 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5065 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5066 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5067 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5068 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5069 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5071 WARN("The application gave us a bad destination rectangle.\n");
5072 return WINEDDERR_INVALIDRECT;
5075 if (src_surface)
5077 surface_get_rect(src_surface, src_rect_in, &src_rect);
5079 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5080 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5081 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5082 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5083 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5085 WARN("Application gave us bad source rectangle for Blt.\n");
5086 return WINEDDERR_INVALIDRECT;
5089 else
5091 memset(&src_rect, 0, sizeof(src_rect));
5094 if (!fx || !(fx->dwDDFX))
5095 flags &= ~WINEDDBLT_DDFX;
5097 if (flags & WINEDDBLT_WAIT)
5098 flags &= ~WINEDDBLT_WAIT;
5100 if (flags & WINEDDBLT_ASYNC)
5102 static unsigned int once;
5104 if (!once++)
5105 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5106 flags &= ~WINEDDBLT_ASYNC;
5109 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5110 if (flags & WINEDDBLT_DONOTWAIT)
5112 static unsigned int once;
5114 if (!once++)
5115 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5116 flags &= ~WINEDDBLT_DONOTWAIT;
5119 if (!device->d3d_initialized)
5121 WARN("D3D not initialized, using fallback.\n");
5122 goto cpu;
5125 /* We want to avoid invalidating the sysmem location for converted
5126 * surfaces, since otherwise we'd have to convert the data back when
5127 * locking them. */
5128 if (dst_surface->container->flags & WINED3D_TEXTURE_CONVERTED
5129 || dst_surface->container->resource.format->convert
5130 || wined3d_format_get_color_key_conversion(dst_surface->container, TRUE))
5132 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5133 goto cpu;
5136 if (flags & ~simple_blit)
5138 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5139 goto fallback;
5142 if (src_surface)
5143 src_swapchain = src_surface->container->swapchain;
5144 else
5145 src_swapchain = NULL;
5147 dst_swapchain = dst_surface->container->swapchain;
5149 /* This isn't strictly needed. FBO blits for example could deal with
5150 * cross-swapchain blits by first downloading the source to a texture
5151 * before switching to the destination context. We just have this here to
5152 * not have to deal with the issue, since cross-swapchain blits should be
5153 * rare. */
5154 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5156 FIXME("Using fallback for cross-swapchain blit.\n");
5157 goto fallback;
5160 scale = src_surface
5161 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5162 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5163 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5165 dst_ds_flags = dst_surface->container->resource.format_flags
5166 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5167 if (src_surface)
5168 src_ds_flags = src_surface->container->resource.format_flags
5169 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5170 else
5171 src_ds_flags = 0;
5173 if (src_ds_flags || dst_ds_flags)
5175 if (flags & WINEDDBLT_DEPTHFILL)
5177 float depth;
5179 TRACE("Depth fill.\n");
5181 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5182 return WINED3DERR_INVALIDCALL;
5184 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
5185 return WINED3D_OK;
5187 else
5189 if (src_ds_flags != dst_ds_flags)
5191 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
5192 return WINED3DERR_INVALIDCALL;
5195 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
5196 &src_rect, dst_surface, dst_surface->container->resource.draw_binding, &dst_rect)))
5197 return WINED3D_OK;
5200 else
5202 const struct blit_shader *blitter;
5204 /* In principle this would apply to depth blits as well, but we don't
5205 * implement those in the CPU blitter at the moment. */
5206 if ((dst_surface->locations & dst_surface->resource.map_binding)
5207 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
5209 if (scale)
5210 TRACE("Not doing sysmem blit because of scaling.\n");
5211 else if (convert)
5212 TRACE("Not doing sysmem blit because of format conversion.\n");
5213 else
5214 goto cpu;
5217 if (flags & WINEDDBLT_COLORFILL)
5219 struct wined3d_color color;
5220 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
5222 TRACE("Color fill.\n");
5224 if (!wined3d_format_convert_color_to_float(dst_surface->resource.format,
5225 palette, fx->u5.dwFillColor, &color))
5226 goto fallback;
5228 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
5229 return WINED3D_OK;
5231 else
5233 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
5234 const struct wined3d_color_key *color_key = NULL;
5236 TRACE("Color blit.\n");
5237 if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5239 color_key = &fx->ddckSrcColorkey;
5240 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5242 else if (flags & WINEDDBLT_KEYSRC)
5244 color_key = &src_surface->container->async.src_blt_color_key;
5245 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5247 else if (flags & WINEDDBLT_ALPHATEST)
5249 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
5251 else if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5252 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5254 /* Upload */
5255 if (scale)
5256 TRACE("Not doing upload because of scaling.\n");
5257 else if (convert)
5258 TRACE("Not doing upload because of format conversion.\n");
5259 else
5261 POINT dst_point = {dst_rect.left, dst_rect.top};
5263 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
5265 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
5267 struct wined3d_context *context = context_acquire(device, dst_surface);
5268 surface_load_location(dst_surface, context, dst_surface->container->resource.draw_binding);
5269 context_release(context);
5271 return WINED3D_OK;
5275 else if (dst_swapchain && dst_swapchain->back_buffers
5276 && dst_surface->container == dst_swapchain->front_buffer
5277 && src_surface->container == dst_swapchain->back_buffers[0])
5279 /* Use present for back -> front blits. The idea behind this is
5280 * that present is potentially faster than a blit, in particular
5281 * when FBO blits aren't available. Some ddraw applications like
5282 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5283 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5284 * applications can't blit directly to the frontbuffer. */
5285 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5287 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5289 /* Set the swap effect to COPY, we don't want the backbuffer
5290 * to become undefined. */
5291 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5292 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5293 dst_swapchain->desc.swap_effect = swap_effect;
5295 return WINED3D_OK;
5298 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
5299 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5300 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5302 struct wined3d_context *context;
5303 TRACE("Using FBO blit.\n");
5305 context = context_acquire(device, NULL);
5306 surface_blt_fbo(device, context, filter,
5307 src_surface, src_surface->container->resource.draw_binding, &src_rect,
5308 dst_surface, dst_surface->container->resource.draw_binding, &dst_rect);
5309 context_release(context);
5311 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5312 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5314 return WINED3D_OK;
5317 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
5318 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5319 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format);
5320 if (blitter)
5322 blitter->blit_surface(device, blit_op, filter, src_surface,
5323 &src_rect, dst_surface, &dst_rect, color_key);
5324 return WINED3D_OK;
5329 fallback:
5330 /* Special cases for render targets. */
5331 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
5332 return WINED3D_OK;
5334 cpu:
5336 /* For the rest call the X11 surface implementation. For render targets
5337 * this should be implemented OpenGL accelerated in surface_blt_special(),
5338 * other blits are rather rare. */
5339 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
5342 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5343 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5345 struct wined3d_device *device = container->resource.device;
5346 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5347 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5348 UINT multisample_quality = desc->multisample_quality;
5349 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
5350 unsigned int resource_size;
5351 HRESULT hr;
5353 /* Quick lockable sanity check.
5354 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5355 * this function is too deep to need to care about things like this.
5356 * Levels need to be checked too, since they all affect what can be done. */
5357 switch (desc->pool)
5359 case WINED3D_POOL_MANAGED:
5360 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5361 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5362 break;
5364 case WINED3D_POOL_DEFAULT:
5365 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5366 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5367 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5368 break;
5370 case WINED3D_POOL_SCRATCH:
5371 case WINED3D_POOL_SYSTEM_MEM:
5372 break;
5374 default:
5375 FIXME("Unknown pool %#x.\n", desc->pool);
5376 break;
5379 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5380 FIXME("Trying to create a render target that isn't in the default pool.\n");
5382 /* FIXME: Check that the format is supported by the device. */
5384 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5385 if (!resource_size)
5386 return WINED3DERR_INVALIDCALL;
5388 if (device->wined3d->flags & WINED3D_NO3D)
5389 surface->surface_ops = &gdi_surface_ops;
5390 else
5391 surface->surface_ops = &surface_ops;
5393 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE,
5394 format, desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height,
5395 1, resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5397 WARN("Failed to initialize resource, returning %#x.\n", hr);
5398 return hr;
5401 surface->container = container;
5402 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5403 list_init(&surface->renderbuffers);
5404 list_init(&surface->overlays);
5406 /* Flags */
5407 if (flags & WINED3D_SURFACE_DISCARD)
5408 surface->flags |= SFLAG_DISCARD;
5409 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5410 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5412 surface->texture_target = target;
5413 surface->texture_level = level;
5414 surface->texture_layer = layer;
5416 /* Call the private setup routine */
5417 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5419 ERR("Private setup failed, hr %#x.\n", hr);
5420 surface_cleanup(surface);
5421 return hr;
5424 /* Similar to lockable rendertargets above, creating the DIB section
5425 * during surface initialization prevents the sysmem pointer from changing
5426 * after a wined3d_surface_getdc() call. */
5427 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5428 && SUCCEEDED(surface_create_dib_section(surface)))
5429 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5431 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5433 wined3d_resource_free_sysmem(&surface->resource);
5434 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5435 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5438 return hr;
5441 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5442 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
5444 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
5445 const struct wined3d_parent_ops *parent_ops;
5446 struct wined3d_surface *object;
5447 void *parent;
5448 HRESULT hr;
5450 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
5451 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
5452 container, desc->width, desc->height, debug_d3dformat(desc->format),
5453 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
5454 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
5456 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
5457 return E_OUTOFMEMORY;
5459 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
5461 WARN("Failed to initialize surface, returning %#x.\n", hr);
5462 HeapFree(GetProcessHeap(), 0, object);
5463 return hr;
5466 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
5467 container, layer * container->level_count + level, object, &parent, &parent_ops)))
5469 WARN("Failed to create surface parent, hr %#x.\n", hr);
5470 wined3d_surface_destroy(object);
5471 return hr;
5474 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
5476 object->resource.parent = parent;
5477 object->resource.parent_ops = parent_ops;
5478 *surface = object;
5480 return hr;
5483 /* Context activation is done by the caller. */
5484 void wined3d_surface_prepare(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5486 switch (location)
5488 case WINED3D_LOCATION_TEXTURE_RGB:
5489 wined3d_texture_prepare_texture(surface->container, context, FALSE);
5490 break;
5492 case WINED3D_LOCATION_TEXTURE_SRGB:
5493 wined3d_texture_prepare_texture(surface->container, context, TRUE);
5494 break;
5496 case WINED3D_LOCATION_RB_MULTISAMPLE:
5497 surface_prepare_rb(surface, context->gl_info, TRUE);
5498 break;
5500 case WINED3D_LOCATION_RB_RESOLVED:
5501 surface_prepare_rb(surface, context->gl_info, FALSE);
5502 break;