wined3d: Convert to WINED3DFMT_B5G5R5A1_UNORM for WINED3D_CT_CK_B5G6R5.
[wine.git] / dlls / wined3d / surface.c
blob2ee3db1c4b2ed9859e185e08671ed3f2b2a00c42
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2011, 2013-2014 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d);
37 #define MAXLOCKCOUNT 50 /* After this amount of locks do not free the sysmem copy. */
39 static const DWORD surface_simple_locations =
40 WINED3D_LOCATION_SYSMEM | WINED3D_LOCATION_USER_MEMORY
41 | WINED3D_LOCATION_DIB | WINED3D_LOCATION_BUFFER;
43 static void surface_cleanup(struct wined3d_surface *surface)
45 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->pbo || surface->rb_multisample
50 || surface->rb_resolved || !list_empty(&surface->renderbuffers))
52 struct wined3d_renderbuffer_entry *entry, *entry2;
53 const struct wined3d_gl_info *gl_info;
54 struct wined3d_context *context;
56 context = context_acquire(surface->resource.device, NULL);
57 gl_info = context->gl_info;
59 if (surface->pbo)
61 TRACE("Deleting PBO %u.\n", surface->pbo);
62 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
65 if (surface->rb_multisample)
67 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
68 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
71 if (surface->rb_resolved)
73 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
74 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
77 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
79 TRACE("Deleting renderbuffer %u.\n", entry->id);
80 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
81 HeapFree(GetProcessHeap(), 0, entry);
84 context_release(context);
87 if (surface->flags & SFLAG_DIBSECTION)
89 DeleteDC(surface->hDC);
90 DeleteObject(surface->dib.DIBsection);
91 surface->dib.bitmap_data = NULL;
94 if (surface->overlay_dest)
95 list_remove(&surface->overlay_entry);
97 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
99 list_remove(&overlay->overlay_entry);
100 overlay->overlay_dest = NULL;
103 resource_cleanup(&surface->resource);
106 void wined3d_surface_destroy(struct wined3d_surface *surface)
108 TRACE("surface %p.\n", surface);
110 surface_cleanup(surface);
111 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
112 HeapFree(GetProcessHeap(), 0, surface);
115 void surface_get_drawable_size(const struct wined3d_surface *surface, const struct wined3d_context *context,
116 unsigned int *width, unsigned int *height)
118 if (surface->container->swapchain)
120 /* The drawable size of an onscreen drawable is the surface size.
121 * (Actually: The window size, but the surface is created in window
122 * size.) */
123 *width = context->current_rt->resource.width;
124 *height = context->current_rt->resource.height;
126 else if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER)
128 const struct wined3d_swapchain *swapchain = context->swapchain;
130 /* The drawable size of a backbuffer / aux buffer offscreen target is
131 * the size of the current context's drawable, which is the size of
132 * the back buffer of the swapchain the active context belongs to. */
133 *width = swapchain->desc.backbuffer_width;
134 *height = swapchain->desc.backbuffer_height;
136 else
138 /* The drawable size of an FBO target is the OpenGL texture size,
139 * which is the power of two size. */
140 *width = context->current_rt->pow2Width;
141 *height = context->current_rt->pow2Height;
145 struct blt_info
147 GLenum binding;
148 GLenum bind_target;
149 enum tex_types tex_type;
150 GLfloat coords[4][3];
153 struct float_rect
155 float l;
156 float t;
157 float r;
158 float b;
161 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
163 f->l = ((r->left * 2.0f) / w) - 1.0f;
164 f->t = ((r->top * 2.0f) / h) - 1.0f;
165 f->r = ((r->right * 2.0f) / w) - 1.0f;
166 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
169 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
171 GLfloat (*coords)[3] = info->coords;
172 struct float_rect f;
174 switch (target)
176 default:
177 FIXME("Unsupported texture target %#x\n", target);
178 /* Fall back to GL_TEXTURE_2D */
179 case GL_TEXTURE_2D:
180 info->binding = GL_TEXTURE_BINDING_2D;
181 info->bind_target = GL_TEXTURE_2D;
182 info->tex_type = tex_2d;
183 coords[0][0] = (float)rect->left / w;
184 coords[0][1] = (float)rect->top / h;
185 coords[0][2] = 0.0f;
187 coords[1][0] = (float)rect->right / w;
188 coords[1][1] = (float)rect->top / h;
189 coords[1][2] = 0.0f;
191 coords[2][0] = (float)rect->left / w;
192 coords[2][1] = (float)rect->bottom / h;
193 coords[2][2] = 0.0f;
195 coords[3][0] = (float)rect->right / w;
196 coords[3][1] = (float)rect->bottom / h;
197 coords[3][2] = 0.0f;
198 break;
200 case GL_TEXTURE_RECTANGLE_ARB:
201 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
202 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
203 info->tex_type = tex_rect;
204 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
205 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
206 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
207 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
208 break;
210 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
211 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
212 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
213 info->tex_type = tex_cube;
214 cube_coords_float(rect, w, h, &f);
216 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
217 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
218 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
219 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
220 break;
222 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
223 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
224 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
225 info->tex_type = tex_cube;
226 cube_coords_float(rect, w, h, &f);
228 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
229 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
230 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
231 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
232 break;
234 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
235 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
236 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
237 info->tex_type = tex_cube;
238 cube_coords_float(rect, w, h, &f);
240 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
241 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
242 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
243 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
244 break;
246 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
247 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
248 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
249 info->tex_type = tex_cube;
250 cube_coords_float(rect, w, h, &f);
252 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
253 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
254 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
255 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
256 break;
258 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
259 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
260 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
261 info->tex_type = tex_cube;
262 cube_coords_float(rect, w, h, &f);
264 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
265 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
266 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
267 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
268 break;
270 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
271 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
272 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
273 info->tex_type = tex_cube;
274 cube_coords_float(rect, w, h, &f);
276 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
277 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
278 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
279 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
280 break;
284 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
286 if (rect_in)
287 *rect_out = *rect_in;
288 else
290 rect_out->left = 0;
291 rect_out->top = 0;
292 rect_out->right = surface->resource.width;
293 rect_out->bottom = surface->resource.height;
297 /* Context activation is done by the caller. */
298 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
299 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
301 const struct wined3d_gl_info *gl_info = context->gl_info;
302 struct wined3d_texture *texture = src_surface->container;
303 struct blt_info info;
305 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
307 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
308 checkGLcall("glEnable(bind_target)");
310 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
312 /* Filtering for StretchRect */
313 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
314 wined3d_gl_mag_filter(magLookup, filter));
315 checkGLcall("glTexParameteri");
316 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
317 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
318 checkGLcall("glTexParameteri");
319 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
320 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
321 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
322 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
323 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
324 checkGLcall("glTexEnvi");
326 /* Draw a quad */
327 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
328 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
329 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
331 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
332 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
334 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
335 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
337 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
338 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
339 gl_info->gl_ops.gl.p_glEnd();
341 /* Unbind the texture */
342 context_bind_texture(context, info.bind_target, 0);
344 /* We changed the filtering settings on the texture. Inform the
345 * container about this to get the filters reset properly next draw. */
346 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_POINT;
347 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
348 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_NONE;
349 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
352 /* Works correctly only for <= 4 bpp formats. */
353 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
355 masks[0] = ((1 << format->red_size) - 1) << format->red_offset;
356 masks[1] = ((1 << format->green_size) - 1) << format->green_offset;
357 masks[2] = ((1 << format->blue_size) - 1) << format->blue_offset;
360 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
362 const struct wined3d_format *format = surface->resource.format;
363 SYSTEM_INFO sysInfo;
364 BITMAPINFO *b_info;
365 int extraline = 0;
366 DWORD *masks;
368 TRACE("surface %p.\n", surface);
370 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
372 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
373 return WINED3DERR_INVALIDCALL;
376 switch (format->byte_count)
378 case 2:
379 case 4:
380 /* Allocate extra space to store the RGB bit masks. */
381 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
382 break;
384 case 3:
385 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
386 break;
388 default:
389 /* Allocate extra space for a palette. */
390 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
391 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
392 break;
395 if (!b_info)
396 return E_OUTOFMEMORY;
398 /* Some applications access the surface in via DWORDs, and do not take
399 * the necessary care at the end of the surface. So we need at least
400 * 4 extra bytes at the end of the surface. Check against the page size,
401 * if the last page used for the surface has at least 4 spare bytes we're
402 * safe, otherwise add an extra line to the DIB section. */
403 GetSystemInfo(&sysInfo);
404 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
406 extraline = 1;
407 TRACE("Adding an extra line to the DIB section.\n");
410 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
411 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
412 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
413 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
414 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
415 * wined3d_surface_get_pitch(surface);
416 b_info->bmiHeader.biPlanes = 1;
417 b_info->bmiHeader.biBitCount = format->byte_count * 8;
419 b_info->bmiHeader.biXPelsPerMeter = 0;
420 b_info->bmiHeader.biYPelsPerMeter = 0;
421 b_info->bmiHeader.biClrUsed = 0;
422 b_info->bmiHeader.biClrImportant = 0;
424 /* Get the bit masks */
425 masks = (DWORD *)b_info->bmiColors;
426 switch (surface->resource.format->id)
428 case WINED3DFMT_B8G8R8_UNORM:
429 b_info->bmiHeader.biCompression = BI_RGB;
430 break;
432 case WINED3DFMT_B5G5R5X1_UNORM:
433 case WINED3DFMT_B5G5R5A1_UNORM:
434 case WINED3DFMT_B4G4R4A4_UNORM:
435 case WINED3DFMT_B4G4R4X4_UNORM:
436 case WINED3DFMT_B2G3R3_UNORM:
437 case WINED3DFMT_B2G3R3A8_UNORM:
438 case WINED3DFMT_R10G10B10A2_UNORM:
439 case WINED3DFMT_R8G8B8A8_UNORM:
440 case WINED3DFMT_R8G8B8X8_UNORM:
441 case WINED3DFMT_B10G10R10A2_UNORM:
442 case WINED3DFMT_B5G6R5_UNORM:
443 case WINED3DFMT_R16G16B16A16_UNORM:
444 b_info->bmiHeader.biCompression = BI_BITFIELDS;
445 get_color_masks(format, masks);
446 break;
448 default:
449 /* Don't know palette */
450 b_info->bmiHeader.biCompression = BI_RGB;
451 break;
454 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
455 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
456 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
457 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
459 if (!surface->dib.DIBsection)
461 ERR("Failed to create DIB section.\n");
462 HeapFree(GetProcessHeap(), 0, b_info);
463 return HRESULT_FROM_WIN32(GetLastError());
466 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
467 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
469 HeapFree(GetProcessHeap(), 0, b_info);
471 /* Now allocate a DC. */
472 surface->hDC = CreateCompatibleDC(0);
473 SelectObject(surface->hDC, surface->dib.DIBsection);
475 surface->flags |= SFLAG_DIBSECTION;
477 return WINED3D_OK;
480 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
481 DWORD location)
483 if (location & WINED3D_LOCATION_BUFFER)
485 data->addr = NULL;
486 data->buffer_object = surface->pbo;
487 return;
489 if (location & WINED3D_LOCATION_USER_MEMORY)
491 data->addr = surface->user_memory;
492 data->buffer_object = 0;
493 return;
495 if (location & WINED3D_LOCATION_DIB)
497 data->addr = surface->dib.bitmap_data;
498 data->buffer_object = 0;
499 return;
501 if (location & WINED3D_LOCATION_SYSMEM)
503 data->addr = surface->resource.heap_memory;
504 data->buffer_object = 0;
505 return;
508 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
509 data->addr = NULL;
510 data->buffer_object = 0;
513 static void surface_prepare_buffer(struct wined3d_surface *surface)
515 struct wined3d_context *context;
516 GLenum error;
517 const struct wined3d_gl_info *gl_info;
519 if (surface->pbo)
520 return;
522 context = context_acquire(surface->resource.device, NULL);
523 gl_info = context->gl_info;
525 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
526 error = gl_info->gl_ops.gl.p_glGetError();
527 if (!surface->pbo || error != GL_NO_ERROR)
528 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
530 TRACE("Binding PBO %u.\n", surface->pbo);
532 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
533 checkGLcall("glBindBufferARB");
535 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
536 NULL, GL_STREAM_DRAW_ARB));
537 checkGLcall("glBufferDataARB");
539 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
540 checkGLcall("glBindBufferARB");
542 context_release(context);
545 static void surface_prepare_system_memory(struct wined3d_surface *surface)
547 TRACE("surface %p.\n", surface);
549 if (surface->resource.heap_memory)
550 return;
552 /* Whatever surface we have, make sure that there is memory allocated
553 * for the downloaded copy, or a PBO to map. */
554 if (!wined3d_resource_allocate_sysmem(&surface->resource))
555 ERR("Failed to allocate system memory.\n");
557 if (surface->locations & WINED3D_LOCATION_SYSMEM)
558 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
561 void surface_prepare_map_memory(struct wined3d_surface *surface)
563 switch (surface->resource.map_binding)
565 case WINED3D_LOCATION_SYSMEM:
566 surface_prepare_system_memory(surface);
567 break;
569 case WINED3D_LOCATION_USER_MEMORY:
570 if (!surface->user_memory)
571 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
572 break;
574 case WINED3D_LOCATION_DIB:
575 if (!surface->dib.bitmap_data)
576 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
577 break;
579 case WINED3D_LOCATION_BUFFER:
580 surface_prepare_buffer(surface);
581 break;
583 default:
584 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
588 static void surface_evict_sysmem(struct wined3d_surface *surface)
590 /* In some conditions the surface memory must not be freed:
591 * SFLAG_CONVERTED: Converting the data back would take too long
592 * SFLAG_DYNLOCK: Avoid freeing the data for performance
593 * SFLAG_CLIENT: OpenGL uses our memory as backup */
594 if (surface->resource.map_count || surface->flags & (SFLAG_CONVERTED | SFLAG_DYNLOCK
595 | SFLAG_CLIENT | SFLAG_PIN_SYSMEM))
596 return;
598 wined3d_resource_free_sysmem(&surface->resource);
599 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
602 static void surface_force_reload(struct wined3d_surface *surface)
604 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
607 static void surface_release_client_storage(struct wined3d_surface *surface)
609 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
610 const struct wined3d_gl_info *gl_info = context->gl_info;
612 if (surface->container->texture_rgb.name)
614 wined3d_texture_bind_and_dirtify(surface->container, context, FALSE);
615 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
616 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
618 if (surface->container->texture_srgb.name)
620 wined3d_texture_bind_and_dirtify(surface->container, context, TRUE);
621 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
622 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
625 context_release(context);
627 surface_invalidate_location(surface, WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
628 surface_force_reload(surface);
631 static BOOL surface_use_pbo(const struct wined3d_surface *surface)
633 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
635 return surface->resource.pool == WINED3D_POOL_DEFAULT
636 && surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU
637 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
638 && !surface->resource.format->convert
639 && !(surface->flags & (SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM));
642 static HRESULT surface_private_setup(struct wined3d_surface *surface)
644 /* TODO: Check against the maximum texture sizes supported by the video card. */
645 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
646 unsigned int pow2Width, pow2Height;
648 TRACE("surface %p.\n", surface);
650 /* Non-power2 support */
651 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
652 || gl_info->supported[ARB_TEXTURE_RECTANGLE])
654 pow2Width = surface->resource.width;
655 pow2Height = surface->resource.height;
657 else
659 /* Find the nearest pow2 match */
660 pow2Width = pow2Height = 1;
661 while (pow2Width < surface->resource.width)
662 pow2Width <<= 1;
663 while (pow2Height < surface->resource.height)
664 pow2Height <<= 1;
666 surface->pow2Width = pow2Width;
667 surface->pow2Height = pow2Height;
669 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
671 /* TODO: Add support for non power two compressed textures. */
672 if (surface->resource.format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE))
674 FIXME("(%p) Compressed or height scaled non-power-two textures are not supported w(%d) h(%d)\n",
675 surface, surface->resource.width, surface->resource.height);
676 return WINED3DERR_NOTAVAILABLE;
680 if (pow2Width != surface->resource.width
681 || pow2Height != surface->resource.height)
683 surface->flags |= SFLAG_NONPOW2;
686 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
687 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
689 /* One of three options:
690 * 1: Do the same as we do with NPOT and scale the texture, (any
691 * texture ops would require the texture to be scaled which is
692 * potentially slow)
693 * 2: Set the texture to the maximum size (bad idea).
694 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
695 * 4: Create the surface, but allow it to be used only for DirectDraw
696 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
697 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
698 * the render target. */
699 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
701 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
702 return WINED3DERR_NOTAVAILABLE;
705 /* We should never use this surface in combination with OpenGL! */
706 TRACE("Creating an oversized surface: %ux%u.\n",
707 surface->pow2Width, surface->pow2Height);
710 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
711 surface->locations = WINED3D_LOCATION_DISCARDED;
713 if (surface_use_pbo(surface))
714 surface->resource.map_binding = WINED3D_LOCATION_BUFFER;
716 return WINED3D_OK;
719 static void surface_unmap(struct wined3d_surface *surface)
721 struct wined3d_device *device = surface->resource.device;
722 const struct wined3d_gl_info *gl_info;
723 struct wined3d_context *context;
725 TRACE("surface %p.\n", surface);
727 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
729 switch (surface->resource.map_binding)
731 case WINED3D_LOCATION_SYSMEM:
732 case WINED3D_LOCATION_USER_MEMORY:
733 case WINED3D_LOCATION_DIB:
734 break;
736 case WINED3D_LOCATION_BUFFER:
737 context = context_acquire(device, NULL);
738 gl_info = context->gl_info;
740 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
741 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
742 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
743 checkGLcall("glUnmapBufferARB");
744 context_release(context);
745 break;
747 default:
748 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
751 if (surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_TEXTURE_RGB))
753 TRACE("Not dirtified, nothing to do.\n");
754 return;
757 if (surface->container->swapchain && surface->container->swapchain->front_buffer == surface->container)
758 surface_load_location(surface, surface->container->resource.draw_binding);
759 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
760 FIXME("Depth / stencil buffer locking is not implemented.\n");
763 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
765 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
766 return FALSE;
767 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
768 return FALSE;
769 return TRUE;
772 static void surface_depth_blt_fbo(const struct wined3d_device *device,
773 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
774 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
776 const struct wined3d_gl_info *gl_info;
777 struct wined3d_context *context;
778 DWORD src_mask, dst_mask;
779 GLbitfield gl_mask;
781 TRACE("device %p\n", device);
782 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
783 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
784 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
785 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
787 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
788 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
790 if (src_mask != dst_mask)
792 ERR("Incompatible formats %s and %s.\n",
793 debug_d3dformat(src_surface->resource.format->id),
794 debug_d3dformat(dst_surface->resource.format->id));
795 return;
798 if (!src_mask)
800 ERR("Not a depth / stencil format: %s.\n",
801 debug_d3dformat(src_surface->resource.format->id));
802 return;
805 gl_mask = 0;
806 if (src_mask & WINED3DFMT_FLAG_DEPTH)
807 gl_mask |= GL_DEPTH_BUFFER_BIT;
808 if (src_mask & WINED3DFMT_FLAG_STENCIL)
809 gl_mask |= GL_STENCIL_BUFFER_BIT;
811 /* Make sure the locations are up-to-date. Loading the destination
812 * surface isn't required if the entire surface is overwritten. */
813 surface_load_location(src_surface, src_location);
814 if (!surface_is_full_rect(dst_surface, dst_rect))
815 surface_load_location(dst_surface, dst_location);
817 context = context_acquire(device, NULL);
818 if (!context->valid)
820 context_release(context);
821 WARN("Invalid context, skipping blit.\n");
822 return;
825 gl_info = context->gl_info;
827 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
828 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
830 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
831 context_set_draw_buffer(context, GL_NONE);
832 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
833 context_invalidate_state(context, STATE_FRAMEBUFFER);
835 if (gl_mask & GL_DEPTH_BUFFER_BIT)
837 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
838 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
840 if (gl_mask & GL_STENCIL_BUFFER_BIT)
842 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
844 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
845 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
847 gl_info->gl_ops.gl.p_glStencilMask(~0U);
848 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
851 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
852 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
854 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
855 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
856 checkGLcall("glBlitFramebuffer()");
858 if (wined3d_settings.strict_draw_ordering)
859 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
861 context_release(context);
864 /* Blit between surface locations. Onscreen on different swapchains is not supported.
865 * Depth / stencil is not supported. */
866 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
867 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
868 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
870 const struct wined3d_gl_info *gl_info;
871 struct wined3d_context *context;
872 RECT src_rect, dst_rect;
873 GLenum gl_filter;
874 GLenum buffer;
876 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
877 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
878 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
879 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
880 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
882 src_rect = *src_rect_in;
883 dst_rect = *dst_rect_in;
885 switch (filter)
887 case WINED3D_TEXF_LINEAR:
888 gl_filter = GL_LINEAR;
889 break;
891 default:
892 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
893 case WINED3D_TEXF_NONE:
894 case WINED3D_TEXF_POINT:
895 gl_filter = GL_NEAREST;
896 break;
899 /* Resolve the source surface first if needed. */
900 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
901 && (src_surface->resource.format->id != dst_surface->resource.format->id
902 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
903 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
904 src_location = WINED3D_LOCATION_RB_RESOLVED;
906 /* Make sure the locations are up-to-date. Loading the destination
907 * surface isn't required if the entire surface is overwritten. (And is
908 * in fact harmful if we're being called by surface_load_location() with
909 * the purpose of loading the destination surface.) */
910 surface_load_location(src_surface, src_location);
911 if (!surface_is_full_rect(dst_surface, &dst_rect))
912 surface_load_location(dst_surface, dst_location);
914 if (src_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, src_surface);
915 else if (dst_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, dst_surface);
916 else context = context_acquire(device, NULL);
918 if (!context->valid)
920 context_release(context);
921 WARN("Invalid context, skipping blit.\n");
922 return;
925 gl_info = context->gl_info;
927 if (src_location == WINED3D_LOCATION_DRAWABLE)
929 TRACE("Source surface %p is onscreen.\n", src_surface);
930 buffer = surface_get_gl_buffer(src_surface);
931 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
933 else
935 TRACE("Source surface %p is offscreen.\n", src_surface);
936 buffer = GL_COLOR_ATTACHMENT0;
939 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
940 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
941 checkGLcall("glReadBuffer()");
942 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
944 if (dst_location == WINED3D_LOCATION_DRAWABLE)
946 TRACE("Destination surface %p is onscreen.\n", dst_surface);
947 buffer = surface_get_gl_buffer(dst_surface);
948 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
950 else
952 TRACE("Destination surface %p is offscreen.\n", dst_surface);
953 buffer = GL_COLOR_ATTACHMENT0;
956 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
957 context_set_draw_buffer(context, buffer);
958 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
959 context_invalidate_state(context, STATE_FRAMEBUFFER);
961 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
962 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
963 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
964 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
965 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
967 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
968 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
970 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
971 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
972 checkGLcall("glBlitFramebuffer()");
974 if (wined3d_settings.strict_draw_ordering
975 || (dst_location == WINED3D_LOCATION_DRAWABLE
976 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
977 gl_info->gl_ops.gl.p_glFlush();
979 context_release(context);
982 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
983 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
984 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
986 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
987 return FALSE;
989 /* Source and/or destination need to be on the GL side */
990 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
991 return FALSE;
993 switch (blit_op)
995 case WINED3D_BLIT_OP_COLOR_BLIT:
996 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
997 return FALSE;
998 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
999 return FALSE;
1000 break;
1002 case WINED3D_BLIT_OP_DEPTH_BLIT:
1003 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1004 return FALSE;
1005 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1006 return FALSE;
1007 break;
1009 default:
1010 return FALSE;
1013 if (!(src_format->id == dst_format->id
1014 || (is_identity_fixup(src_format->color_fixup)
1015 && is_identity_fixup(dst_format->color_fixup))))
1016 return FALSE;
1018 return TRUE;
1021 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1022 DWORD color, struct wined3d_color *float_color)
1024 const struct wined3d_format *format = surface->resource.format;
1025 const struct wined3d_palette *palette;
1027 switch (format->id)
1029 case WINED3DFMT_P8_UINT:
1030 palette = surface->container->swapchain ? surface->container->swapchain->palette : NULL;
1032 if (palette)
1034 float_color->r = palette->colors[color].rgbRed / 255.0f;
1035 float_color->g = palette->colors[color].rgbGreen / 255.0f;
1036 float_color->b = palette->colors[color].rgbBlue / 255.0f;
1038 else
1040 float_color->r = 0.0f;
1041 float_color->g = 0.0f;
1042 float_color->b = 0.0f;
1044 float_color->a = color / 255.0f;
1045 break;
1047 case WINED3DFMT_B5G6R5_UNORM:
1048 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1049 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1050 float_color->b = (color & 0x1f) / 31.0f;
1051 float_color->a = 1.0f;
1052 break;
1054 case WINED3DFMT_B8G8R8_UNORM:
1055 case WINED3DFMT_B8G8R8X8_UNORM:
1056 float_color->r = D3DCOLOR_R(color);
1057 float_color->g = D3DCOLOR_G(color);
1058 float_color->b = D3DCOLOR_B(color);
1059 float_color->a = 1.0f;
1060 break;
1062 case WINED3DFMT_B8G8R8A8_UNORM:
1063 float_color->r = D3DCOLOR_R(color);
1064 float_color->g = D3DCOLOR_G(color);
1065 float_color->b = D3DCOLOR_B(color);
1066 float_color->a = D3DCOLOR_A(color);
1067 break;
1069 default:
1070 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1071 return FALSE;
1074 return TRUE;
1077 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1079 const struct wined3d_format *format = surface->resource.format;
1081 switch (format->id)
1083 case WINED3DFMT_S1_UINT_D15_UNORM:
1084 *float_depth = depth / (float)0x00007fff;
1085 break;
1087 case WINED3DFMT_D16_UNORM:
1088 *float_depth = depth / (float)0x0000ffff;
1089 break;
1091 case WINED3DFMT_D24_UNORM_S8_UINT:
1092 case WINED3DFMT_X8D24_UNORM:
1093 *float_depth = depth / (float)0x00ffffff;
1094 break;
1096 case WINED3DFMT_D32_UNORM:
1097 *float_depth = depth / (float)0xffffffff;
1098 break;
1100 default:
1101 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1102 return FALSE;
1105 return TRUE;
1108 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1110 const struct wined3d_resource *resource = &surface->container->resource;
1111 struct wined3d_device *device = resource->device;
1112 const struct blit_shader *blitter;
1114 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1115 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1116 if (!blitter)
1118 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1119 return WINED3DERR_INVALIDCALL;
1122 return blitter->depth_fill(device, surface, rect, depth);
1125 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1126 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1128 struct wined3d_device *device = src_surface->resource.device;
1130 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1131 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1132 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1133 return WINED3DERR_INVALIDCALL;
1135 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1137 surface_modify_ds_location(dst_surface, dst_location,
1138 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1140 return WINED3D_OK;
1143 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1144 struct wined3d_surface *render_target)
1146 TRACE("surface %p, render_target %p.\n", surface, render_target);
1148 /* TODO: Check surface sizes, pools, etc. */
1150 if (render_target->resource.multisample_type)
1151 return WINED3DERR_INVALIDCALL;
1153 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1156 /* Context activation is done by the caller. */
1157 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1159 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1160 checkGLcall("glDeleteBuffersARB(1, &surface->pbo)");
1162 surface->pbo = 0;
1163 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1166 static ULONG surface_resource_incref(struct wined3d_resource *resource)
1168 return wined3d_surface_incref(surface_from_resource(resource));
1171 static ULONG surface_resource_decref(struct wined3d_resource *resource)
1173 return wined3d_surface_decref(surface_from_resource(resource));
1176 static void surface_unload(struct wined3d_resource *resource)
1178 struct wined3d_surface *surface = surface_from_resource(resource);
1179 struct wined3d_renderbuffer_entry *entry, *entry2;
1180 struct wined3d_device *device = resource->device;
1181 const struct wined3d_gl_info *gl_info;
1182 struct wined3d_context *context;
1184 TRACE("surface %p.\n", surface);
1186 if (resource->pool == WINED3D_POOL_DEFAULT)
1188 /* Default pool resources are supposed to be destroyed before Reset is called.
1189 * Implicit resources stay however. So this means we have an implicit render target
1190 * or depth stencil. The content may be destroyed, but we still have to tear down
1191 * opengl resources, so we cannot leave early.
1193 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1194 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1195 * or the depth stencil into an FBO the texture or render buffer will be removed
1196 * and all flags get lost */
1197 surface_prepare_system_memory(surface);
1198 memset(surface->resource.heap_memory, 0, surface->resource.size);
1199 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1200 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1202 /* We also get here when the ddraw swapchain is destroyed, for example
1203 * for a mode switch. In this case this surface won't necessarily be
1204 * an implicit surface. We have to mark it lost so that the
1205 * application can restore it after the mode switch. */
1206 surface->flags |= SFLAG_LOST;
1208 else
1210 surface_prepare_map_memory(surface);
1211 surface_load_location(surface, surface->resource.map_binding);
1212 surface_invalidate_location(surface, ~surface->resource.map_binding);
1214 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1216 context = context_acquire(device, NULL);
1217 gl_info = context->gl_info;
1219 /* Destroy PBOs, but load them into real sysmem before */
1220 if (surface->pbo)
1221 surface_remove_pbo(surface, gl_info);
1223 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1224 * all application-created targets the application has to release the surface
1225 * before calling _Reset
1227 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1229 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1230 list_remove(&entry->entry);
1231 HeapFree(GetProcessHeap(), 0, entry);
1233 list_init(&surface->renderbuffers);
1234 surface->current_renderbuffer = NULL;
1236 if (surface->rb_multisample)
1238 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1239 surface->rb_multisample = 0;
1241 if (surface->rb_resolved)
1243 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1244 surface->rb_resolved = 0;
1247 context_release(context);
1249 resource_unload(resource);
1252 static const struct wined3d_resource_ops surface_resource_ops =
1254 surface_resource_incref,
1255 surface_resource_decref,
1256 surface_unload,
1259 static const struct wined3d_surface_ops surface_ops =
1261 surface_private_setup,
1262 surface_unmap,
1265 /*****************************************************************************
1266 * Initializes the GDI surface, aka creates the DIB section we render to
1267 * The DIB section creation is done by calling GetDC, which will create the
1268 * section and releasing the dc to allow the app to use it. The dib section
1269 * will stay until the surface is released
1271 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1272 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1273 * avoid confusion in the shared surface code.
1275 * Returns:
1276 * WINED3D_OK on success
1277 * The return values of called methods on failure
1279 *****************************************************************************/
1280 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1282 HRESULT hr;
1284 TRACE("surface %p.\n", surface);
1286 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1288 ERR("Overlays not yet supported by GDI surfaces.\n");
1289 return WINED3DERR_INVALIDCALL;
1292 /* Sysmem textures have memory already allocated - release it,
1293 * this avoids an unnecessary memcpy. */
1294 hr = surface_create_dib_section(surface);
1295 if (FAILED(hr))
1296 return hr;
1297 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1299 /* We don't mind the nonpow2 stuff in GDI. */
1300 surface->pow2Width = surface->resource.width;
1301 surface->pow2Height = surface->resource.height;
1303 return WINED3D_OK;
1306 static void gdi_surface_unmap(struct wined3d_surface *surface)
1308 TRACE("surface %p.\n", surface);
1310 /* Tell the swapchain to update the screen. */
1311 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
1312 x11_copy_to_screen(surface->container->swapchain, &surface->lockedRect);
1314 memset(&surface->lockedRect, 0, sizeof(RECT));
1317 static const struct wined3d_surface_ops gdi_surface_ops =
1319 gdi_surface_private_setup,
1320 gdi_surface_unmap,
1323 /* This call just downloads data, the caller is responsible for binding the
1324 * correct texture. */
1325 /* Context activation is done by the caller. */
1326 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1327 DWORD dst_location)
1329 const struct wined3d_format *format = surface->resource.format;
1330 struct wined3d_bo_address data;
1332 /* Only support read back of converted P8 surfaces. */
1333 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1335 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1336 return;
1339 surface_get_memory(surface, &data, dst_location);
1341 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1343 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
1344 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1346 if (data.buffer_object)
1348 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1349 checkGLcall("glBindBufferARB");
1350 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
1351 checkGLcall("glGetCompressedTexImageARB");
1352 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1353 checkGLcall("glBindBufferARB");
1355 else
1357 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
1358 surface->texture_level, data.addr));
1359 checkGLcall("glGetCompressedTexImageARB");
1362 else
1364 void *mem;
1365 GLenum gl_format = format->glFormat;
1366 GLenum gl_type = format->glType;
1367 int src_pitch = 0;
1368 int dst_pitch = 0;
1370 if (surface->flags & SFLAG_NONPOW2)
1372 unsigned char alignment = surface->resource.device->surface_alignment;
1373 src_pitch = format->byte_count * surface->pow2Width;
1374 dst_pitch = wined3d_surface_get_pitch(surface);
1375 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1376 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1378 else
1380 mem = data.addr;
1383 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1384 surface, surface->texture_level, gl_format, gl_type, mem);
1386 if (data.buffer_object)
1388 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1389 checkGLcall("glBindBufferARB");
1391 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1392 gl_format, gl_type, NULL);
1393 checkGLcall("glGetTexImage");
1395 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1396 checkGLcall("glBindBufferARB");
1398 else
1400 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1401 gl_format, gl_type, mem);
1402 checkGLcall("glGetTexImage");
1405 if (surface->flags & SFLAG_NONPOW2)
1407 const BYTE *src_data;
1408 BYTE *dst_data;
1409 UINT y;
1411 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1412 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1413 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1415 * We're doing this...
1417 * instead of boxing the texture :
1418 * |<-texture width ->| -->pow2width| /\
1419 * |111111111111111111| | |
1420 * |222 Texture 222222| boxed empty | texture height
1421 * |3333 Data 33333333| | |
1422 * |444444444444444444| | \/
1423 * ----------------------------------- |
1424 * | boxed empty | boxed empty | pow2height
1425 * | | | \/
1426 * -----------------------------------
1429 * we're repacking the data to the expected texture width
1431 * |<-texture width ->| -->pow2width| /\
1432 * |111111111111111111222222222222222| |
1433 * |222333333333333333333444444444444| texture height
1434 * |444444 | |
1435 * | | \/
1436 * | | |
1437 * | empty | pow2height
1438 * | | \/
1439 * -----------------------------------
1441 * == is the same as
1443 * |<-texture width ->| /\
1444 * |111111111111111111|
1445 * |222222222222222222|texture height
1446 * |333333333333333333|
1447 * |444444444444444444| \/
1448 * --------------------
1450 * This also means that any references to surface memory should work with the data as if it were a
1451 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1453 * internally the texture is still stored in a boxed format so any references to textureName will
1454 * get a boxed texture with width pow2width and not a texture of width resource.width.
1456 * Performance should not be an issue, because applications normally do not lock the surfaces when
1457 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
1458 * and doesn't have to be re-read. */
1459 src_data = mem;
1460 dst_data = data.addr;
1461 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1462 for (y = 0; y < surface->resource.height; ++y)
1464 memcpy(dst_data, src_data, dst_pitch);
1465 src_data += src_pitch;
1466 dst_data += dst_pitch;
1469 HeapFree(GetProcessHeap(), 0, mem);
1474 /* This call just uploads data, the caller is responsible for binding the
1475 * correct texture. */
1476 /* Context activation is done by the caller. */
1477 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1478 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1479 BOOL srgb, const struct wined3d_bo_address *data)
1481 UINT update_w = src_rect->right - src_rect->left;
1482 UINT update_h = src_rect->bottom - src_rect->top;
1484 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1485 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1486 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1488 if (surface->resource.map_count)
1490 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
1491 surface->flags |= SFLAG_PIN_SYSMEM;
1494 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1496 update_h *= format->height_scale.numerator;
1497 update_h /= format->height_scale.denominator;
1500 if (data->buffer_object)
1502 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
1503 checkGLcall("glBindBufferARB");
1506 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1508 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1509 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1510 const BYTE *addr = data->addr;
1511 GLenum internal;
1513 addr += (src_rect->top / format->block_height) * src_pitch;
1514 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1516 if (srgb)
1517 internal = format->glGammaInternal;
1518 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1519 && wined3d_resource_is_offscreen(&surface->container->resource))
1520 internal = format->rtInternal;
1521 else
1522 internal = format->glInternal;
1524 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
1525 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1526 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1528 if (row_length == src_pitch)
1530 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1531 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1533 else
1535 UINT row, y;
1537 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
1538 * can't use the unpack row length like below. */
1539 for (row = 0, y = dst_point->y; row < row_count; ++row)
1541 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1542 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1543 y += format->block_height;
1544 addr += src_pitch;
1547 checkGLcall("glCompressedTexSubImage2DARB");
1549 else
1551 const BYTE *addr = data->addr;
1553 addr += src_rect->top * src_pitch;
1554 addr += src_rect->left * format->byte_count;
1556 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1557 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1558 update_w, update_h, format->glFormat, format->glType, addr);
1560 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1561 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1562 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1563 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1564 checkGLcall("glTexSubImage2D");
1567 if (data->buffer_object)
1569 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1570 checkGLcall("glBindBufferARB");
1573 if (wined3d_settings.strict_draw_ordering)
1574 gl_info->gl_ops.gl.p_glFlush();
1576 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1578 struct wined3d_device *device = surface->resource.device;
1579 unsigned int i;
1581 for (i = 0; i < device->context_count; ++i)
1583 context_surface_update(device->contexts[i], surface);
1588 static void d3dfmt_get_conv(const struct wined3d_texture *texture, BOOL need_alpha_ck,
1589 struct wined3d_format *format, enum wined3d_conversion_type *conversion_type)
1591 BOOL colorkey_active = need_alpha_ck && (texture->color_key_flags & WINEDDSD_CKSRCBLT);
1592 const struct wined3d_device *device = texture->resource.device;
1593 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1594 unsigned int i;
1596 static const struct
1598 enum wined3d_format_id src_format;
1599 enum wined3d_conversion_type conversion_type;
1600 GLint gl_internal;
1601 GLint gl_format;
1602 GLint gl_type;
1603 unsigned int conv_byte_count;
1605 color_key_info[] =
1607 {WINED3DFMT_B5G6R5_UNORM, WINED3D_CT_CK_B5G6R5, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 2},
1608 {WINED3DFMT_B5G5R5X1_UNORM, WINED3D_CT_CK_B5G5R5X1, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 2},
1609 {WINED3DFMT_B8G8R8_UNORM, WINED3D_CT_CK_B8G8R8, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, 4},
1610 {WINED3DFMT_B8G8R8X8_UNORM, WINED3D_CT_CK_B8G8R8X8, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, 4},
1611 {WINED3DFMT_B8G8R8A8_UNORM, WINED3D_CT_CK_B8G8R8A8, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 4},
1614 *format = *texture->resource.format;
1615 *conversion_type = WINED3D_CT_NONE;
1617 if (colorkey_active)
1619 for (i = 0; i < sizeof(color_key_info) / sizeof(*color_key_info); ++i)
1621 if (color_key_info[i].src_format != texture->resource.format->id)
1622 continue;
1624 *conversion_type = color_key_info[i].conversion_type;
1625 format->glInternal = color_key_info[i].gl_internal;
1626 format->glFormat = color_key_info[i].gl_format;
1627 format->glType = color_key_info[i].gl_type;
1628 format->conv_byte_count = color_key_info[i].conv_byte_count;
1629 break;
1633 if (texture->resource.format->id == WINED3DFMT_P8_UINT)
1635 /* FIXME: This should check if the blitter backend can do P8
1636 * conversion, instead of checking for ARB_fragment_program. */
1637 if (!((gl_info->supported[ARB_FRAGMENT_PROGRAM] && texture->swapchain
1638 && texture == texture->swapchain->front_buffer)) || colorkey_active)
1640 *conversion_type = WINED3D_CT_P8;
1641 format->glInternal = GL_RGBA8;
1642 format->glFormat = GL_BGRA;
1643 format->glType = GL_UNSIGNED_INT_8_8_8_8_REV;
1644 format->conv_byte_count = 4;
1647 else if (texture->resource.format->id == WINED3DFMT_B2G3R3_UNORM && colorkey_active)
1649 /* This texture format will never be used... So do not care about
1650 * color-keying up until the point in time it will be needed. */
1651 FIXME("Color-keying not supported with WINED3DFMT_B2G3R3_UNORM.\n");
1654 if (*conversion_type != WINED3D_CT_NONE)
1656 format->rtInternal = format->glInternal;
1657 format->glGammaInternal = format->glInternal;
1661 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
1663 UINT width_mask, height_mask;
1665 if (!rect->left && !rect->top
1666 && rect->right == surface->resource.width
1667 && rect->bottom == surface->resource.height)
1668 return TRUE;
1670 /* This assumes power of two block sizes, but NPOT block sizes would be
1671 * silly anyway. */
1672 width_mask = surface->resource.format->block_width - 1;
1673 height_mask = surface->resource.format->block_height - 1;
1675 if (!(rect->left & width_mask) && !(rect->top & height_mask)
1676 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
1677 return TRUE;
1679 return FALSE;
1682 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1683 struct wined3d_surface *src_surface, const RECT *src_rect)
1685 const struct wined3d_format *src_format;
1686 const struct wined3d_format *dst_format;
1687 const struct wined3d_gl_info *gl_info;
1688 enum wined3d_conversion_type convert;
1689 struct wined3d_context *context;
1690 struct wined3d_bo_address data;
1691 struct wined3d_format format;
1692 UINT update_w, update_h;
1693 UINT dst_w, dst_h;
1694 RECT r, dst_rect;
1695 UINT src_pitch;
1696 POINT p;
1698 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1699 dst_surface, wine_dbgstr_point(dst_point),
1700 src_surface, wine_dbgstr_rect(src_rect));
1702 src_format = src_surface->resource.format;
1703 dst_format = dst_surface->resource.format;
1705 if (src_format->id != dst_format->id)
1707 WARN("Source and destination surfaces should have the same format.\n");
1708 return WINED3DERR_INVALIDCALL;
1711 if (!dst_point)
1713 p.x = 0;
1714 p.y = 0;
1715 dst_point = &p;
1717 else if (dst_point->x < 0 || dst_point->y < 0)
1719 WARN("Invalid destination point.\n");
1720 return WINED3DERR_INVALIDCALL;
1723 if (!src_rect)
1725 r.left = 0;
1726 r.top = 0;
1727 r.right = src_surface->resource.width;
1728 r.bottom = src_surface->resource.height;
1729 src_rect = &r;
1731 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1732 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1734 WARN("Invalid source rectangle.\n");
1735 return WINED3DERR_INVALIDCALL;
1738 dst_w = dst_surface->resource.width;
1739 dst_h = dst_surface->resource.height;
1741 update_w = src_rect->right - src_rect->left;
1742 update_h = src_rect->bottom - src_rect->top;
1744 if (update_w > dst_w || dst_point->x > dst_w - update_w
1745 || update_h > dst_h || dst_point->y > dst_h - update_h)
1747 WARN("Destination out of bounds.\n");
1748 return WINED3DERR_INVALIDCALL;
1751 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
1753 WARN("Source rectangle not block-aligned.\n");
1754 return WINED3DERR_INVALIDCALL;
1757 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1758 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
1760 WARN("Destination rectangle not block-aligned.\n");
1761 return WINED3DERR_INVALIDCALL;
1764 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1765 d3dfmt_get_conv(dst_surface->container, FALSE, &format, &convert);
1766 if (convert != WINED3D_CT_NONE || format.convert)
1767 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1769 context = context_acquire(dst_surface->resource.device, NULL);
1770 gl_info = context->gl_info;
1772 /* Only load the surface for partial updates. For newly allocated texture
1773 * the texture wouldn't be the current location, and we'd upload zeroes
1774 * just to overwrite them again. */
1775 if (update_w == dst_w && update_h == dst_h)
1776 surface_prepare_texture(dst_surface, context, FALSE);
1777 else
1778 surface_load_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1779 wined3d_texture_bind(dst_surface->container, context, FALSE);
1781 surface_get_memory(src_surface, &data, src_surface->locations);
1782 src_pitch = wined3d_surface_get_pitch(src_surface);
1784 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
1786 context_invalidate_active_texture(context);
1788 context_release(context);
1790 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1791 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1793 return WINED3D_OK;
1796 /* This call just allocates the texture, the caller is responsible for binding
1797 * the correct texture. */
1798 /* Context activation is done by the caller. */
1799 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1800 const struct wined3d_format *format, BOOL srgb)
1802 BOOL disable_client_storage = FALSE;
1803 GLsizei width = surface->pow2Width;
1804 GLsizei height = surface->pow2Height;
1805 const BYTE *mem = NULL;
1806 GLenum internal;
1808 if (srgb)
1809 internal = format->glGammaInternal;
1810 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1811 && wined3d_resource_is_offscreen(&surface->container->resource))
1812 internal = format->rtInternal;
1813 else
1814 internal = format->glInternal;
1816 if (!internal)
1817 FIXME("No GL internal format for format %s.\n", debug_d3dformat(format->id));
1819 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1821 height *= format->height_scale.numerator;
1822 height /= format->height_scale.denominator;
1825 TRACE("(%p) : Creating surface (target %#x) level %d, d3d format %s, internal format %#x, width %d, height %d, gl format %#x, gl type=%#x\n",
1826 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
1827 internal, width, height, format->glFormat, format->glType);
1829 if (gl_info->supported[APPLE_CLIENT_STORAGE])
1831 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
1832 || !surface->resource.heap_memory)
1834 /* In some cases we want to disable client storage.
1835 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
1836 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
1837 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
1838 * heap_memory == NULL: Not defined in the extension. Seems to disable client storage effectively
1840 surface->flags &= ~SFLAG_CLIENT;
1842 else
1844 surface->flags |= SFLAG_CLIENT;
1845 mem = surface->resource.heap_memory;
1847 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
1848 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
1849 disable_client_storage = TRUE;
1853 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
1855 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
1856 internal, width, height, 0, surface->resource.size, mem));
1857 checkGLcall("glCompressedTexImage2DARB");
1859 else
1861 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
1862 internal, width, height, 0, format->glFormat, format->glType, mem);
1863 checkGLcall("glTexImage2D");
1866 if (disable_client_storage)
1868 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
1869 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
1873 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1874 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1875 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1876 /* Context activation is done by the caller. */
1877 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1879 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1880 struct wined3d_renderbuffer_entry *entry;
1881 GLuint renderbuffer = 0;
1882 unsigned int src_width, src_height;
1883 unsigned int width, height;
1885 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1887 width = rt->pow2Width;
1888 height = rt->pow2Height;
1890 else
1892 width = surface->pow2Width;
1893 height = surface->pow2Height;
1896 src_width = surface->pow2Width;
1897 src_height = surface->pow2Height;
1899 /* A depth stencil smaller than the render target is not valid */
1900 if (width > src_width || height > src_height) return;
1902 /* Remove any renderbuffer set if the sizes match */
1903 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1904 || (width == src_width && height == src_height))
1906 surface->current_renderbuffer = NULL;
1907 return;
1910 /* Look if we've already got a renderbuffer of the correct dimensions */
1911 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1913 if (entry->width == width && entry->height == height)
1915 renderbuffer = entry->id;
1916 surface->current_renderbuffer = entry;
1917 break;
1921 if (!renderbuffer)
1923 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1924 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1925 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1926 surface->resource.format->glInternal, width, height);
1928 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1929 entry->width = width;
1930 entry->height = height;
1931 entry->id = renderbuffer;
1932 list_add_head(&surface->renderbuffers, &entry->entry);
1934 surface->current_renderbuffer = entry;
1937 checkGLcall("set_compatible_renderbuffer");
1940 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1942 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1944 TRACE("surface %p.\n", surface);
1946 if (!swapchain)
1948 ERR("Surface %p is not on a swapchain.\n", surface);
1949 return GL_NONE;
1952 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
1954 if (swapchain->render_to_fbo)
1956 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1957 return GL_COLOR_ATTACHMENT0;
1959 TRACE("Returning GL_BACK\n");
1960 return GL_BACK;
1962 else if (surface->container == swapchain->front_buffer)
1964 TRACE("Returning GL_FRONT\n");
1965 return GL_FRONT;
1968 FIXME("Higher back buffer, returning GL_BACK\n");
1969 return GL_BACK;
1972 void surface_load(struct wined3d_surface *surface, BOOL srgb)
1974 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1975 BOOL ck_changed;
1977 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1979 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
1980 ERR("Not supported on scratch surfaces.\n");
1982 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->container->color_key_flags & WINEDDSD_CKSRCBLT);
1984 /* Reload if either the texture and sysmem have different ideas about the
1985 * color key, or the actual key values changed. */
1986 if (ck_changed || ((surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
1987 && (surface->gl_color_key.color_space_low_value
1988 != surface->container->src_blt_color_key.color_space_low_value
1989 || surface->gl_color_key.color_space_high_value
1990 != surface->container->src_blt_color_key.color_space_high_value)))
1992 TRACE("Reloading because of color keying\n");
1993 /* To perform the color key conversion we need a sysmem copy of
1994 * the surface. Make sure we have it. */
1996 surface_prepare_map_memory(surface);
1997 surface_load_location(surface, surface->resource.map_binding);
1998 surface_invalidate_location(surface, ~surface->resource.map_binding);
1999 /* Switching color keying on / off may change the internal format. */
2000 if (ck_changed)
2001 surface_force_reload(surface);
2003 else if (!(surface->locations & location))
2005 TRACE("Reloading because surface is dirty.\n");
2007 else
2009 TRACE("surface is already in texture\n");
2010 return;
2013 surface_load_location(surface, location);
2014 surface_evict_sysmem(surface);
2017 /* See also float_16_to_32() in wined3d_private.h */
2018 static inline unsigned short float_32_to_16(const float *in)
2020 int exp = 0;
2021 float tmp = fabsf(*in);
2022 unsigned int mantissa;
2023 unsigned short ret;
2025 /* Deal with special numbers */
2026 if (*in == 0.0f)
2027 return 0x0000;
2028 if (isnan(*in))
2029 return 0x7c01;
2030 if (isinf(*in))
2031 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2033 if (tmp < powf(2, 10))
2037 tmp = tmp * 2.0f;
2038 exp--;
2039 } while (tmp < powf(2, 10));
2041 else if (tmp >= powf(2, 11))
2045 tmp /= 2.0f;
2046 exp++;
2047 } while (tmp >= powf(2, 11));
2050 mantissa = (unsigned int)tmp;
2051 if (tmp - mantissa >= 0.5f)
2052 ++mantissa; /* Round to nearest, away from zero. */
2054 exp += 10; /* Normalize the mantissa. */
2055 exp += 15; /* Exponent is encoded with excess 15. */
2057 if (exp > 30) /* too big */
2059 ret = 0x7c00; /* INF */
2061 else if (exp <= 0)
2063 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2064 while (exp <= 0)
2066 mantissa = mantissa >> 1;
2067 ++exp;
2069 ret = mantissa & 0x3ff;
2071 else
2073 ret = (exp << 10) | (mantissa & 0x3ff);
2076 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2077 return ret;
2080 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2082 TRACE("surface %p, container %p.\n", surface, surface->container);
2084 return wined3d_texture_incref(surface->container);
2087 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2089 TRACE("surface %p, container %p.\n", surface, surface->container);
2091 return wined3d_texture_decref(surface->container);
2094 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2096 TRACE("surface %p.\n", surface);
2098 if (!surface->resource.device->d3d_initialized)
2100 ERR("D3D not initialized.\n");
2101 return;
2104 wined3d_texture_preload(surface->container);
2107 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2109 TRACE("surface %p.\n", surface);
2111 return surface->resource.parent;
2114 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2116 TRACE("surface %p.\n", surface);
2118 return &surface->resource;
2121 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2123 TRACE("surface %p, flags %#x.\n", surface, flags);
2125 switch (flags)
2127 case WINEDDGBS_CANBLT:
2128 case WINEDDGBS_ISBLTDONE:
2129 return WINED3D_OK;
2131 default:
2132 return WINED3DERR_INVALIDCALL;
2136 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2138 TRACE("surface %p, flags %#x.\n", surface, flags);
2140 /* XXX: DDERR_INVALIDSURFACETYPE */
2142 switch (flags)
2144 case WINEDDGFS_CANFLIP:
2145 case WINEDDGFS_ISFLIPDONE:
2146 return WINED3D_OK;
2148 default:
2149 return WINED3DERR_INVALIDCALL;
2153 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2155 TRACE("surface %p.\n", surface);
2157 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2158 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2161 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2163 TRACE("surface %p.\n", surface);
2165 surface->flags &= ~SFLAG_LOST;
2166 return WINED3D_OK;
2169 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2171 unsigned int alignment;
2172 DWORD pitch;
2174 TRACE("surface %p.\n", surface);
2176 if (surface->pitch)
2177 return surface->pitch;
2179 alignment = surface->resource.device->surface_alignment;
2180 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
2181 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2183 TRACE("Returning %u.\n", pitch);
2185 return pitch;
2188 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2190 LONG w, h;
2192 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2194 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2196 WARN("Not an overlay surface.\n");
2197 return WINEDDERR_NOTAOVERLAYSURFACE;
2200 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2201 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2202 surface->overlay_destrect.left = x;
2203 surface->overlay_destrect.top = y;
2204 surface->overlay_destrect.right = x + w;
2205 surface->overlay_destrect.bottom = y + h;
2207 return WINED3D_OK;
2210 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2212 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2214 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2216 TRACE("Not an overlay surface.\n");
2217 return WINEDDERR_NOTAOVERLAYSURFACE;
2220 if (!surface->overlay_dest)
2222 TRACE("Overlay not visible.\n");
2223 *x = 0;
2224 *y = 0;
2225 return WINEDDERR_OVERLAYNOTVISIBLE;
2228 *x = surface->overlay_destrect.left;
2229 *y = surface->overlay_destrect.top;
2231 TRACE("Returning position %d, %d.\n", *x, *y);
2233 return WINED3D_OK;
2236 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2237 DWORD flags, struct wined3d_surface *ref)
2239 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2241 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2243 TRACE("Not an overlay surface.\n");
2244 return WINEDDERR_NOTAOVERLAYSURFACE;
2247 return WINED3D_OK;
2250 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2251 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2253 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2254 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2256 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2258 WARN("Not an overlay surface.\n");
2259 return WINEDDERR_NOTAOVERLAYSURFACE;
2261 else if (!dst_surface)
2263 WARN("Dest surface is NULL.\n");
2264 return WINED3DERR_INVALIDCALL;
2267 if (src_rect)
2269 surface->overlay_srcrect = *src_rect;
2271 else
2273 surface->overlay_srcrect.left = 0;
2274 surface->overlay_srcrect.top = 0;
2275 surface->overlay_srcrect.right = surface->resource.width;
2276 surface->overlay_srcrect.bottom = surface->resource.height;
2279 if (dst_rect)
2281 surface->overlay_destrect = *dst_rect;
2283 else
2285 surface->overlay_destrect.left = 0;
2286 surface->overlay_destrect.top = 0;
2287 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2288 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2291 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2293 surface->overlay_dest = NULL;
2294 list_remove(&surface->overlay_entry);
2297 if (flags & WINEDDOVER_SHOW)
2299 if (surface->overlay_dest != dst_surface)
2301 surface->overlay_dest = dst_surface;
2302 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2305 else if (flags & WINEDDOVER_HIDE)
2307 /* tests show that the rectangles are erased on hide */
2308 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2309 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2310 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2311 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2312 surface->overlay_dest = NULL;
2315 return WINED3D_OK;
2318 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface,
2319 const struct wined3d_gl_info *gl_info, void *mem, unsigned int pitch)
2321 struct wined3d_resource *texture_resource = &surface->container->resource;
2322 unsigned int width, height;
2323 BOOL create_dib = FALSE;
2324 DWORD valid_location = 0;
2325 HRESULT hr;
2327 if (surface->flags & SFLAG_DIBSECTION)
2329 DeleteDC(surface->hDC);
2330 DeleteObject(surface->dib.DIBsection);
2331 surface->dib.bitmap_data = NULL;
2332 surface->flags &= ~SFLAG_DIBSECTION;
2333 create_dib = TRUE;
2336 surface->locations = 0;
2337 wined3d_resource_free_sysmem(&surface->resource);
2339 width = texture_resource->width;
2340 height = texture_resource->height;
2341 surface->resource.width = width;
2342 surface->resource.height = height;
2343 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2344 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2346 surface->pow2Width = width;
2347 surface->pow2Height = height;
2349 else
2351 surface->pow2Width = surface->pow2Height = 1;
2352 while (surface->pow2Width < width)
2353 surface->pow2Width <<= 1;
2354 while (surface->pow2Height < height)
2355 surface->pow2Height <<= 1;
2358 if (surface->pow2Width != width || surface->pow2Height != height)
2359 surface->flags |= SFLAG_NONPOW2;
2360 else
2361 surface->flags &= ~SFLAG_NONPOW2;
2363 if ((surface->user_memory = mem))
2365 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
2366 valid_location = WINED3D_LOCATION_USER_MEMORY;
2368 surface->pitch = pitch;
2369 surface->resource.format = texture_resource->format;
2370 surface->resource.multisample_type = texture_resource->multisample_type;
2371 surface->resource.multisample_quality = texture_resource->multisample_quality;
2372 if (surface->pitch)
2373 surface->resource.size = height * surface->pitch;
2374 else
2375 surface->resource.size = wined3d_format_calculate_size(texture_resource->format,
2376 texture_resource->device->surface_alignment, width, height, 1);
2378 /* The format might be changed to a format that needs conversion.
2379 * If the surface didn't use PBOs previously but could now, don't
2380 * change it - whatever made us not use PBOs might come back, e.g.
2381 * color keys. */
2382 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2383 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2385 if (create_dib)
2387 if (FAILED(hr = surface_create_dib_section(surface)))
2389 ERR("Failed to create dib section, hr %#x.\n", hr);
2390 return hr;
2392 if (!valid_location)
2393 valid_location = WINED3D_LOCATION_DIB;
2396 if (!valid_location)
2398 surface_prepare_system_memory(surface);
2399 valid_location = WINED3D_LOCATION_SYSMEM;
2402 surface_validate_location(surface, valid_location);
2404 return WINED3D_OK;
2407 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2408 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2410 unsigned short *dst_s;
2411 const float *src_f;
2412 unsigned int x, y;
2414 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2416 for (y = 0; y < h; ++y)
2418 src_f = (const float *)(src + y * pitch_in);
2419 dst_s = (unsigned short *) (dst + y * pitch_out);
2420 for (x = 0; x < w; ++x)
2422 dst_s[x] = float_32_to_16(src_f + x);
2427 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2428 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2430 static const unsigned char convert_5to8[] =
2432 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2433 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2434 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2435 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2437 static const unsigned char convert_6to8[] =
2439 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2440 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2441 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2442 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2443 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2444 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2445 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2446 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2448 unsigned int x, y;
2450 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2452 for (y = 0; y < h; ++y)
2454 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2455 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2456 for (x = 0; x < w; ++x)
2458 WORD pixel = src_line[x];
2459 dst_line[x] = 0xff000000
2460 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2461 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2462 | convert_5to8[(pixel & 0x001f)];
2467 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2468 * in both cases we're just setting the X / Alpha channel to 0xff. */
2469 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2470 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2472 unsigned int x, y;
2474 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2476 for (y = 0; y < h; ++y)
2478 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2479 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2481 for (x = 0; x < w; ++x)
2483 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2488 static inline BYTE cliptobyte(int x)
2490 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2493 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2494 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2496 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2497 unsigned int x, y;
2499 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2501 for (y = 0; y < h; ++y)
2503 const BYTE *src_line = src + y * pitch_in;
2504 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2505 for (x = 0; x < w; ++x)
2507 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2508 * C = Y - 16; D = U - 128; E = V - 128;
2509 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2510 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2511 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2512 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2513 * U and V are shared between the pixels. */
2514 if (!(x & 1)) /* For every even pixel, read new U and V. */
2516 d = (int) src_line[1] - 128;
2517 e = (int) src_line[3] - 128;
2518 r2 = 409 * e + 128;
2519 g2 = - 100 * d - 208 * e + 128;
2520 b2 = 516 * d + 128;
2522 c2 = 298 * ((int) src_line[0] - 16);
2523 dst_line[x] = 0xff000000
2524 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2525 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2526 | cliptobyte((c2 + b2) >> 8); /* blue */
2527 /* Scale RGB values to 0..255 range,
2528 * then clip them if still not in range (may be negative),
2529 * then shift them within DWORD if necessary. */
2530 src_line += 2;
2535 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2536 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2538 unsigned int x, y;
2539 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2541 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2543 for (y = 0; y < h; ++y)
2545 const BYTE *src_line = src + y * pitch_in;
2546 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2547 for (x = 0; x < w; ++x)
2549 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2550 * C = Y - 16; D = U - 128; E = V - 128;
2551 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2552 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2553 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2554 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2555 * U and V are shared between the pixels. */
2556 if (!(x & 1)) /* For every even pixel, read new U and V. */
2558 d = (int) src_line[1] - 128;
2559 e = (int) src_line[3] - 128;
2560 r2 = 409 * e + 128;
2561 g2 = - 100 * d - 208 * e + 128;
2562 b2 = 516 * d + 128;
2564 c2 = 298 * ((int) src_line[0] - 16);
2565 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2566 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2567 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2568 /* Scale RGB values to 0..255 range,
2569 * then clip them if still not in range (may be negative),
2570 * then shift them within DWORD if necessary. */
2571 src_line += 2;
2576 struct d3dfmt_converter_desc
2578 enum wined3d_format_id from, to;
2579 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2582 static const struct d3dfmt_converter_desc converters[] =
2584 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2585 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2586 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2587 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2588 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2589 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2592 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2593 enum wined3d_format_id to)
2595 unsigned int i;
2597 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2599 if (converters[i].from == from && converters[i].to == to)
2600 return &converters[i];
2603 return NULL;
2606 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2608 struct wined3d_map_desc src_map, dst_map;
2609 const struct d3dfmt_converter_desc *conv;
2610 struct wined3d_texture *ret = NULL;
2611 struct wined3d_resource_desc desc;
2612 struct wined3d_surface *dst;
2614 conv = find_converter(source->resource.format->id, to_fmt);
2615 if (!conv)
2617 FIXME("Cannot find a conversion function from format %s to %s.\n",
2618 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2619 return NULL;
2622 /* FIXME: Multisampled conversion? */
2623 wined3d_resource_get_desc(&source->resource, &desc);
2624 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2625 desc.format = to_fmt;
2626 desc.usage = 0;
2627 desc.pool = WINED3D_POOL_SCRATCH;
2628 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2629 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, &wined3d_null_parent_ops, &ret)))
2631 ERR("Failed to create a destination surface for conversion.\n");
2632 return NULL;
2634 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2636 memset(&src_map, 0, sizeof(src_map));
2637 memset(&dst_map, 0, sizeof(dst_map));
2639 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2641 ERR("Failed to lock the source surface.\n");
2642 wined3d_texture_decref(ret);
2643 return NULL;
2645 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2647 ERR("Failed to lock the destination surface.\n");
2648 wined3d_surface_unmap(source);
2649 wined3d_texture_decref(ret);
2650 return NULL;
2653 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2654 source->resource.width, source->resource.height);
2656 wined3d_surface_unmap(dst);
2657 wined3d_surface_unmap(source);
2659 return ret;
2662 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2663 unsigned int bpp, UINT pitch, DWORD color)
2665 BYTE *first;
2666 unsigned int x, y;
2668 /* Do first row */
2670 #define COLORFILL_ROW(type) \
2671 do { \
2672 type *d = (type *)buf; \
2673 for (x = 0; x < width; ++x) \
2674 d[x] = (type)color; \
2675 } while(0)
2677 switch (bpp)
2679 case 1:
2680 COLORFILL_ROW(BYTE);
2681 break;
2683 case 2:
2684 COLORFILL_ROW(WORD);
2685 break;
2687 case 3:
2689 BYTE *d = buf;
2690 for (x = 0; x < width; ++x, d += 3)
2692 d[0] = (color ) & 0xff;
2693 d[1] = (color >> 8) & 0xff;
2694 d[2] = (color >> 16) & 0xff;
2696 break;
2698 case 4:
2699 COLORFILL_ROW(DWORD);
2700 break;
2702 default:
2703 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2704 return WINED3DERR_NOTAVAILABLE;
2707 #undef COLORFILL_ROW
2709 /* Now copy first row. */
2710 first = buf;
2711 for (y = 1; y < height; ++y)
2713 buf += pitch;
2714 memcpy(buf, first, width * bpp);
2717 return WINED3D_OK;
2720 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2722 return surface_from_resource(resource);
2725 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2727 TRACE("surface %p.\n", surface);
2729 if (!surface->resource.map_count)
2731 WARN("Trying to unmap unmapped surface.\n");
2732 return WINEDDERR_NOTLOCKED;
2734 --surface->resource.map_count;
2736 surface->surface_ops->surface_unmap(surface);
2738 return WINED3D_OK;
2741 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2742 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
2744 const struct wined3d_format *format = surface->resource.format;
2745 struct wined3d_device *device = surface->resource.device;
2746 struct wined3d_context *context;
2747 const struct wined3d_gl_info *gl_info;
2748 BYTE *base_memory;
2750 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
2751 surface, map_desc, wine_dbgstr_rect(rect), flags);
2753 if (surface->resource.map_count)
2755 WARN("Surface is already mapped.\n");
2756 return WINED3DERR_INVALIDCALL;
2759 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
2760 && !surface_check_block_align(surface, rect))
2762 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
2763 wine_dbgstr_rect(rect), format->block_width, format->block_height);
2765 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2766 return WINED3DERR_INVALIDCALL;
2769 ++surface->resource.map_count;
2771 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2772 WARN("Trying to lock unlockable surface.\n");
2774 /* Performance optimization: Count how often a surface is mapped, if it is
2775 * mapped regularly do not throw away the system memory copy. This avoids
2776 * the need to download the surface from OpenGL all the time. The surface
2777 * is still downloaded if the OpenGL texture is changed. */
2778 if (!(surface->flags & SFLAG_DYNLOCK) && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2780 if (++surface->lockCount > MAXLOCKCOUNT)
2782 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2783 surface->flags |= SFLAG_DYNLOCK;
2787 surface_prepare_map_memory(surface);
2788 if (flags & WINED3D_MAP_DISCARD)
2790 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2791 wined3d_debug_location(surface->resource.map_binding));
2792 surface_validate_location(surface, surface->resource.map_binding);
2794 else
2796 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2797 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2799 surface_load_location(surface, surface->resource.map_binding);
2802 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2803 surface_invalidate_location(surface, ~surface->resource.map_binding);
2805 switch (surface->resource.map_binding)
2807 case WINED3D_LOCATION_SYSMEM:
2808 base_memory = surface->resource.heap_memory;
2809 break;
2811 case WINED3D_LOCATION_USER_MEMORY:
2812 base_memory = surface->user_memory;
2813 break;
2815 case WINED3D_LOCATION_DIB:
2816 base_memory = surface->dib.bitmap_data;
2817 break;
2819 case WINED3D_LOCATION_BUFFER:
2820 context = context_acquire(device, NULL);
2821 gl_info = context->gl_info;
2823 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
2824 base_memory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
2825 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2826 checkGLcall("map PBO");
2828 context_release(context);
2829 break;
2831 default:
2832 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2833 base_memory = NULL;
2836 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2837 map_desc->row_pitch = surface->resource.width * format->byte_count;
2838 else
2839 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2840 map_desc->slice_pitch = 0;
2842 if (!rect)
2844 map_desc->data = base_memory;
2845 surface->lockedRect.left = 0;
2846 surface->lockedRect.top = 0;
2847 surface->lockedRect.right = surface->resource.width;
2848 surface->lockedRect.bottom = surface->resource.height;
2850 else
2852 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2854 /* Compressed textures are block based, so calculate the offset of
2855 * the block that contains the top-left pixel of the locked rectangle. */
2856 map_desc->data = base_memory
2857 + ((rect->top / format->block_height) * map_desc->row_pitch)
2858 + ((rect->left / format->block_width) * format->block_byte_count);
2860 else
2862 map_desc->data = base_memory
2863 + (map_desc->row_pitch * rect->top)
2864 + (rect->left * format->byte_count);
2866 surface->lockedRect.left = rect->left;
2867 surface->lockedRect.top = rect->top;
2868 surface->lockedRect.right = rect->right;
2869 surface->lockedRect.bottom = rect->bottom;
2872 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2873 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2875 return WINED3D_OK;
2878 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
2880 HRESULT hr;
2882 TRACE("surface %p, dc %p.\n", surface, dc);
2884 /* Give more detailed info for ddraw. */
2885 if (surface->flags & SFLAG_DCINUSE)
2886 return WINEDDERR_DCALREADYCREATED;
2888 /* Can't GetDC if the surface is locked. */
2889 if (surface->resource.map_count)
2890 return WINED3DERR_INVALIDCALL;
2892 /* Create a DIB section if there isn't a dc yet. */
2893 if (!surface->hDC)
2895 if (surface->flags & SFLAG_CLIENT)
2897 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
2898 surface_release_client_storage(surface);
2900 hr = surface_create_dib_section(surface);
2901 if (FAILED(hr))
2902 return WINED3DERR_INVALIDCALL;
2903 if (!(surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2904 || surface->flags & SFLAG_PIN_SYSMEM
2905 || surface->pbo))
2906 surface->resource.map_binding = WINED3D_LOCATION_DIB;
2909 surface_load_location(surface, WINED3D_LOCATION_DIB);
2910 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
2912 surface->flags |= SFLAG_DCINUSE;
2913 surface->resource.map_count++;
2915 *dc = surface->hDC;
2916 TRACE("Returning dc %p.\n", *dc);
2918 return WINED3D_OK;
2921 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
2923 TRACE("surface %p, dc %p.\n", surface, dc);
2925 if (!(surface->flags & SFLAG_DCINUSE))
2926 return WINEDDERR_NODC;
2928 if (surface->hDC != dc)
2930 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
2931 dc, surface->hDC);
2932 return WINEDDERR_NODC;
2935 surface->resource.map_count--;
2936 surface->flags &= ~SFLAG_DCINUSE;
2938 if (surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY || (surface->flags & SFLAG_PIN_SYSMEM
2939 && surface->resource.map_binding != WINED3D_LOCATION_DIB))
2941 /* The game Salammbo modifies the surface contents without mapping the surface between
2942 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
2943 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
2944 * Do not only copy the DIB to the map location, but also make sure the map location is
2945 * copied back to the DIB in the next getdc call.
2947 * The same consideration applies to user memory surfaces. */
2948 surface_load_location(surface, surface->resource.map_binding);
2949 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
2952 return WINED3D_OK;
2955 static void read_from_framebuffer(struct wined3d_surface *surface, DWORD dst_location)
2957 struct wined3d_device *device = surface->resource.device;
2958 const struct wined3d_gl_info *gl_info;
2959 struct wined3d_context *context;
2960 BYTE *mem;
2961 BYTE *row, *top, *bottom;
2962 int i;
2963 BOOL srcIsUpsideDown;
2964 struct wined3d_bo_address data;
2966 surface_get_memory(surface, &data, dst_location);
2968 context = context_acquire(device, surface);
2969 context_apply_blit_state(context, device);
2970 gl_info = context->gl_info;
2972 /* Select the correct read buffer, and give some debug output.
2973 * There is no need to keep track of the current read buffer or reset it, every part of the code
2974 * that reads sets the read buffer as desired.
2976 if (wined3d_resource_is_offscreen(&surface->container->resource))
2978 /* Mapping the primary render target which is not on a swapchain.
2979 * Read from the back buffer. */
2980 TRACE("Mapping offscreen render target.\n");
2981 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2982 srcIsUpsideDown = TRUE;
2984 else
2986 /* Onscreen surfaces are always part of a swapchain */
2987 GLenum buffer = surface_get_gl_buffer(surface);
2988 TRACE("Mapping %#x buffer.\n", buffer);
2989 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2990 checkGLcall("glReadBuffer");
2991 srcIsUpsideDown = FALSE;
2994 if (data.buffer_object)
2996 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
2997 checkGLcall("glBindBufferARB");
3000 /* Setup pixel store pack state -- to glReadPixels into the correct place */
3001 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
3002 checkGLcall("glPixelStorei");
3004 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
3005 surface->resource.width, surface->resource.height,
3006 surface->resource.format->glFormat,
3007 surface->resource.format->glType, data.addr);
3008 checkGLcall("glReadPixels");
3010 /* Reset previous pixel store pack state */
3011 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
3012 checkGLcall("glPixelStorei");
3014 if (!srcIsUpsideDown)
3016 /* glReadPixels returns the image upside down, and there is no way to prevent this.
3017 * Flip the lines in software. */
3018 UINT pitch = wined3d_surface_get_pitch(surface);
3020 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
3021 goto error;
3023 if (data.buffer_object)
3025 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3026 checkGLcall("glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB)");
3028 else
3029 mem = data.addr;
3031 top = mem;
3032 bottom = mem + pitch * (surface->resource.height - 1);
3033 for (i = 0; i < surface->resource.height / 2; i++)
3035 memcpy(row, top, pitch);
3036 memcpy(top, bottom, pitch);
3037 memcpy(bottom, row, pitch);
3038 top += pitch;
3039 bottom -= pitch;
3041 HeapFree(GetProcessHeap(), 0, row);
3043 if (data.buffer_object)
3044 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB));
3047 error:
3048 if (data.buffer_object)
3050 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
3051 checkGLcall("glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0)");
3054 context_release(context);
3057 /* Read the framebuffer contents into a texture. Note that this function
3058 * doesn't do any kind of flipping. Using this on an onscreen surface will
3059 * result in a flipped D3D texture. */
3060 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
3062 struct wined3d_device *device = surface->resource.device;
3063 const struct wined3d_gl_info *gl_info;
3064 struct wined3d_context *context;
3066 context = context_acquire(device, surface);
3067 gl_info = context->gl_info;
3068 device_invalidate_state(device, STATE_FRAMEBUFFER);
3070 surface_prepare_texture(surface, context, srgb);
3071 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3073 TRACE("Reading back offscreen render target %p.\n", surface);
3075 if (wined3d_resource_is_offscreen(&surface->container->resource))
3076 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3077 else
3078 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
3079 checkGLcall("glReadBuffer");
3081 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
3082 0, 0, 0, 0, surface->resource.width, surface->resource.height);
3083 checkGLcall("glCopyTexSubImage2D");
3085 context_release(context);
3088 /* Context activation is done by the caller. */
3089 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
3090 struct wined3d_context *context, BOOL srgb)
3092 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
3093 enum wined3d_conversion_type convert;
3094 struct wined3d_format format;
3096 if (surface->flags & alloc_flag) return;
3098 d3dfmt_get_conv(surface->container, TRUE, &format, &convert);
3099 if (convert != WINED3D_CT_NONE || format.convert)
3100 surface->flags |= SFLAG_CONVERTED;
3101 else surface->flags &= ~SFLAG_CONVERTED;
3103 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3104 surface_allocate_surface(surface, context->gl_info, &format, srgb);
3105 surface->flags |= alloc_flag;
3108 /* Context activation is done by the caller. */
3109 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
3111 struct wined3d_texture *texture = surface->container;
3112 UINT sub_count = texture->level_count * texture->layer_count;
3113 UINT i;
3115 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
3117 for (i = 0; i < sub_count; ++i)
3119 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
3120 surface_prepare_texture_internal(s, context, srgb);
3123 return;
3126 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
3128 if (multisample)
3130 if (surface->rb_multisample)
3131 return;
3133 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
3134 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
3135 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
3136 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
3137 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
3139 else
3141 if (surface->rb_resolved)
3142 return;
3144 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
3145 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
3146 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
3147 surface->pow2Width, surface->pow2Height);
3148 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
3152 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
3154 /* FIXME: Is this really how color keys are supposed to work? I think it
3155 * makes more sense to compare the individual channels. */
3156 return color >= color_key->color_space_low_value
3157 && color <= color_key->color_space_high_value;
3160 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
3161 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
3163 const BYTE *source;
3164 unsigned int x, y;
3165 BYTE *dest;
3167 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
3168 src, dst, pitch, width, height, outpitch, conversion_type, surface);
3170 switch (conversion_type)
3172 case WINED3D_CT_P8:
3173 if (surface->container->swapchain && surface->container->swapchain->palette)
3175 const struct wined3d_palette *palette = surface->container->swapchain->palette;
3176 for (y = 0; y < height; y++)
3178 source = src + pitch * y;
3179 dest = dst + outpitch * y;
3180 for (x = 0; x < width; x++)
3182 BYTE color = *source++;
3183 *dest++ = palette->colors[color].rgbBlue;
3184 *dest++ = palette->colors[color].rgbGreen;
3185 *dest++ = palette->colors[color].rgbRed;
3186 *dest++ = 0;
3190 else
3192 /* This should probably use the system palette, but unless
3193 * the X server is running in P8 mode there is no such thing.
3194 * The probably best solution is to set the fixed 20 colors
3195 * from the default windows palette and set the rest to black,
3196 * white, or some ugly pink. For now use black for the entire
3197 * palette. Don't use pink everywhere. Age of Empires 2 draws
3198 * a front buffer filled with zeroes without a palette when
3199 * starting and we don't want the screen to flash in an ugly
3200 * color. */
3201 FIXME("P8 surface loaded without a palette.\n");
3202 for (y = 0; y < height; ++y)
3204 memset(&dst[outpitch * y], 0, width * 4);
3207 break;
3209 case WINED3D_CT_CK_B5G6R5:
3210 for (y = 0; y < height; ++y)
3212 source = src + pitch * y;
3213 dest = dst + outpitch * y;
3214 for (x = 0; x < width; ++x)
3216 WORD color = *(const WORD *)source;
3217 if (!color_in_range(&surface->container->src_blt_color_key, color))
3218 *(WORD *)dest = 0x8000 | ((color & 0xffc0) >> 1) | (color & 0x1f);
3219 else
3220 *(WORD *)dest = ((color & 0xffc0) >> 1) | (color & 0x1f);
3221 source += 2;
3222 dest += 2;
3225 break;
3227 case WINED3D_CT_CK_B5G5R5X1:
3229 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
3230 const WORD *Source;
3231 WORD *Dest;
3232 TRACE("Color keyed 5551\n");
3233 for (y = 0; y < height; y++) {
3234 Source = (const WORD *)(src + y * pitch);
3235 Dest = (WORD *) (dst + y * outpitch);
3236 for (x = 0; x < width; x++ ) {
3237 WORD color = *Source++;
3238 *Dest = color;
3239 if (!color_in_range(&surface->container->src_blt_color_key, color))
3240 *Dest |= (1 << 15);
3241 else
3242 *Dest &= ~(1 << 15);
3243 Dest++;
3247 break;
3249 case WINED3D_CT_CK_B8G8R8:
3250 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
3251 for (y = 0; y < height; y++)
3253 source = src + pitch * y;
3254 dest = dst + outpitch * y;
3255 for (x = 0; x < width; x++) {
3256 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
3257 DWORD dstcolor = color << 8;
3258 if (!color_in_range(&surface->container->src_blt_color_key, color))
3259 dstcolor |= 0xff;
3260 *(DWORD*)dest = dstcolor;
3261 source += 3;
3262 dest += 4;
3265 break;
3267 case WINED3D_CT_CK_B8G8R8X8:
3268 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
3269 for (y = 0; y < height; y++)
3271 source = src + pitch * y;
3272 dest = dst + outpitch * y;
3273 for (x = 0; x < width; x++) {
3274 DWORD color = 0xffffff & *(const DWORD*)source;
3275 DWORD dstcolor = color << 8;
3276 if (!color_in_range(&surface->container->src_blt_color_key, color))
3277 dstcolor |= 0xff;
3278 *(DWORD*)dest = dstcolor;
3279 source += 4;
3280 dest += 4;
3283 break;
3285 case WINED3D_CT_CK_B8G8R8A8:
3286 for (y = 0; y < height; ++y)
3288 source = src + pitch * y;
3289 dest = dst + outpitch * y;
3290 for (x = 0; x < width; ++x)
3292 DWORD color = *(const DWORD *)source;
3293 if (color_in_range(&surface->container->src_blt_color_key, color))
3294 color &= ~0xff000000;
3295 *(DWORD*)dest = color;
3296 source += 4;
3297 dest += 4;
3300 break;
3302 default:
3303 ERR("Unsupported conversion type %#x.\n", conversion_type);
3305 return WINED3D_OK;
3308 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
3310 if (front->container->level_count != 1 || front->container->layer_count != 1
3311 || back->container->level_count != 1 || back->container->layer_count != 1)
3312 ERR("Flip between surfaces %p and %p not supported.\n", front, back);
3314 /* Flip the surface contents */
3315 /* Flip the DC */
3317 HDC tmp;
3318 tmp = front->hDC;
3319 front->hDC = back->hDC;
3320 back->hDC = tmp;
3323 /* Flip the DIBsection */
3325 HBITMAP tmp = front->dib.DIBsection;
3326 front->dib.DIBsection = back->dib.DIBsection;
3327 back->dib.DIBsection = tmp;
3330 /* Flip the surface data */
3332 void* tmp;
3334 tmp = front->dib.bitmap_data;
3335 front->dib.bitmap_data = back->dib.bitmap_data;
3336 back->dib.bitmap_data = tmp;
3338 tmp = front->resource.heap_memory;
3339 front->resource.heap_memory = back->resource.heap_memory;
3340 back->resource.heap_memory = tmp;
3343 /* Flip the PBO */
3345 GLuint tmp_pbo = front->pbo;
3346 front->pbo = back->pbo;
3347 back->pbo = tmp_pbo;
3350 /* Flip the opengl texture */
3352 GLuint tmp;
3354 tmp = back->container->texture_rgb.name;
3355 back->container->texture_rgb.name = front->container->texture_rgb.name;
3356 front->container->texture_rgb.name = tmp;
3358 tmp = back->container->texture_srgb.name;
3359 back->container->texture_srgb.name = front->container->texture_srgb.name;
3360 front->container->texture_srgb.name = tmp;
3362 tmp = back->rb_multisample;
3363 back->rb_multisample = front->rb_multisample;
3364 front->rb_multisample = tmp;
3366 tmp = back->rb_resolved;
3367 back->rb_resolved = front->rb_resolved;
3368 front->rb_resolved = tmp;
3370 resource_unload(&back->resource);
3371 resource_unload(&front->resource);
3375 DWORD tmp_flags = back->flags;
3376 back->flags = front->flags;
3377 front->flags = tmp_flags;
3379 tmp_flags = back->locations;
3380 back->locations = front->locations;
3381 front->locations = tmp_flags;
3385 /* Does a direct frame buffer -> texture copy. Stretching is done with single
3386 * pixel copy calls. */
3387 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3388 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3390 struct wined3d_device *device = dst_surface->resource.device;
3391 const struct wined3d_gl_info *gl_info;
3392 float xrel, yrel;
3393 struct wined3d_context *context;
3394 BOOL upsidedown = FALSE;
3395 RECT dst_rect = *dst_rect_in;
3397 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3398 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3400 if(dst_rect.top > dst_rect.bottom) {
3401 UINT tmp = dst_rect.bottom;
3402 dst_rect.bottom = dst_rect.top;
3403 dst_rect.top = tmp;
3404 upsidedown = TRUE;
3407 context = context_acquire(device, src_surface);
3408 gl_info = context->gl_info;
3409 context_apply_blit_state(context, device);
3410 wined3d_texture_load(dst_surface->container, context, FALSE);
3412 /* Bind the target texture */
3413 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
3414 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
3416 TRACE("Reading from an offscreen target\n");
3417 upsidedown = !upsidedown;
3418 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3420 else
3422 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3424 checkGLcall("glReadBuffer");
3426 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3427 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3429 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3431 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3433 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3434 ERR("Texture filtering not supported in direct blit.\n");
3436 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3437 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3439 ERR("Texture filtering not supported in direct blit\n");
3442 if (upsidedown
3443 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3444 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3446 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
3447 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3448 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3449 src_rect->left, src_surface->resource.height - src_rect->bottom,
3450 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3452 else
3454 LONG row;
3455 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
3456 /* I have to process this row by row to swap the image,
3457 * otherwise it would be upside down, so stretching in y direction
3458 * doesn't cost extra time
3460 * However, stretching in x direction can be avoided if not necessary
3462 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3463 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3465 /* Well, that stuff works, but it's very slow.
3466 * find a better way instead
3468 LONG col;
3470 for (col = dst_rect.left; col < dst_rect.right; ++col)
3472 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3473 dst_rect.left + col /* x offset */, row /* y offset */,
3474 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3477 else
3479 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3480 dst_rect.left /* x offset */, row /* y offset */,
3481 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3485 checkGLcall("glCopyTexSubImage2D");
3487 context_release(context);
3489 /* The texture is now most up to date - If the surface is a render target
3490 * and has a drawable, this path is never entered. */
3491 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3492 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3495 /* Uses the hardware to stretch and flip the image */
3496 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3497 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3499 struct wined3d_device *device = dst_surface->resource.device;
3500 GLuint src, backup = 0;
3501 float left, right, top, bottom; /* Texture coordinates */
3502 UINT fbwidth = src_surface->resource.width;
3503 UINT fbheight = src_surface->resource.height;
3504 const struct wined3d_gl_info *gl_info;
3505 struct wined3d_context *context;
3506 GLenum drawBuffer = GL_BACK;
3507 GLenum texture_target;
3508 BOOL noBackBufferBackup;
3509 BOOL src_offscreen;
3510 BOOL upsidedown = FALSE;
3511 RECT dst_rect = *dst_rect_in;
3513 TRACE("Using hwstretch blit\n");
3514 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3515 context = context_acquire(device, src_surface);
3516 gl_info = context->gl_info;
3517 context_apply_blit_state(context, device);
3518 wined3d_texture_load(dst_surface->container, context, FALSE);
3520 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
3521 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3522 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3524 /* Get it a description */
3525 wined3d_texture_load(src_surface->container, context, FALSE);
3528 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3529 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3531 if (context->aux_buffers >= 2)
3533 /* Got more than one aux buffer? Use the 2nd aux buffer */
3534 drawBuffer = GL_AUX1;
3536 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3538 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3539 drawBuffer = GL_AUX0;
3542 if (noBackBufferBackup)
3544 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3545 checkGLcall("glGenTextures");
3546 context_bind_texture(context, GL_TEXTURE_2D, backup);
3547 texture_target = GL_TEXTURE_2D;
3549 else
3551 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3552 * we are reading from the back buffer, the backup can be used as source texture
3554 texture_target = src_surface->texture_target;
3555 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3556 gl_info->gl_ops.gl.p_glEnable(texture_target);
3557 checkGLcall("glEnable(texture_target)");
3559 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3560 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3563 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3564 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3566 if(dst_rect.top > dst_rect.bottom) {
3567 UINT tmp = dst_rect.bottom;
3568 dst_rect.bottom = dst_rect.top;
3569 dst_rect.top = tmp;
3570 upsidedown = TRUE;
3573 if (src_offscreen)
3575 TRACE("Reading from an offscreen target\n");
3576 upsidedown = !upsidedown;
3577 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3579 else
3581 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3584 /* TODO: Only back up the part that will be overwritten */
3585 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3587 checkGLcall("glCopyTexSubImage2D");
3589 /* No issue with overriding these - the sampler is dirty due to blit usage */
3590 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3591 wined3d_gl_mag_filter(magLookup, filter));
3592 checkGLcall("glTexParameteri");
3593 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3594 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
3595 checkGLcall("glTexParameteri");
3597 if (!src_surface->container->swapchain
3598 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
3600 src = backup ? backup : src_surface->container->texture_rgb.name;
3602 else
3604 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3605 checkGLcall("glReadBuffer(GL_FRONT)");
3607 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3608 checkGLcall("glGenTextures(1, &src)");
3609 context_bind_texture(context, GL_TEXTURE_2D, src);
3611 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3612 * out for power of 2 sizes
3614 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3615 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3616 checkGLcall("glTexImage2D");
3617 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3619 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3620 checkGLcall("glTexParameteri");
3621 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3622 checkGLcall("glTexParameteri");
3624 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3625 checkGLcall("glReadBuffer(GL_BACK)");
3627 if (texture_target != GL_TEXTURE_2D)
3629 gl_info->gl_ops.gl.p_glDisable(texture_target);
3630 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3631 texture_target = GL_TEXTURE_2D;
3634 checkGLcall("glEnd and previous");
3636 left = src_rect->left;
3637 right = src_rect->right;
3639 if (!upsidedown)
3641 top = src_surface->resource.height - src_rect->top;
3642 bottom = src_surface->resource.height - src_rect->bottom;
3644 else
3646 top = src_surface->resource.height - src_rect->bottom;
3647 bottom = src_surface->resource.height - src_rect->top;
3650 if (src_surface->flags & SFLAG_NORMCOORD)
3652 left /= src_surface->pow2Width;
3653 right /= src_surface->pow2Width;
3654 top /= src_surface->pow2Height;
3655 bottom /= src_surface->pow2Height;
3658 /* draw the source texture stretched and upside down. The correct surface is bound already */
3659 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3660 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3662 context_set_draw_buffer(context, drawBuffer);
3663 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3665 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3666 /* bottom left */
3667 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3668 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3670 /* top left */
3671 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3672 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3674 /* top right */
3675 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3676 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3678 /* bottom right */
3679 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3680 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3681 gl_info->gl_ops.gl.p_glEnd();
3682 checkGLcall("glEnd and previous");
3684 if (texture_target != dst_surface->texture_target)
3686 gl_info->gl_ops.gl.p_glDisable(texture_target);
3687 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3688 texture_target = dst_surface->texture_target;
3691 /* Now read the stretched and upside down image into the destination texture */
3692 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
3693 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
3695 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3696 0, 0, /* We blitted the image to the origin */
3697 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3698 checkGLcall("glCopyTexSubImage2D");
3700 if (drawBuffer == GL_BACK)
3702 /* Write the back buffer backup back. */
3703 if (backup)
3705 if (texture_target != GL_TEXTURE_2D)
3707 gl_info->gl_ops.gl.p_glDisable(texture_target);
3708 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3709 texture_target = GL_TEXTURE_2D;
3711 context_bind_texture(context, GL_TEXTURE_2D, backup);
3713 else
3715 if (texture_target != src_surface->texture_target)
3717 gl_info->gl_ops.gl.p_glDisable(texture_target);
3718 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3719 texture_target = src_surface->texture_target;
3721 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3724 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3725 /* top left */
3726 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3727 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3729 /* bottom left */
3730 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3731 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3733 /* bottom right */
3734 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3735 (float)fbheight / (float)src_surface->pow2Height);
3736 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3738 /* top right */
3739 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3740 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3741 gl_info->gl_ops.gl.p_glEnd();
3743 gl_info->gl_ops.gl.p_glDisable(texture_target);
3744 checkGLcall("glDisable(texture_target)");
3746 /* Cleanup */
3747 if (src != src_surface->container->texture_rgb.name && src != backup)
3749 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3750 checkGLcall("glDeleteTextures(1, &src)");
3752 if (backup)
3754 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3755 checkGLcall("glDeleteTextures(1, &backup)");
3758 if (wined3d_settings.strict_draw_ordering)
3759 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3761 context_release(context);
3763 /* The texture is now most up to date - If the surface is a render target
3764 * and has a drawable, this path is never entered. */
3765 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3766 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3769 /* Front buffer coordinates are always full screen coordinates, but our GL
3770 * drawable is limited to the window's client area. The sysmem and texture
3771 * copies do have the full screen size. Note that GL has a bottom-left
3772 * origin, while D3D has a top-left origin. */
3773 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3775 UINT drawable_height;
3777 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3779 POINT offset = {0, 0};
3780 RECT windowsize;
3782 ScreenToClient(window, &offset);
3783 OffsetRect(rect, offset.x, offset.y);
3785 GetClientRect(window, &windowsize);
3786 drawable_height = windowsize.bottom - windowsize.top;
3788 else
3790 drawable_height = surface->resource.height;
3793 rect->top = drawable_height - rect->top;
3794 rect->bottom = drawable_height - rect->bottom;
3797 static void surface_blt_to_drawable(const struct wined3d_device *device,
3798 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3799 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3800 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3802 const struct wined3d_gl_info *gl_info;
3803 struct wined3d_context *context;
3804 RECT src_rect, dst_rect;
3806 src_rect = *src_rect_in;
3807 dst_rect = *dst_rect_in;
3809 context = context_acquire(device, dst_surface);
3810 gl_info = context->gl_info;
3812 /* Make sure the surface is up-to-date. This should probably use
3813 * surface_load_location() and worry about the destination surface too,
3814 * unless we're overwriting it completely. */
3815 wined3d_texture_load(src_surface->container, context, FALSE);
3817 /* Activate the destination context, set it up for blitting */
3818 context_apply_blit_state(context, device);
3820 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3821 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3823 device->blitter->set_shader(device->blit_priv, context, src_surface);
3825 if (alpha_test)
3827 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3828 checkGLcall("glEnable(GL_ALPHA_TEST)");
3830 /* For P8 surfaces, the alpha component contains the palette index.
3831 * Which means that the colorkey is one of the palette entries. In
3832 * other cases pixels that should be masked away have alpha set to 0. */
3833 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3834 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3835 (float)src_surface->container->src_blt_color_key.color_space_low_value / 256.0f);
3836 else
3837 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3838 checkGLcall("glAlphaFunc");
3840 else
3842 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3843 checkGLcall("glDisable(GL_ALPHA_TEST)");
3846 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3848 if (alpha_test)
3850 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3851 checkGLcall("glDisable(GL_ALPHA_TEST)");
3854 /* Leave the opengl state valid for blitting */
3855 device->blitter->unset_shader(context->gl_info);
3857 if (wined3d_settings.strict_draw_ordering
3858 || (dst_surface->container->swapchain
3859 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3860 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3862 context_release(context);
3865 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3867 struct wined3d_device *device = s->resource.device;
3868 const struct blit_shader *blitter;
3870 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
3871 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
3872 if (!blitter)
3874 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3875 return WINED3DERR_INVALIDCALL;
3878 return blitter->color_fill(device, s, rect, color);
3881 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3882 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3883 enum wined3d_texture_filter_type filter)
3885 struct wined3d_device *device = dst_surface->resource.device;
3886 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3887 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3888 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3890 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3891 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3892 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3894 /* Get the swapchain. One of the surfaces has to be a primary surface */
3895 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3897 WARN("Destination is in sysmem, rejecting gl blt\n");
3898 return WINED3DERR_INVALIDCALL;
3901 dst_swapchain = dst_surface->container->swapchain;
3903 if (src_surface)
3905 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3907 WARN("Src is in sysmem, rejecting gl blt\n");
3908 return WINED3DERR_INVALIDCALL;
3911 src_swapchain = src_surface->container->swapchain;
3913 else
3915 src_swapchain = NULL;
3918 /* Early sort out of cases where no render target is used */
3919 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
3921 TRACE("No surface is render target, not using hardware blit.\n");
3922 return WINED3DERR_INVALIDCALL;
3925 /* No destination color keying supported */
3926 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
3928 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3929 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3930 return WINED3DERR_INVALIDCALL;
3933 if (dst_swapchain && dst_swapchain == src_swapchain)
3935 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3936 return WINED3DERR_INVALIDCALL;
3939 if (dst_swapchain && src_swapchain)
3941 FIXME("Implement hardware blit between two different swapchains\n");
3942 return WINED3DERR_INVALIDCALL;
3945 if (dst_swapchain)
3947 /* Handled with regular texture -> swapchain blit */
3948 if (src_surface == rt)
3949 TRACE("Blit from active render target to a swapchain\n");
3951 else if (src_swapchain && dst_surface == rt)
3953 FIXME("Implement blit from a swapchain to the active render target\n");
3954 return WINED3DERR_INVALIDCALL;
3957 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
3959 /* Blit from render target to texture */
3960 BOOL stretchx;
3962 /* P8 read back is not implemented */
3963 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
3964 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
3966 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3967 return WINED3DERR_INVALIDCALL;
3970 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3972 TRACE("Color keying not supported by frame buffer to texture blit\n");
3973 return WINED3DERR_INVALIDCALL;
3974 /* Destination color key is checked above */
3977 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
3978 stretchx = TRUE;
3979 else
3980 stretchx = FALSE;
3982 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3983 * flip the image nor scale it.
3985 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3986 * -> If the app wants an image width an unscaled width, copy it line per line
3987 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
3988 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3989 * back buffer. This is slower than reading line per line, thus not used for flipping
3990 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3991 * pixel by pixel. */
3992 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
3993 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
3995 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
3996 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
3998 else
4000 TRACE("Using hardware stretching to flip / stretch the texture.\n");
4001 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
4004 surface_evict_sysmem(dst_surface);
4006 return WINED3D_OK;
4008 else if (src_surface)
4010 /* Blit from offscreen surface to render target */
4011 struct wined3d_color_key old_blt_key = src_surface->container->src_blt_color_key;
4012 DWORD old_color_key_flags = src_surface->container->color_key_flags;
4014 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4016 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4017 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
4018 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
4020 FIXME("Unsupported blit operation falling back to software\n");
4021 return WINED3DERR_INVALIDCALL;
4024 /* Color keying: Check if we have to do a color keyed blt,
4025 * and if not check if a color key is activated.
4027 * Just modify the color keying parameters in the surface and restore them afterwards
4028 * The surface keeps track of the color key last used to load the opengl surface.
4029 * PreLoad will catch the change to the flags and color key and reload if necessary.
4031 if (flags & WINEDDBLT_KEYSRC)
4033 /* Use color key from surface */
4035 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4037 /* Use color key from DDBltFx */
4038 wined3d_texture_set_color_key(src_surface->container, WINEDDSD_CKSRCBLT, &DDBltFx->ddckSrcColorkey);
4040 else
4042 /* Do not use color key */
4043 wined3d_texture_set_color_key(src_surface->container, WINEDDSD_CKSRCBLT, NULL);
4046 surface_blt_to_drawable(device, filter,
4047 flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_ALPHATEST),
4048 src_surface, src_rect, dst_surface, dst_rect);
4050 /* Restore the color key parameters */
4051 wined3d_texture_set_color_key(src_surface->container, WINEDDSD_CKSRCBLT,
4052 (old_color_key_flags & WINEDDSD_CKSRCBLT) ? &old_blt_key : NULL);
4054 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
4055 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
4057 return WINED3D_OK;
4060 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
4061 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
4062 return WINED3DERR_INVALIDCALL;
4065 /* Context activation is done by the caller. */
4066 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
4067 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
4069 struct wined3d_device *device = surface->resource.device;
4070 const struct wined3d_gl_info *gl_info = context->gl_info;
4071 GLint compare_mode = GL_NONE;
4072 struct blt_info info;
4073 GLint old_binding = 0;
4074 RECT rect;
4076 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4078 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
4079 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
4080 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4081 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
4082 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
4083 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
4084 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
4085 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
4086 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4087 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
4088 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
4090 SetRect(&rect, 0, h, w, 0);
4091 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
4092 context_active_texture(context, context->gl_info, 0);
4093 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
4094 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
4095 if (gl_info->supported[ARB_SHADOW])
4097 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
4098 if (compare_mode != GL_NONE)
4099 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
4102 device->shader_backend->shader_select_depth_blt(device->shader_priv,
4103 gl_info, info.tex_type, &surface->ds_current_size);
4105 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
4106 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
4107 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
4108 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
4109 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
4110 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
4111 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
4112 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
4113 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
4114 gl_info->gl_ops.gl.p_glEnd();
4116 if (compare_mode != GL_NONE)
4117 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
4118 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
4120 gl_info->gl_ops.gl.p_glPopAttrib();
4122 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
4125 void surface_modify_ds_location(struct wined3d_surface *surface,
4126 DWORD location, UINT w, UINT h)
4128 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
4130 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
4131 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
4132 wined3d_texture_set_dirty(surface->container);
4134 surface->ds_current_size.cx = w;
4135 surface->ds_current_size.cy = h;
4136 surface->locations = location;
4139 /* Context activation is done by the caller. */
4140 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4142 const struct wined3d_gl_info *gl_info = context->gl_info;
4143 struct wined3d_device *device = surface->resource.device;
4144 GLsizei w, h;
4146 TRACE("surface %p, new location %#x.\n", surface, location);
4148 /* TODO: Make this work for modes other than FBO */
4149 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4151 if (!(surface->locations & location))
4153 w = surface->ds_current_size.cx;
4154 h = surface->ds_current_size.cy;
4155 surface->ds_current_size.cx = 0;
4156 surface->ds_current_size.cy = 0;
4158 else
4160 w = surface->resource.width;
4161 h = surface->resource.height;
4164 if (surface->ds_current_size.cx == surface->resource.width
4165 && surface->ds_current_size.cy == surface->resource.height)
4167 TRACE("Location (%#x) is already up to date.\n", location);
4168 return;
4171 if (surface->current_renderbuffer)
4173 FIXME("Not supported with fixed up depth stencil.\n");
4174 return;
4177 if (surface->locations & WINED3D_LOCATION_DISCARDED)
4179 TRACE("Surface was discarded, no need copy data.\n");
4180 switch (location)
4182 case WINED3D_LOCATION_TEXTURE_RGB:
4183 surface_prepare_texture(surface, context, FALSE);
4184 break;
4185 case WINED3D_LOCATION_RB_MULTISAMPLE:
4186 surface_prepare_rb(surface, gl_info, TRUE);
4187 break;
4188 case WINED3D_LOCATION_DRAWABLE:
4189 /* Nothing to do */
4190 break;
4191 default:
4192 FIXME("Unhandled location %#x\n", location);
4194 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
4195 surface->locations |= location;
4196 surface->ds_current_size.cx = surface->resource.width;
4197 surface->ds_current_size.cy = surface->resource.height;
4198 return;
4201 if (!surface->locations)
4203 FIXME("No up to date depth stencil location.\n");
4204 surface->locations |= location;
4205 surface->ds_current_size.cx = surface->resource.width;
4206 surface->ds_current_size.cy = surface->resource.height;
4207 return;
4210 if (location == WINED3D_LOCATION_TEXTURE_RGB)
4212 GLint old_binding = 0;
4213 GLenum bind_target;
4215 /* The render target is allowed to be smaller than the depth/stencil
4216 * buffer, so the onscreen depth/stencil buffer is potentially smaller
4217 * than the offscreen surface. Don't overwrite the offscreen surface
4218 * with undefined data. */
4219 w = min(w, context->swapchain->desc.backbuffer_width);
4220 h = min(h, context->swapchain->desc.backbuffer_height);
4222 TRACE("Copying onscreen depth buffer to depth texture.\n");
4224 if (!device->depth_blt_texture)
4225 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
4227 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4228 * directly on the FBO texture. That's because we need to flip. */
4229 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4230 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
4231 NULL, WINED3D_LOCATION_DRAWABLE);
4232 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4234 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4235 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4237 else
4239 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4240 bind_target = GL_TEXTURE_2D;
4242 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
4243 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
4244 * internal format, because the internal format might include stencil
4245 * data. In principle we should copy stencil data as well, but unless
4246 * the driver supports stencil export it's hard to do, and doesn't
4247 * seem to be needed in practice. If the hardware doesn't support
4248 * writing stencil data, the glCopyTexImage2D() call might trigger
4249 * software fallbacks. */
4250 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
4251 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4252 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4253 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4254 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4255 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4256 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4257 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
4259 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4260 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
4261 context_set_draw_buffer(context, GL_NONE);
4263 /* Do the actual blit */
4264 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
4265 checkGLcall("depth_blt");
4267 context_invalidate_state(context, STATE_FRAMEBUFFER);
4269 if (wined3d_settings.strict_draw_ordering)
4270 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4272 else if (location == WINED3D_LOCATION_DRAWABLE)
4274 TRACE("Copying depth texture to onscreen depth buffer.\n");
4276 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4277 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
4278 NULL, WINED3D_LOCATION_DRAWABLE);
4279 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
4280 0, surface->pow2Height - h, w, h, surface->texture_target);
4281 checkGLcall("depth_blt");
4283 context_invalidate_state(context, STATE_FRAMEBUFFER);
4285 if (wined3d_settings.strict_draw_ordering)
4286 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4288 else
4290 ERR("Invalid location (%#x) specified.\n", location);
4293 surface->locations |= location;
4294 surface->ds_current_size.cx = surface->resource.width;
4295 surface->ds_current_size.cy = surface->resource.height;
4298 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
4300 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4302 surface->locations |= location;
4305 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
4307 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4309 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4310 wined3d_texture_set_dirty(surface->container);
4311 surface->locations &= ~location;
4313 if (!surface->locations)
4314 ERR("Surface %p does not have any up to date location.\n", surface);
4317 static DWORD resource_access_from_location(DWORD location)
4319 switch (location)
4321 case WINED3D_LOCATION_SYSMEM:
4322 case WINED3D_LOCATION_USER_MEMORY:
4323 case WINED3D_LOCATION_DIB:
4324 case WINED3D_LOCATION_BUFFER:
4325 return WINED3D_RESOURCE_ACCESS_CPU;
4327 case WINED3D_LOCATION_DRAWABLE:
4328 case WINED3D_LOCATION_TEXTURE_SRGB:
4329 case WINED3D_LOCATION_TEXTURE_RGB:
4330 case WINED3D_LOCATION_RB_MULTISAMPLE:
4331 case WINED3D_LOCATION_RB_RESOLVED:
4332 return WINED3D_RESOURCE_ACCESS_GPU;
4334 default:
4335 FIXME("Unhandled location %#x.\n", location);
4336 return 0;
4340 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
4342 struct wined3d_device *device = surface->resource.device;
4343 struct wined3d_context *context;
4344 const struct wined3d_gl_info *gl_info;
4345 struct wined3d_bo_address dst, src;
4346 UINT size = surface->resource.size;
4348 surface_get_memory(surface, &dst, location);
4349 surface_get_memory(surface, &src, surface->locations);
4351 if (dst.buffer_object)
4353 context = context_acquire(device, NULL);
4354 gl_info = context->gl_info;
4355 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, dst.buffer_object));
4356 GL_EXTCALL(glBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, size, src.addr));
4357 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4358 checkGLcall("Upload PBO");
4359 context_release(context);
4360 return;
4362 if (src.buffer_object)
4364 context = context_acquire(device, NULL);
4365 gl_info = context->gl_info;
4366 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, src.buffer_object));
4367 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_PACK_BUFFER_ARB, 0, size, dst.addr));
4368 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4369 checkGLcall("Download PBO");
4370 context_release(context);
4371 return;
4373 memcpy(dst.addr, src.addr, size);
4376 static void surface_load_sysmem(struct wined3d_surface *surface,
4377 const struct wined3d_gl_info *gl_info, DWORD dst_location)
4379 if (surface->locations & surface_simple_locations)
4381 surface_copy_simple_location(surface, dst_location);
4382 return;
4385 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
4386 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4388 /* Download the surface to system memory. */
4389 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4391 struct wined3d_device *device = surface->resource.device;
4392 struct wined3d_context *context;
4394 /* TODO: Use already acquired context when possible. */
4395 context = context_acquire(device, NULL);
4397 wined3d_texture_bind_and_dirtify(surface->container, context,
4398 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
4399 surface_download_data(surface, gl_info, dst_location);
4401 context_release(context);
4403 return;
4406 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
4408 read_from_framebuffer(surface, dst_location);
4409 return;
4412 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
4413 surface, wined3d_debug_location(surface->locations));
4416 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
4417 const struct wined3d_gl_info *gl_info)
4419 RECT r;
4421 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
4422 && wined3d_resource_is_offscreen(&surface->container->resource))
4424 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
4425 return WINED3DERR_INVALIDCALL;
4428 surface_get_rect(surface, NULL, &r);
4429 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4430 surface_blt_to_drawable(surface->resource.device,
4431 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
4433 return WINED3D_OK;
4436 static HRESULT surface_load_texture(struct wined3d_surface *surface,
4437 const struct wined3d_gl_info *gl_info, BOOL srgb)
4439 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
4440 struct wined3d_device *device = surface->resource.device;
4441 enum wined3d_conversion_type convert;
4442 struct wined3d_context *context;
4443 UINT width, src_pitch, dst_pitch;
4444 struct wined3d_bo_address data;
4445 struct wined3d_format format;
4446 POINT dst_point = {0, 0};
4447 BYTE *mem = NULL;
4449 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
4450 && wined3d_resource_is_offscreen(&surface->container->resource)
4451 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
4453 surface_load_fb_texture(surface, srgb);
4455 return WINED3D_OK;
4458 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
4459 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
4460 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4461 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4462 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4464 if (srgb)
4465 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
4466 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
4467 else
4468 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
4469 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
4471 return WINED3D_OK;
4474 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
4475 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
4476 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4477 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4478 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4480 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
4481 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
4482 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
4483 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4485 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
4486 &rect, surface, dst_location, &rect);
4488 return WINED3D_OK;
4491 /* Upload from system memory */
4493 d3dfmt_get_conv(surface->container, TRUE /* We need color keying */, &format, &convert);
4495 if (srgb)
4497 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
4498 == WINED3D_LOCATION_TEXTURE_RGB)
4500 /* Performance warning... */
4501 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
4502 surface_prepare_map_memory(surface);
4503 surface_load_location(surface, surface->resource.map_binding);
4506 else
4508 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
4509 == WINED3D_LOCATION_TEXTURE_SRGB)
4511 /* Performance warning... */
4512 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
4513 surface_prepare_map_memory(surface);
4514 surface_load_location(surface, surface->resource.map_binding);
4518 if (!(surface->locations & surface_simple_locations))
4520 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
4521 /* Lets hope we get it from somewhere... */
4522 surface_prepare_system_memory(surface);
4523 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
4526 /* TODO: Use already acquired context when possible. */
4527 context = context_acquire(device, NULL);
4529 surface_prepare_texture(surface, context, srgb);
4530 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
4532 if (surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
4534 surface->flags |= SFLAG_GLCKEY;
4535 surface->gl_color_key = surface->container->src_blt_color_key;
4537 else surface->flags &= ~SFLAG_GLCKEY;
4539 width = surface->resource.width;
4540 src_pitch = wined3d_surface_get_pitch(surface);
4542 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
4543 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
4544 * called. */
4545 if ((convert != WINED3D_CT_NONE || format.convert) && surface->pbo)
4547 TRACE("Removing the pbo attached to surface %p.\n", surface);
4549 if (surface->flags & SFLAG_DIBSECTION)
4550 surface->resource.map_binding = WINED3D_LOCATION_DIB;
4551 else
4552 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
4554 surface_prepare_map_memory(surface);
4555 surface_load_location(surface, surface->resource.map_binding);
4556 surface_remove_pbo(surface, gl_info);
4559 surface_get_memory(surface, &data, surface->locations);
4560 if (format.convert)
4562 /* This code is entered for texture formats which need a fixup. */
4563 UINT height = surface->resource.height;
4565 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4566 dst_pitch = width * format.conv_byte_count;
4567 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4569 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4571 ERR("Out of memory (%u).\n", dst_pitch * height);
4572 context_release(context);
4573 return E_OUTOFMEMORY;
4575 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4576 dst_pitch, dst_pitch * height, width, height, 1);
4577 format.byte_count = format.conv_byte_count;
4578 src_pitch = dst_pitch;
4579 data.addr = mem;
4581 else if (convert != WINED3D_CT_NONE)
4583 /* This code is only entered for color keying fixups */
4584 UINT height = surface->resource.height;
4586 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4587 dst_pitch = width * format.conv_byte_count;
4588 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4590 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4592 ERR("Out of memory (%u).\n", dst_pitch * height);
4593 context_release(context);
4594 return E_OUTOFMEMORY;
4596 d3dfmt_convert_surface(data.addr, mem, src_pitch,
4597 width, height, dst_pitch, convert, surface);
4598 format.byte_count = format.conv_byte_count;
4599 src_pitch = dst_pitch;
4600 data.addr = mem;
4603 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
4605 context_release(context);
4607 HeapFree(GetProcessHeap(), 0, mem);
4609 return WINED3D_OK;
4612 static void surface_multisample_resolve(struct wined3d_surface *surface)
4614 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4616 if (!(surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE))
4617 ERR("Trying to resolve multisampled surface %p, but location WINED3D_LOCATION_RB_MULTISAMPLE not current.\n",
4618 surface);
4620 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
4621 surface, WINED3D_LOCATION_RB_MULTISAMPLE, &rect, surface, WINED3D_LOCATION_RB_RESOLVED, &rect);
4624 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
4626 struct wined3d_device *device = surface->resource.device;
4627 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4628 HRESULT hr;
4630 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4632 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4634 if (location == WINED3D_LOCATION_TEXTURE_RGB
4635 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4637 struct wined3d_context *context = context_acquire(device, NULL);
4638 surface_load_ds_location(surface, context, location);
4639 context_release(context);
4640 return WINED3D_OK;
4642 else if (location & surface->locations
4643 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
4645 /* Already up to date, nothing to do. */
4646 return WINED3D_OK;
4648 else
4650 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4651 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4652 return WINED3DERR_INVALIDCALL;
4656 if (surface->locations & location)
4658 TRACE("Location already up to date.\n");
4659 return WINED3D_OK;
4662 if (WARN_ON(d3d_surface))
4664 DWORD required_access = resource_access_from_location(location);
4665 if ((surface->resource.access_flags & required_access) != required_access)
4666 WARN("Operation requires %#x access, but surface only has %#x.\n",
4667 required_access, surface->resource.access_flags);
4670 if (!surface->locations)
4672 ERR("Surface %p does not have any up to date location.\n", surface);
4673 surface->flags |= SFLAG_LOST;
4674 return WINED3DERR_DEVICELOST;
4677 switch (location)
4679 case WINED3D_LOCATION_DIB:
4680 case WINED3D_LOCATION_USER_MEMORY:
4681 case WINED3D_LOCATION_SYSMEM:
4682 case WINED3D_LOCATION_BUFFER:
4683 surface_load_sysmem(surface, gl_info, location);
4684 break;
4686 case WINED3D_LOCATION_DRAWABLE:
4687 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
4688 return hr;
4689 break;
4691 case WINED3D_LOCATION_RB_RESOLVED:
4692 surface_multisample_resolve(surface);
4693 break;
4695 case WINED3D_LOCATION_TEXTURE_RGB:
4696 case WINED3D_LOCATION_TEXTURE_SRGB:
4697 if (FAILED(hr = surface_load_texture(surface, gl_info, location == WINED3D_LOCATION_TEXTURE_SRGB)))
4698 return hr;
4699 break;
4701 default:
4702 ERR("Don't know how to handle location %#x.\n", location);
4703 break;
4706 surface_validate_location(surface, location);
4708 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
4709 surface_evict_sysmem(surface);
4711 return WINED3D_OK;
4714 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
4715 /* Context activation is done by the caller. */
4716 static void ffp_blit_free(struct wined3d_device *device) { }
4718 /* Context activation is done by the caller. */
4719 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4721 const struct wined3d_gl_info *gl_info = context->gl_info;
4723 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
4724 checkGLcall("glEnable(target)");
4726 return WINED3D_OK;
4729 /* Context activation is done by the caller. */
4730 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
4732 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
4733 checkGLcall("glDisable(GL_TEXTURE_2D)");
4734 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4736 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4737 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4739 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4741 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
4742 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4746 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4747 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4748 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4750 switch (blit_op)
4752 case WINED3D_BLIT_OP_COLOR_BLIT:
4753 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4754 return FALSE;
4756 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4758 TRACE("Checking support for fixup:\n");
4759 dump_color_fixup_desc(src_format->color_fixup);
4762 /* We only support identity conversions. */
4763 if (!is_identity_fixup(src_format->color_fixup)
4764 || !is_identity_fixup(dst_format->color_fixup))
4766 TRACE("Fixups are not supported.\n");
4767 return FALSE;
4770 return TRUE;
4772 case WINED3D_BLIT_OP_COLOR_FILL:
4773 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
4774 return FALSE;
4776 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4778 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4779 return FALSE;
4781 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4783 TRACE("Color fill not supported\n");
4784 return FALSE;
4787 /* FIXME: We should reject color fills on formats with fixups,
4788 * but this would break P8 color fills for example. */
4790 return TRUE;
4792 case WINED3D_BLIT_OP_DEPTH_FILL:
4793 return TRUE;
4795 default:
4796 TRACE("Unsupported blit_op=%d\n", blit_op);
4797 return FALSE;
4801 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4802 const RECT *dst_rect, const struct wined3d_color *color)
4804 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4805 struct wined3d_rendertarget_view *view;
4806 struct wined3d_fb_state fb = {&view, NULL};
4807 HRESULT hr;
4809 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4810 NULL, &wined3d_null_parent_ops, &view)))
4812 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4813 return hr;
4816 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4817 wined3d_rendertarget_view_decref(view);
4819 return WINED3D_OK;
4822 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4823 const RECT *dst_rect, float depth)
4825 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4826 struct wined3d_fb_state fb = {NULL, NULL};
4827 HRESULT hr;
4829 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4830 NULL, &wined3d_null_parent_ops, &fb.depth_stencil)))
4832 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4833 return hr;
4836 device_clear_render_targets(device, 0, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4837 wined3d_rendertarget_view_decref(fb.depth_stencil);
4839 return WINED3D_OK;
4842 const struct blit_shader ffp_blit = {
4843 ffp_blit_alloc,
4844 ffp_blit_free,
4845 ffp_blit_set,
4846 ffp_blit_unset,
4847 ffp_blit_supported,
4848 ffp_blit_color_fill,
4849 ffp_blit_depth_fill,
4852 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4854 return WINED3D_OK;
4857 /* Context activation is done by the caller. */
4858 static void cpu_blit_free(struct wined3d_device *device)
4862 /* Context activation is done by the caller. */
4863 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4865 return WINED3D_OK;
4868 /* Context activation is done by the caller. */
4869 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4873 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4874 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4875 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4877 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4879 return TRUE;
4882 return FALSE;
4885 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4886 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4887 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4889 UINT row_block_count;
4890 const BYTE *src_row;
4891 BYTE *dst_row;
4892 UINT x, y;
4894 src_row = src_data;
4895 dst_row = dst_data;
4897 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4899 if (!flags)
4901 for (y = 0; y < update_h; y += format->block_height)
4903 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4904 src_row += src_pitch;
4905 dst_row += dst_pitch;
4908 return WINED3D_OK;
4911 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4913 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4915 switch (format->id)
4917 case WINED3DFMT_DXT1:
4918 for (y = 0; y < update_h; y += format->block_height)
4920 struct block
4922 WORD color[2];
4923 BYTE control_row[4];
4926 const struct block *s = (const struct block *)src_row;
4927 struct block *d = (struct block *)dst_row;
4929 for (x = 0; x < row_block_count; ++x)
4931 d[x].color[0] = s[x].color[0];
4932 d[x].color[1] = s[x].color[1];
4933 d[x].control_row[0] = s[x].control_row[3];
4934 d[x].control_row[1] = s[x].control_row[2];
4935 d[x].control_row[2] = s[x].control_row[1];
4936 d[x].control_row[3] = s[x].control_row[0];
4938 src_row -= src_pitch;
4939 dst_row += dst_pitch;
4941 return WINED3D_OK;
4943 case WINED3DFMT_DXT2:
4944 case WINED3DFMT_DXT3:
4945 for (y = 0; y < update_h; y += format->block_height)
4947 struct block
4949 WORD alpha_row[4];
4950 WORD color[2];
4951 BYTE control_row[4];
4954 const struct block *s = (const struct block *)src_row;
4955 struct block *d = (struct block *)dst_row;
4957 for (x = 0; x < row_block_count; ++x)
4959 d[x].alpha_row[0] = s[x].alpha_row[3];
4960 d[x].alpha_row[1] = s[x].alpha_row[2];
4961 d[x].alpha_row[2] = s[x].alpha_row[1];
4962 d[x].alpha_row[3] = s[x].alpha_row[0];
4963 d[x].color[0] = s[x].color[0];
4964 d[x].color[1] = s[x].color[1];
4965 d[x].control_row[0] = s[x].control_row[3];
4966 d[x].control_row[1] = s[x].control_row[2];
4967 d[x].control_row[2] = s[x].control_row[1];
4968 d[x].control_row[3] = s[x].control_row[0];
4970 src_row -= src_pitch;
4971 dst_row += dst_pitch;
4973 return WINED3D_OK;
4975 default:
4976 FIXME("Compressed flip not implemented for format %s.\n",
4977 debug_d3dformat(format->id));
4978 return E_NOTIMPL;
4982 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
4983 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
4985 return E_NOTIMPL;
4988 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4989 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4990 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4992 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
4993 const struct wined3d_format *src_format, *dst_format;
4994 struct wined3d_texture *src_texture = NULL;
4995 struct wined3d_map_desc dst_map, src_map;
4996 const BYTE *sbase = NULL;
4997 HRESULT hr = WINED3D_OK;
4998 const BYTE *sbuf;
4999 BYTE *dbuf;
5000 int x, y;
5002 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5003 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5004 flags, fx, debug_d3dtexturefiltertype(filter));
5006 if (src_surface == dst_surface)
5008 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
5009 src_map = dst_map;
5010 src_format = dst_surface->resource.format;
5011 dst_format = src_format;
5013 else
5015 dst_format = dst_surface->resource.format;
5016 if (src_surface)
5018 if (dst_surface->resource.format->id != src_surface->resource.format->id)
5020 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
5022 /* The conv function writes a FIXME */
5023 WARN("Cannot convert source surface format to dest format.\n");
5024 goto release;
5026 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
5028 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
5029 src_format = src_surface->resource.format;
5031 else
5033 src_format = dst_format;
5036 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
5039 bpp = dst_surface->resource.format->byte_count;
5040 srcheight = src_rect->bottom - src_rect->top;
5041 srcwidth = src_rect->right - src_rect->left;
5042 dstheight = dst_rect->bottom - dst_rect->top;
5043 dstwidth = dst_rect->right - dst_rect->left;
5044 width = (dst_rect->right - dst_rect->left) * bpp;
5046 if (src_surface)
5047 sbase = (BYTE *)src_map.data
5048 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
5049 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
5050 if (src_surface != dst_surface)
5051 dbuf = dst_map.data;
5052 else
5053 dbuf = (BYTE *)dst_map.data
5054 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
5055 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
5057 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
5059 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
5061 if (src_surface == dst_surface)
5063 FIXME("Only plain blits supported on compressed surfaces.\n");
5064 hr = E_NOTIMPL;
5065 goto release;
5068 if (srcheight != dstheight || srcwidth != dstwidth)
5070 WARN("Stretching not supported on compressed surfaces.\n");
5071 hr = WINED3DERR_INVALIDCALL;
5072 goto release;
5075 if (!surface_check_block_align(src_surface, src_rect))
5077 WARN("Source rectangle not block-aligned.\n");
5078 hr = WINED3DERR_INVALIDCALL;
5079 goto release;
5082 if (!surface_check_block_align(dst_surface, dst_rect))
5084 WARN("Destination rectangle not block-aligned.\n");
5085 hr = WINED3DERR_INVALIDCALL;
5086 goto release;
5089 hr = surface_cpu_blt_compressed(sbase, dbuf,
5090 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
5091 src_format, flags, fx);
5092 goto release;
5095 /* First, all the 'source-less' blits */
5096 if (flags & WINEDDBLT_COLORFILL)
5098 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
5099 flags &= ~WINEDDBLT_COLORFILL;
5102 if (flags & WINEDDBLT_DEPTHFILL)
5104 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
5106 if (flags & WINEDDBLT_ROP)
5108 /* Catch some degenerate cases here. */
5109 switch (fx->dwROP)
5111 case BLACKNESS:
5112 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
5113 break;
5114 case 0xaa0029: /* No-op */
5115 break;
5116 case WHITENESS:
5117 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
5118 break;
5119 case SRCCOPY: /* Well, we do that below? */
5120 break;
5121 default:
5122 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
5123 goto error;
5125 flags &= ~WINEDDBLT_ROP;
5127 if (flags & WINEDDBLT_DDROPS)
5129 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
5131 /* Now the 'with source' blits. */
5132 if (src_surface)
5134 int sx, xinc, sy, yinc;
5136 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
5137 goto release;
5139 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
5140 && (srcwidth != dstwidth || srcheight != dstheight))
5142 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
5143 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
5146 xinc = (srcwidth << 16) / dstwidth;
5147 yinc = (srcheight << 16) / dstheight;
5149 if (!flags)
5151 /* No effects, we can cheat here. */
5152 if (dstwidth == srcwidth)
5154 if (dstheight == srcheight)
5156 /* No stretching in either direction. This needs to be as
5157 * fast as possible. */
5158 sbuf = sbase;
5160 /* Check for overlapping surfaces. */
5161 if (src_surface != dst_surface || dst_rect->top < src_rect->top
5162 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
5164 /* No overlap, or dst above src, so copy from top downwards. */
5165 for (y = 0; y < dstheight; ++y)
5167 memcpy(dbuf, sbuf, width);
5168 sbuf += src_map.row_pitch;
5169 dbuf += dst_map.row_pitch;
5172 else if (dst_rect->top > src_rect->top)
5174 /* Copy from bottom upwards. */
5175 sbuf += src_map.row_pitch * dstheight;
5176 dbuf += dst_map.row_pitch * dstheight;
5177 for (y = 0; y < dstheight; ++y)
5179 sbuf -= src_map.row_pitch;
5180 dbuf -= dst_map.row_pitch;
5181 memcpy(dbuf, sbuf, width);
5184 else
5186 /* Src and dst overlapping on the same line, use memmove. */
5187 for (y = 0; y < dstheight; ++y)
5189 memmove(dbuf, sbuf, width);
5190 sbuf += src_map.row_pitch;
5191 dbuf += dst_map.row_pitch;
5195 else
5197 /* Stretching in y direction only. */
5198 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5200 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5201 memcpy(dbuf, sbuf, width);
5202 dbuf += dst_map.row_pitch;
5206 else
5208 /* Stretching in X direction. */
5209 int last_sy = -1;
5210 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5212 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5214 if ((sy >> 16) == (last_sy >> 16))
5216 /* This source row is the same as last source row -
5217 * Copy the already stretched row. */
5218 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
5220 else
5222 #define STRETCH_ROW(type) \
5223 do { \
5224 const type *s = (const type *)sbuf; \
5225 type *d = (type *)dbuf; \
5226 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5227 d[x] = s[sx >> 16]; \
5228 } while(0)
5230 switch(bpp)
5232 case 1:
5233 STRETCH_ROW(BYTE);
5234 break;
5235 case 2:
5236 STRETCH_ROW(WORD);
5237 break;
5238 case 4:
5239 STRETCH_ROW(DWORD);
5240 break;
5241 case 3:
5243 const BYTE *s;
5244 BYTE *d = dbuf;
5245 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
5247 DWORD pixel;
5249 s = sbuf + 3 * (sx >> 16);
5250 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5251 d[0] = (pixel ) & 0xff;
5252 d[1] = (pixel >> 8) & 0xff;
5253 d[2] = (pixel >> 16) & 0xff;
5254 d += 3;
5256 break;
5258 default:
5259 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
5260 hr = WINED3DERR_NOTAVAILABLE;
5261 goto error;
5263 #undef STRETCH_ROW
5265 dbuf += dst_map.row_pitch;
5266 last_sy = sy;
5270 else
5272 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
5273 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
5274 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
5275 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
5277 /* The color keying flags are checked for correctness in ddraw */
5278 if (flags & WINEDDBLT_KEYSRC)
5280 keylow = src_surface->container->src_blt_color_key.color_space_low_value;
5281 keyhigh = src_surface->container->src_blt_color_key.color_space_high_value;
5283 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5285 keylow = fx->ddckSrcColorkey.color_space_low_value;
5286 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
5289 if (flags & WINEDDBLT_KEYDEST)
5291 /* Destination color keys are taken from the source surface! */
5292 destkeylow = src_surface->container->dst_blt_color_key.color_space_low_value;
5293 destkeyhigh = src_surface->container->dst_blt_color_key.color_space_high_value;
5295 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
5297 destkeylow = fx->ddckDestColorkey.color_space_low_value;
5298 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
5301 if (bpp == 1)
5303 keymask = 0xff;
5305 else
5307 DWORD masks[3];
5308 get_color_masks(src_format, masks);
5309 keymask = masks[0]
5310 | masks[1]
5311 | masks[2];
5313 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
5316 if (flags & WINEDDBLT_DDFX)
5318 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
5319 LONG tmpxy;
5320 dTopLeft = dbuf;
5321 dTopRight = dbuf + ((dstwidth - 1) * bpp);
5322 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
5323 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
5325 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
5327 /* I don't think we need to do anything about this flag */
5328 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
5330 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
5332 tmp = dTopRight;
5333 dTopRight = dTopLeft;
5334 dTopLeft = tmp;
5335 tmp = dBottomRight;
5336 dBottomRight = dBottomLeft;
5337 dBottomLeft = tmp;
5338 dstxinc = dstxinc * -1;
5340 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
5342 tmp = dTopLeft;
5343 dTopLeft = dBottomLeft;
5344 dBottomLeft = tmp;
5345 tmp = dTopRight;
5346 dTopRight = dBottomRight;
5347 dBottomRight = tmp;
5348 dstyinc = dstyinc * -1;
5350 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
5352 /* I don't think we need to do anything about this flag */
5353 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
5355 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
5357 tmp = dBottomRight;
5358 dBottomRight = dTopLeft;
5359 dTopLeft = tmp;
5360 tmp = dBottomLeft;
5361 dBottomLeft = dTopRight;
5362 dTopRight = tmp;
5363 dstxinc = dstxinc * -1;
5364 dstyinc = dstyinc * -1;
5366 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
5368 tmp = dTopLeft;
5369 dTopLeft = dBottomLeft;
5370 dBottomLeft = dBottomRight;
5371 dBottomRight = dTopRight;
5372 dTopRight = tmp;
5373 tmpxy = dstxinc;
5374 dstxinc = dstyinc;
5375 dstyinc = tmpxy;
5376 dstxinc = dstxinc * -1;
5378 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
5380 tmp = dTopLeft;
5381 dTopLeft = dTopRight;
5382 dTopRight = dBottomRight;
5383 dBottomRight = dBottomLeft;
5384 dBottomLeft = tmp;
5385 tmpxy = dstxinc;
5386 dstxinc = dstyinc;
5387 dstyinc = tmpxy;
5388 dstyinc = dstyinc * -1;
5390 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
5392 /* I don't think we need to do anything about this flag */
5393 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
5395 dbuf = dTopLeft;
5396 flags &= ~(WINEDDBLT_DDFX);
5399 #define COPY_COLORKEY_FX(type) \
5400 do { \
5401 const type *s; \
5402 type *d = (type *)dbuf, *dx, tmp; \
5403 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
5405 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
5406 dx = d; \
5407 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5409 tmp = s[sx >> 16]; \
5410 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
5411 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
5413 dx[0] = tmp; \
5415 dx = (type *)(((BYTE *)dx) + dstxinc); \
5417 d = (type *)(((BYTE *)d) + dstyinc); \
5419 } while(0)
5421 switch (bpp)
5423 case 1:
5424 COPY_COLORKEY_FX(BYTE);
5425 break;
5426 case 2:
5427 COPY_COLORKEY_FX(WORD);
5428 break;
5429 case 4:
5430 COPY_COLORKEY_FX(DWORD);
5431 break;
5432 case 3:
5434 const BYTE *s;
5435 BYTE *d = dbuf, *dx;
5436 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5438 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5439 dx = d;
5440 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
5442 DWORD pixel, dpixel = 0;
5443 s = sbuf + 3 * (sx>>16);
5444 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5445 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
5446 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
5447 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
5449 dx[0] = (pixel ) & 0xff;
5450 dx[1] = (pixel >> 8) & 0xff;
5451 dx[2] = (pixel >> 16) & 0xff;
5453 dx += dstxinc;
5455 d += dstyinc;
5457 break;
5459 default:
5460 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
5461 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
5462 hr = WINED3DERR_NOTAVAILABLE;
5463 goto error;
5464 #undef COPY_COLORKEY_FX
5469 error:
5470 if (flags && FIXME_ON(d3d_surface))
5472 FIXME("\tUnsupported flags: %#x.\n", flags);
5475 release:
5476 wined3d_surface_unmap(dst_surface);
5477 if (src_surface && src_surface != dst_surface)
5478 wined3d_surface_unmap(src_surface);
5479 /* Release the converted surface, if any. */
5480 if (src_texture)
5481 wined3d_texture_decref(src_texture);
5483 return hr;
5486 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5487 const RECT *dst_rect, const struct wined3d_color *color)
5489 static const RECT src_rect;
5490 WINEDDBLTFX BltFx;
5492 memset(&BltFx, 0, sizeof(BltFx));
5493 BltFx.dwSize = sizeof(BltFx);
5494 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
5495 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
5496 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
5499 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
5500 struct wined3d_surface *surface, const RECT *rect, float depth)
5502 FIXME("Depth filling not implemented by cpu_blit.\n");
5503 return WINED3DERR_INVALIDCALL;
5506 const struct blit_shader cpu_blit = {
5507 cpu_blit_alloc,
5508 cpu_blit_free,
5509 cpu_blit_set,
5510 cpu_blit_unset,
5511 cpu_blit_supported,
5512 cpu_blit_color_fill,
5513 cpu_blit_depth_fill,
5516 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5517 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5518 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5520 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5521 struct wined3d_device *device = dst_surface->resource.device;
5522 DWORD src_ds_flags, dst_ds_flags;
5523 RECT src_rect, dst_rect;
5524 BOOL scale, convert;
5525 enum wined3d_conversion_type dst_convert_type;
5526 struct wined3d_format dst_conv_fmt;
5528 static const DWORD simple_blit = WINEDDBLT_ASYNC
5529 | WINEDDBLT_COLORFILL
5530 | WINEDDBLT_WAIT
5531 | WINEDDBLT_DEPTHFILL
5532 | WINEDDBLT_DONOTWAIT;
5534 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5535 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
5536 flags, fx, debug_d3dtexturefiltertype(filter));
5537 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5539 if (fx)
5541 TRACE("dwSize %#x.\n", fx->dwSize);
5542 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5543 TRACE("dwROP %#x.\n", fx->dwROP);
5544 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5545 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5546 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5547 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5548 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5549 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5550 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5551 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5552 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5553 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5554 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5555 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5556 TRACE("dwReserved %#x.\n", fx->dwReserved);
5557 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5558 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5559 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5560 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5561 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5562 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5563 fx->ddckDestColorkey.color_space_low_value,
5564 fx->ddckDestColorkey.color_space_high_value);
5565 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5566 fx->ddckSrcColorkey.color_space_low_value,
5567 fx->ddckSrcColorkey.color_space_high_value);
5570 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5572 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5573 return WINEDDERR_SURFACEBUSY;
5576 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5578 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5579 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5580 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5581 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5582 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5584 WARN("The application gave us a bad destination rectangle.\n");
5585 return WINEDDERR_INVALIDRECT;
5588 if (src_surface)
5590 surface_get_rect(src_surface, src_rect_in, &src_rect);
5592 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5593 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5594 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5595 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5596 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5598 WARN("Application gave us bad source rectangle for Blt.\n");
5599 return WINEDDERR_INVALIDRECT;
5602 else
5604 memset(&src_rect, 0, sizeof(src_rect));
5607 if (!fx || !(fx->dwDDFX))
5608 flags &= ~WINEDDBLT_DDFX;
5610 if (flags & WINEDDBLT_WAIT)
5611 flags &= ~WINEDDBLT_WAIT;
5613 if (flags & WINEDDBLT_ASYNC)
5615 static unsigned int once;
5617 if (!once++)
5618 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5619 flags &= ~WINEDDBLT_ASYNC;
5622 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5623 if (flags & WINEDDBLT_DONOTWAIT)
5625 static unsigned int once;
5627 if (!once++)
5628 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5629 flags &= ~WINEDDBLT_DONOTWAIT;
5632 if (!device->d3d_initialized)
5634 WARN("D3D not initialized, using fallback.\n");
5635 goto cpu;
5638 /* We want to avoid invalidating the sysmem location for converted
5639 * surfaces, since otherwise we'd have to convert the data back when
5640 * locking them. */
5641 d3dfmt_get_conv(dst_surface->container, TRUE, &dst_conv_fmt, &dst_convert_type);
5642 if (dst_convert_type != WINED3D_CT_NONE || dst_conv_fmt.convert || dst_surface->flags & SFLAG_CONVERTED)
5644 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5645 goto cpu;
5648 if (flags & ~simple_blit)
5650 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5651 goto fallback;
5654 if (src_surface)
5655 src_swapchain = src_surface->container->swapchain;
5656 else
5657 src_swapchain = NULL;
5659 dst_swapchain = dst_surface->container->swapchain;
5661 /* This isn't strictly needed. FBO blits for example could deal with
5662 * cross-swapchain blits by first downloading the source to a texture
5663 * before switching to the destination context. We just have this here to
5664 * not have to deal with the issue, since cross-swapchain blits should be
5665 * rare. */
5666 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5668 FIXME("Using fallback for cross-swapchain blit.\n");
5669 goto fallback;
5672 scale = src_surface
5673 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5674 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5675 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5677 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5678 if (src_surface)
5679 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5680 else
5681 src_ds_flags = 0;
5683 if (src_ds_flags || dst_ds_flags)
5685 if (flags & WINEDDBLT_DEPTHFILL)
5687 float depth;
5689 TRACE("Depth fill.\n");
5691 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5692 return WINED3DERR_INVALIDCALL;
5694 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
5695 return WINED3D_OK;
5697 else
5699 if (src_ds_flags != dst_ds_flags)
5701 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
5702 return WINED3DERR_INVALIDCALL;
5705 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
5706 &src_rect, dst_surface, dst_surface->container->resource.draw_binding, &dst_rect)))
5707 return WINED3D_OK;
5710 else
5712 /* In principle this would apply to depth blits as well, but we don't
5713 * implement those in the CPU blitter at the moment. */
5714 if ((dst_surface->locations & dst_surface->resource.map_binding)
5715 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
5717 if (scale)
5718 TRACE("Not doing sysmem blit because of scaling.\n");
5719 else if (convert)
5720 TRACE("Not doing sysmem blit because of format conversion.\n");
5721 else
5722 goto cpu;
5725 if (flags & WINEDDBLT_COLORFILL)
5727 struct wined3d_color color;
5729 TRACE("Color fill.\n");
5731 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
5732 goto fallback;
5734 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
5735 return WINED3D_OK;
5737 else
5739 TRACE("Color blit.\n");
5741 /* Upload */
5742 if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5743 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5745 if (scale)
5746 TRACE("Not doing upload because of scaling.\n");
5747 else if (convert)
5748 TRACE("Not doing upload because of format conversion.\n");
5749 else
5751 POINT dst_point = {dst_rect.left, dst_rect.top};
5753 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
5755 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
5756 surface_load_location(dst_surface, dst_surface->container->resource.draw_binding);
5757 return WINED3D_OK;
5762 /* Use present for back -> front blits. The idea behind this is
5763 * that present is potentially faster than a blit, in particular
5764 * when FBO blits aren't available. Some ddraw applications like
5765 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5766 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5767 * applications can't blit directly to the frontbuffer. */
5768 if (dst_swapchain && dst_swapchain->back_buffers
5769 && dst_surface->container == dst_swapchain->front_buffer
5770 && src_surface->container == dst_swapchain->back_buffers[0])
5772 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5774 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5776 /* Set the swap effect to COPY, we don't want the backbuffer
5777 * to become undefined. */
5778 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5779 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5780 dst_swapchain->desc.swap_effect = swap_effect;
5782 return WINED3D_OK;
5785 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5786 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5787 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5789 TRACE("Using FBO blit.\n");
5791 surface_blt_fbo(device, filter,
5792 src_surface, src_surface->container->resource.draw_binding, &src_rect,
5793 dst_surface, dst_surface->container->resource.draw_binding, &dst_rect);
5794 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5795 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5797 return WINED3D_OK;
5800 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5801 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5802 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5804 TRACE("Using arbfp blit.\n");
5806 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
5807 return WINED3D_OK;
5812 fallback:
5813 /* Special cases for render targets. */
5814 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
5815 return WINED3D_OK;
5817 cpu:
5819 /* For the rest call the X11 surface implementation. For render targets
5820 * this should be implemented OpenGL accelerated in surface_blt_special(),
5821 * other blits are rather rare. */
5822 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
5825 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5826 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5828 struct wined3d_device *device = container->resource.device;
5829 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5830 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5831 UINT multisample_quality = desc->multisample_quality;
5832 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
5833 unsigned int resource_size;
5834 HRESULT hr;
5836 if (multisample_quality > 0)
5838 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
5839 multisample_quality = 0;
5842 /* Quick lockable sanity check.
5843 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5844 * this function is too deep to need to care about things like this.
5845 * Levels need to be checked too, since they all affect what can be done. */
5846 switch (desc->pool)
5848 case WINED3D_POOL_MANAGED:
5849 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5850 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5851 break;
5853 case WINED3D_POOL_DEFAULT:
5854 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5855 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5856 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5857 break;
5859 case WINED3D_POOL_SCRATCH:
5860 case WINED3D_POOL_SYSTEM_MEM:
5861 break;
5863 default:
5864 FIXME("Unknown pool %#x.\n", desc->pool);
5865 break;
5868 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5869 FIXME("Trying to create a render target that isn't in the default pool.\n");
5871 /* FIXME: Check that the format is supported by the device. */
5873 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5874 if (!resource_size)
5875 return WINED3DERR_INVALIDCALL;
5877 if (device->wined3d->flags & WINED3D_NO3D)
5878 surface->surface_ops = &gdi_surface_ops;
5879 else
5880 surface->surface_ops = &surface_ops;
5882 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
5883 desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height, 1,
5884 resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5886 WARN("Failed to initialize resource, returning %#x.\n", hr);
5887 return hr;
5890 surface->container = container;
5891 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5892 list_init(&surface->renderbuffers);
5893 list_init(&surface->overlays);
5895 /* Flags */
5896 if (target != GL_TEXTURE_RECTANGLE_ARB)
5897 surface->flags |= SFLAG_NORMCOORD;
5898 if (flags & WINED3D_SURFACE_DISCARD)
5899 surface->flags |= SFLAG_DISCARD;
5900 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
5901 surface->flags |= SFLAG_PIN_SYSMEM;
5902 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5903 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5905 surface->texture_target = target;
5906 surface->texture_level = level;
5907 surface->texture_layer = layer;
5909 /* Call the private setup routine */
5910 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5912 ERR("Private setup failed, hr %#x.\n", hr);
5913 surface_cleanup(surface);
5914 return hr;
5917 /* Similar to lockable rendertargets above, creating the DIB section
5918 * during surface initialization prevents the sysmem pointer from changing
5919 * after a wined3d_surface_getdc() call. */
5920 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5921 && SUCCEEDED(surface_create_dib_section(surface)))
5922 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5924 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5926 wined3d_resource_free_sysmem(&surface->resource);
5927 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5928 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5931 return hr;
5934 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5935 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
5937 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
5938 const struct wined3d_parent_ops *parent_ops;
5939 struct wined3d_surface *object;
5940 void *parent;
5941 HRESULT hr;
5943 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
5944 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
5945 container, desc->width, desc->height, debug_d3dformat(desc->format),
5946 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
5947 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
5949 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
5950 return E_OUTOFMEMORY;
5952 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
5954 WARN("Failed to initialize surface, returning %#x.\n", hr);
5955 HeapFree(GetProcessHeap(), 0, object);
5956 return hr;
5959 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
5960 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
5962 WARN("Failed to create surface parent, hr %#x.\n", hr);
5963 wined3d_surface_destroy(object);
5964 return hr;
5967 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
5969 object->resource.parent = parent;
5970 object->resource.parent_ops = parent_ops;
5971 *surface = object;
5973 return hr;