wined3d: Introduce a separate function for WINED3D_CT_CK_B5G6R5.
[wine.git] / dlls / wined3d / surface.c
blob6b13dd0915b23dcefc50d2b8806380afb6889a39
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 const struct wined3d_format *dst_format;
1595 enum wined3d_format_id dst_format_id;
1596 unsigned int i;
1598 static const struct
1600 enum wined3d_format_id src_format;
1601 enum wined3d_conversion_type conversion_type;
1602 enum wined3d_format_id dst_format;
1604 color_key_info[] =
1606 {WINED3DFMT_B5G6R5_UNORM, WINED3D_CT_CK_B5G6R5, WINED3DFMT_B5G5R5A1_UNORM},
1607 {WINED3DFMT_B5G5R5X1_UNORM, WINED3D_CT_CK_B5G5R5X1, WINED3DFMT_B5G5R5A1_UNORM},
1608 {WINED3DFMT_B8G8R8_UNORM, WINED3D_CT_CK_B8G8R8, WINED3DFMT_B8G8R8A8_UNORM},
1609 {WINED3DFMT_B8G8R8X8_UNORM, WINED3D_CT_CK_B8G8R8X8, WINED3DFMT_B8G8R8A8_UNORM},
1610 {WINED3DFMT_B8G8R8A8_UNORM, WINED3D_CT_CK_B8G8R8A8, WINED3DFMT_B8G8R8A8_UNORM},
1613 *format = *texture->resource.format;
1614 *conversion_type = WINED3D_CT_NONE;
1615 dst_format_id = format->id;
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 dst_format_id = color_key_info[i].dst_format;
1626 break;
1630 if (texture->resource.format->id == WINED3DFMT_P8_UINT)
1632 /* FIXME: This should check if the blitter backend can do P8
1633 * conversion, instead of checking for ARB_fragment_program. */
1634 if (!((gl_info->supported[ARB_FRAGMENT_PROGRAM] && texture->swapchain
1635 && texture == texture->swapchain->front_buffer)) || colorkey_active)
1637 *conversion_type = WINED3D_CT_P8;
1638 dst_format_id = WINED3DFMT_B8G8R8A8_UNORM;
1641 else if (texture->resource.format->id == WINED3DFMT_B2G3R3_UNORM && colorkey_active)
1643 /* This texture format will never be used... So do not care about
1644 * color-keying up until the point in time it will be needed. */
1645 FIXME("Color-keying not supported with WINED3DFMT_B2G3R3_UNORM.\n");
1648 if (*conversion_type != WINED3D_CT_NONE)
1650 dst_format = wined3d_get_format(gl_info, dst_format_id);
1651 format->glInternal = dst_format->glInternal;
1652 format->glGammaInternal = format->glInternal;
1653 format->rtInternal = format->glInternal;
1654 format->glFormat = dst_format->glFormat;
1655 format->glType = dst_format->glType;
1656 format->conv_byte_count = dst_format->byte_count;
1660 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
1662 UINT width_mask, height_mask;
1664 if (!rect->left && !rect->top
1665 && rect->right == surface->resource.width
1666 && rect->bottom == surface->resource.height)
1667 return TRUE;
1669 /* This assumes power of two block sizes, but NPOT block sizes would be
1670 * silly anyway. */
1671 width_mask = surface->resource.format->block_width - 1;
1672 height_mask = surface->resource.format->block_height - 1;
1674 if (!(rect->left & width_mask) && !(rect->top & height_mask)
1675 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
1676 return TRUE;
1678 return FALSE;
1681 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1682 struct wined3d_surface *src_surface, const RECT *src_rect)
1684 const struct wined3d_format *src_format;
1685 const struct wined3d_format *dst_format;
1686 const struct wined3d_gl_info *gl_info;
1687 enum wined3d_conversion_type convert;
1688 struct wined3d_context *context;
1689 struct wined3d_bo_address data;
1690 struct wined3d_format format;
1691 UINT update_w, update_h;
1692 UINT dst_w, dst_h;
1693 RECT r, dst_rect;
1694 UINT src_pitch;
1695 POINT p;
1697 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1698 dst_surface, wine_dbgstr_point(dst_point),
1699 src_surface, wine_dbgstr_rect(src_rect));
1701 src_format = src_surface->resource.format;
1702 dst_format = dst_surface->resource.format;
1704 if (src_format->id != dst_format->id)
1706 WARN("Source and destination surfaces should have the same format.\n");
1707 return WINED3DERR_INVALIDCALL;
1710 if (!dst_point)
1712 p.x = 0;
1713 p.y = 0;
1714 dst_point = &p;
1716 else if (dst_point->x < 0 || dst_point->y < 0)
1718 WARN("Invalid destination point.\n");
1719 return WINED3DERR_INVALIDCALL;
1722 if (!src_rect)
1724 r.left = 0;
1725 r.top = 0;
1726 r.right = src_surface->resource.width;
1727 r.bottom = src_surface->resource.height;
1728 src_rect = &r;
1730 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1731 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1733 WARN("Invalid source rectangle.\n");
1734 return WINED3DERR_INVALIDCALL;
1737 dst_w = dst_surface->resource.width;
1738 dst_h = dst_surface->resource.height;
1740 update_w = src_rect->right - src_rect->left;
1741 update_h = src_rect->bottom - src_rect->top;
1743 if (update_w > dst_w || dst_point->x > dst_w - update_w
1744 || update_h > dst_h || dst_point->y > dst_h - update_h)
1746 WARN("Destination out of bounds.\n");
1747 return WINED3DERR_INVALIDCALL;
1750 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
1752 WARN("Source rectangle not block-aligned.\n");
1753 return WINED3DERR_INVALIDCALL;
1756 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1757 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
1759 WARN("Destination rectangle not block-aligned.\n");
1760 return WINED3DERR_INVALIDCALL;
1763 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1764 d3dfmt_get_conv(dst_surface->container, FALSE, &format, &convert);
1765 if (convert != WINED3D_CT_NONE || format.convert)
1766 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1768 context = context_acquire(dst_surface->resource.device, NULL);
1769 gl_info = context->gl_info;
1771 /* Only load the surface for partial updates. For newly allocated texture
1772 * the texture wouldn't be the current location, and we'd upload zeroes
1773 * just to overwrite them again. */
1774 if (update_w == dst_w && update_h == dst_h)
1775 surface_prepare_texture(dst_surface, context, FALSE);
1776 else
1777 surface_load_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1778 wined3d_texture_bind(dst_surface->container, context, FALSE);
1780 surface_get_memory(src_surface, &data, src_surface->locations);
1781 src_pitch = wined3d_surface_get_pitch(src_surface);
1783 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
1785 context_invalidate_active_texture(context);
1787 context_release(context);
1789 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1790 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1792 return WINED3D_OK;
1795 /* This call just allocates the texture, the caller is responsible for binding
1796 * the correct texture. */
1797 /* Context activation is done by the caller. */
1798 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1799 const struct wined3d_format *format, BOOL srgb)
1801 BOOL disable_client_storage = FALSE;
1802 GLsizei width = surface->pow2Width;
1803 GLsizei height = surface->pow2Height;
1804 const BYTE *mem = NULL;
1805 GLenum internal;
1807 if (srgb)
1808 internal = format->glGammaInternal;
1809 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1810 && wined3d_resource_is_offscreen(&surface->container->resource))
1811 internal = format->rtInternal;
1812 else
1813 internal = format->glInternal;
1815 if (!internal)
1816 FIXME("No GL internal format for format %s.\n", debug_d3dformat(format->id));
1818 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1820 height *= format->height_scale.numerator;
1821 height /= format->height_scale.denominator;
1824 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",
1825 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
1826 internal, width, height, format->glFormat, format->glType);
1828 if (gl_info->supported[APPLE_CLIENT_STORAGE])
1830 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
1831 || !surface->resource.heap_memory)
1833 /* In some cases we want to disable client storage.
1834 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
1835 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
1836 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
1837 * heap_memory == NULL: Not defined in the extension. Seems to disable client storage effectively
1839 surface->flags &= ~SFLAG_CLIENT;
1841 else
1843 surface->flags |= SFLAG_CLIENT;
1844 mem = surface->resource.heap_memory;
1846 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
1847 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
1848 disable_client_storage = TRUE;
1852 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
1854 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
1855 internal, width, height, 0, surface->resource.size, mem));
1856 checkGLcall("glCompressedTexImage2DARB");
1858 else
1860 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
1861 internal, width, height, 0, format->glFormat, format->glType, mem);
1862 checkGLcall("glTexImage2D");
1865 if (disable_client_storage)
1867 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
1868 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
1872 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1873 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1874 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1875 /* Context activation is done by the caller. */
1876 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1878 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1879 struct wined3d_renderbuffer_entry *entry;
1880 GLuint renderbuffer = 0;
1881 unsigned int src_width, src_height;
1882 unsigned int width, height;
1884 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1886 width = rt->pow2Width;
1887 height = rt->pow2Height;
1889 else
1891 width = surface->pow2Width;
1892 height = surface->pow2Height;
1895 src_width = surface->pow2Width;
1896 src_height = surface->pow2Height;
1898 /* A depth stencil smaller than the render target is not valid */
1899 if (width > src_width || height > src_height) return;
1901 /* Remove any renderbuffer set if the sizes match */
1902 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1903 || (width == src_width && height == src_height))
1905 surface->current_renderbuffer = NULL;
1906 return;
1909 /* Look if we've already got a renderbuffer of the correct dimensions */
1910 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1912 if (entry->width == width && entry->height == height)
1914 renderbuffer = entry->id;
1915 surface->current_renderbuffer = entry;
1916 break;
1920 if (!renderbuffer)
1922 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1923 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1924 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1925 surface->resource.format->glInternal, width, height);
1927 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1928 entry->width = width;
1929 entry->height = height;
1930 entry->id = renderbuffer;
1931 list_add_head(&surface->renderbuffers, &entry->entry);
1933 surface->current_renderbuffer = entry;
1936 checkGLcall("set_compatible_renderbuffer");
1939 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1941 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1943 TRACE("surface %p.\n", surface);
1945 if (!swapchain)
1947 ERR("Surface %p is not on a swapchain.\n", surface);
1948 return GL_NONE;
1951 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
1953 if (swapchain->render_to_fbo)
1955 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1956 return GL_COLOR_ATTACHMENT0;
1958 TRACE("Returning GL_BACK\n");
1959 return GL_BACK;
1961 else if (surface->container == swapchain->front_buffer)
1963 TRACE("Returning GL_FRONT\n");
1964 return GL_FRONT;
1967 FIXME("Higher back buffer, returning GL_BACK\n");
1968 return GL_BACK;
1971 void surface_load(struct wined3d_surface *surface, BOOL srgb)
1973 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1974 BOOL ck_changed;
1976 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1978 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
1979 ERR("Not supported on scratch surfaces.\n");
1981 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->container->color_key_flags & WINEDDSD_CKSRCBLT);
1983 /* Reload if either the texture and sysmem have different ideas about the
1984 * color key, or the actual key values changed. */
1985 if (ck_changed || ((surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
1986 && (surface->gl_color_key.color_space_low_value
1987 != surface->container->src_blt_color_key.color_space_low_value
1988 || surface->gl_color_key.color_space_high_value
1989 != surface->container->src_blt_color_key.color_space_high_value)))
1991 TRACE("Reloading because of color keying\n");
1992 /* To perform the color key conversion we need a sysmem copy of
1993 * the surface. Make sure we have it. */
1995 surface_prepare_map_memory(surface);
1996 surface_load_location(surface, surface->resource.map_binding);
1997 surface_invalidate_location(surface, ~surface->resource.map_binding);
1998 /* Switching color keying on / off may change the internal format. */
1999 if (ck_changed)
2000 surface_force_reload(surface);
2002 else if (!(surface->locations & location))
2004 TRACE("Reloading because surface is dirty.\n");
2006 else
2008 TRACE("surface is already in texture\n");
2009 return;
2012 surface_load_location(surface, location);
2013 surface_evict_sysmem(surface);
2016 /* See also float_16_to_32() in wined3d_private.h */
2017 static inline unsigned short float_32_to_16(const float *in)
2019 int exp = 0;
2020 float tmp = fabsf(*in);
2021 unsigned int mantissa;
2022 unsigned short ret;
2024 /* Deal with special numbers */
2025 if (*in == 0.0f)
2026 return 0x0000;
2027 if (isnan(*in))
2028 return 0x7c01;
2029 if (isinf(*in))
2030 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2032 if (tmp < powf(2, 10))
2036 tmp = tmp * 2.0f;
2037 exp--;
2038 } while (tmp < powf(2, 10));
2040 else if (tmp >= powf(2, 11))
2044 tmp /= 2.0f;
2045 exp++;
2046 } while (tmp >= powf(2, 11));
2049 mantissa = (unsigned int)tmp;
2050 if (tmp - mantissa >= 0.5f)
2051 ++mantissa; /* Round to nearest, away from zero. */
2053 exp += 10; /* Normalize the mantissa. */
2054 exp += 15; /* Exponent is encoded with excess 15. */
2056 if (exp > 30) /* too big */
2058 ret = 0x7c00; /* INF */
2060 else if (exp <= 0)
2062 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2063 while (exp <= 0)
2065 mantissa = mantissa >> 1;
2066 ++exp;
2068 ret = mantissa & 0x3ff;
2070 else
2072 ret = (exp << 10) | (mantissa & 0x3ff);
2075 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2076 return ret;
2079 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2081 TRACE("surface %p, container %p.\n", surface, surface->container);
2083 return wined3d_texture_incref(surface->container);
2086 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2088 TRACE("surface %p, container %p.\n", surface, surface->container);
2090 return wined3d_texture_decref(surface->container);
2093 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2095 TRACE("surface %p.\n", surface);
2097 if (!surface->resource.device->d3d_initialized)
2099 ERR("D3D not initialized.\n");
2100 return;
2103 wined3d_texture_preload(surface->container);
2106 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2108 TRACE("surface %p.\n", surface);
2110 return surface->resource.parent;
2113 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2115 TRACE("surface %p.\n", surface);
2117 return &surface->resource;
2120 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2122 TRACE("surface %p, flags %#x.\n", surface, flags);
2124 switch (flags)
2126 case WINEDDGBS_CANBLT:
2127 case WINEDDGBS_ISBLTDONE:
2128 return WINED3D_OK;
2130 default:
2131 return WINED3DERR_INVALIDCALL;
2135 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2137 TRACE("surface %p, flags %#x.\n", surface, flags);
2139 /* XXX: DDERR_INVALIDSURFACETYPE */
2141 switch (flags)
2143 case WINEDDGFS_CANFLIP:
2144 case WINEDDGFS_ISFLIPDONE:
2145 return WINED3D_OK;
2147 default:
2148 return WINED3DERR_INVALIDCALL;
2152 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2154 TRACE("surface %p.\n", surface);
2156 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2157 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2160 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2162 TRACE("surface %p.\n", surface);
2164 surface->flags &= ~SFLAG_LOST;
2165 return WINED3D_OK;
2168 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2170 unsigned int alignment;
2171 DWORD pitch;
2173 TRACE("surface %p.\n", surface);
2175 if (surface->pitch)
2176 return surface->pitch;
2178 alignment = surface->resource.device->surface_alignment;
2179 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
2180 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2182 TRACE("Returning %u.\n", pitch);
2184 return pitch;
2187 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2189 LONG w, h;
2191 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2193 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2195 WARN("Not an overlay surface.\n");
2196 return WINEDDERR_NOTAOVERLAYSURFACE;
2199 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2200 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2201 surface->overlay_destrect.left = x;
2202 surface->overlay_destrect.top = y;
2203 surface->overlay_destrect.right = x + w;
2204 surface->overlay_destrect.bottom = y + h;
2206 return WINED3D_OK;
2209 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2211 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2213 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2215 TRACE("Not an overlay surface.\n");
2216 return WINEDDERR_NOTAOVERLAYSURFACE;
2219 if (!surface->overlay_dest)
2221 TRACE("Overlay not visible.\n");
2222 *x = 0;
2223 *y = 0;
2224 return WINEDDERR_OVERLAYNOTVISIBLE;
2227 *x = surface->overlay_destrect.left;
2228 *y = surface->overlay_destrect.top;
2230 TRACE("Returning position %d, %d.\n", *x, *y);
2232 return WINED3D_OK;
2235 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2236 DWORD flags, struct wined3d_surface *ref)
2238 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2240 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2242 TRACE("Not an overlay surface.\n");
2243 return WINEDDERR_NOTAOVERLAYSURFACE;
2246 return WINED3D_OK;
2249 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2250 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2252 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2253 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2255 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2257 WARN("Not an overlay surface.\n");
2258 return WINEDDERR_NOTAOVERLAYSURFACE;
2260 else if (!dst_surface)
2262 WARN("Dest surface is NULL.\n");
2263 return WINED3DERR_INVALIDCALL;
2266 if (src_rect)
2268 surface->overlay_srcrect = *src_rect;
2270 else
2272 surface->overlay_srcrect.left = 0;
2273 surface->overlay_srcrect.top = 0;
2274 surface->overlay_srcrect.right = surface->resource.width;
2275 surface->overlay_srcrect.bottom = surface->resource.height;
2278 if (dst_rect)
2280 surface->overlay_destrect = *dst_rect;
2282 else
2284 surface->overlay_destrect.left = 0;
2285 surface->overlay_destrect.top = 0;
2286 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2287 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2290 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2292 surface->overlay_dest = NULL;
2293 list_remove(&surface->overlay_entry);
2296 if (flags & WINEDDOVER_SHOW)
2298 if (surface->overlay_dest != dst_surface)
2300 surface->overlay_dest = dst_surface;
2301 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2304 else if (flags & WINEDDOVER_HIDE)
2306 /* tests show that the rectangles are erased on hide */
2307 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2308 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2309 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2310 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2311 surface->overlay_dest = NULL;
2314 return WINED3D_OK;
2317 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface,
2318 const struct wined3d_gl_info *gl_info, void *mem, unsigned int pitch)
2320 struct wined3d_resource *texture_resource = &surface->container->resource;
2321 unsigned int width, height;
2322 BOOL create_dib = FALSE;
2323 DWORD valid_location = 0;
2324 HRESULT hr;
2326 if (surface->flags & SFLAG_DIBSECTION)
2328 DeleteDC(surface->hDC);
2329 DeleteObject(surface->dib.DIBsection);
2330 surface->dib.bitmap_data = NULL;
2331 surface->flags &= ~SFLAG_DIBSECTION;
2332 create_dib = TRUE;
2335 surface->locations = 0;
2336 wined3d_resource_free_sysmem(&surface->resource);
2338 width = texture_resource->width;
2339 height = texture_resource->height;
2340 surface->resource.width = width;
2341 surface->resource.height = height;
2342 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2343 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2345 surface->pow2Width = width;
2346 surface->pow2Height = height;
2348 else
2350 surface->pow2Width = surface->pow2Height = 1;
2351 while (surface->pow2Width < width)
2352 surface->pow2Width <<= 1;
2353 while (surface->pow2Height < height)
2354 surface->pow2Height <<= 1;
2357 if (surface->pow2Width != width || surface->pow2Height != height)
2358 surface->flags |= SFLAG_NONPOW2;
2359 else
2360 surface->flags &= ~SFLAG_NONPOW2;
2362 if ((surface->user_memory = mem))
2364 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
2365 valid_location = WINED3D_LOCATION_USER_MEMORY;
2367 surface->pitch = pitch;
2368 surface->resource.format = texture_resource->format;
2369 surface->resource.multisample_type = texture_resource->multisample_type;
2370 surface->resource.multisample_quality = texture_resource->multisample_quality;
2371 if (surface->pitch)
2372 surface->resource.size = height * surface->pitch;
2373 else
2374 surface->resource.size = wined3d_format_calculate_size(texture_resource->format,
2375 texture_resource->device->surface_alignment, width, height, 1);
2377 /* The format might be changed to a format that needs conversion.
2378 * If the surface didn't use PBOs previously but could now, don't
2379 * change it - whatever made us not use PBOs might come back, e.g.
2380 * color keys. */
2381 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2382 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2384 if (create_dib)
2386 if (FAILED(hr = surface_create_dib_section(surface)))
2388 ERR("Failed to create dib section, hr %#x.\n", hr);
2389 return hr;
2391 if (!valid_location)
2392 valid_location = WINED3D_LOCATION_DIB;
2395 if (!valid_location)
2397 surface_prepare_system_memory(surface);
2398 valid_location = WINED3D_LOCATION_SYSMEM;
2401 surface_validate_location(surface, valid_location);
2403 return WINED3D_OK;
2406 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2407 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2409 unsigned short *dst_s;
2410 const float *src_f;
2411 unsigned int x, y;
2413 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2415 for (y = 0; y < h; ++y)
2417 src_f = (const float *)(src + y * pitch_in);
2418 dst_s = (unsigned short *) (dst + y * pitch_out);
2419 for (x = 0; x < w; ++x)
2421 dst_s[x] = float_32_to_16(src_f + x);
2426 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2427 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2429 static const unsigned char convert_5to8[] =
2431 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2432 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2433 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2434 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2436 static const unsigned char convert_6to8[] =
2438 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2439 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2440 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2441 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2442 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2443 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2444 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2445 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2447 unsigned int x, y;
2449 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2451 for (y = 0; y < h; ++y)
2453 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2454 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2455 for (x = 0; x < w; ++x)
2457 WORD pixel = src_line[x];
2458 dst_line[x] = 0xff000000
2459 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2460 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2461 | convert_5to8[(pixel & 0x001f)];
2466 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2467 * in both cases we're just setting the X / Alpha channel to 0xff. */
2468 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2469 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2471 unsigned int x, y;
2473 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2475 for (y = 0; y < h; ++y)
2477 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2478 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2480 for (x = 0; x < w; ++x)
2482 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2487 static inline BYTE cliptobyte(int x)
2489 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2492 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2493 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2495 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2496 unsigned int x, y;
2498 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2500 for (y = 0; y < h; ++y)
2502 const BYTE *src_line = src + y * pitch_in;
2503 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2504 for (x = 0; x < w; ++x)
2506 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2507 * C = Y - 16; D = U - 128; E = V - 128;
2508 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2509 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2510 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2511 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2512 * U and V are shared between the pixels. */
2513 if (!(x & 1)) /* For every even pixel, read new U and V. */
2515 d = (int) src_line[1] - 128;
2516 e = (int) src_line[3] - 128;
2517 r2 = 409 * e + 128;
2518 g2 = - 100 * d - 208 * e + 128;
2519 b2 = 516 * d + 128;
2521 c2 = 298 * ((int) src_line[0] - 16);
2522 dst_line[x] = 0xff000000
2523 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2524 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2525 | cliptobyte((c2 + b2) >> 8); /* blue */
2526 /* Scale RGB values to 0..255 range,
2527 * then clip them if still not in range (may be negative),
2528 * then shift them within DWORD if necessary. */
2529 src_line += 2;
2534 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2535 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2537 unsigned int x, y;
2538 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2540 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2542 for (y = 0; y < h; ++y)
2544 const BYTE *src_line = src + y * pitch_in;
2545 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2546 for (x = 0; x < w; ++x)
2548 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2549 * C = Y - 16; D = U - 128; E = V - 128;
2550 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2551 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2552 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2553 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2554 * U and V are shared between the pixels. */
2555 if (!(x & 1)) /* For every even pixel, read new U and V. */
2557 d = (int) src_line[1] - 128;
2558 e = (int) src_line[3] - 128;
2559 r2 = 409 * e + 128;
2560 g2 = - 100 * d - 208 * e + 128;
2561 b2 = 516 * d + 128;
2563 c2 = 298 * ((int) src_line[0] - 16);
2564 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2565 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2566 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2567 /* Scale RGB values to 0..255 range,
2568 * then clip them if still not in range (may be negative),
2569 * then shift them within DWORD if necessary. */
2570 src_line += 2;
2575 struct d3dfmt_converter_desc
2577 enum wined3d_format_id from, to;
2578 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2581 static const struct d3dfmt_converter_desc converters[] =
2583 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2584 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2585 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2586 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2587 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2588 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2591 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2592 enum wined3d_format_id to)
2594 unsigned int i;
2596 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2598 if (converters[i].from == from && converters[i].to == to)
2599 return &converters[i];
2602 return NULL;
2605 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2607 struct wined3d_map_desc src_map, dst_map;
2608 const struct d3dfmt_converter_desc *conv;
2609 struct wined3d_texture *ret = NULL;
2610 struct wined3d_resource_desc desc;
2611 struct wined3d_surface *dst;
2613 conv = find_converter(source->resource.format->id, to_fmt);
2614 if (!conv)
2616 FIXME("Cannot find a conversion function from format %s to %s.\n",
2617 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2618 return NULL;
2621 /* FIXME: Multisampled conversion? */
2622 wined3d_resource_get_desc(&source->resource, &desc);
2623 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2624 desc.format = to_fmt;
2625 desc.usage = 0;
2626 desc.pool = WINED3D_POOL_SCRATCH;
2627 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2628 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, &wined3d_null_parent_ops, &ret)))
2630 ERR("Failed to create a destination surface for conversion.\n");
2631 return NULL;
2633 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2635 memset(&src_map, 0, sizeof(src_map));
2636 memset(&dst_map, 0, sizeof(dst_map));
2638 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2640 ERR("Failed to lock the source surface.\n");
2641 wined3d_texture_decref(ret);
2642 return NULL;
2644 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2646 ERR("Failed to lock the destination surface.\n");
2647 wined3d_surface_unmap(source);
2648 wined3d_texture_decref(ret);
2649 return NULL;
2652 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2653 source->resource.width, source->resource.height);
2655 wined3d_surface_unmap(dst);
2656 wined3d_surface_unmap(source);
2658 return ret;
2661 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2662 unsigned int bpp, UINT pitch, DWORD color)
2664 BYTE *first;
2665 unsigned int x, y;
2667 /* Do first row */
2669 #define COLORFILL_ROW(type) \
2670 do { \
2671 type *d = (type *)buf; \
2672 for (x = 0; x < width; ++x) \
2673 d[x] = (type)color; \
2674 } while(0)
2676 switch (bpp)
2678 case 1:
2679 COLORFILL_ROW(BYTE);
2680 break;
2682 case 2:
2683 COLORFILL_ROW(WORD);
2684 break;
2686 case 3:
2688 BYTE *d = buf;
2689 for (x = 0; x < width; ++x, d += 3)
2691 d[0] = (color ) & 0xff;
2692 d[1] = (color >> 8) & 0xff;
2693 d[2] = (color >> 16) & 0xff;
2695 break;
2697 case 4:
2698 COLORFILL_ROW(DWORD);
2699 break;
2701 default:
2702 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2703 return WINED3DERR_NOTAVAILABLE;
2706 #undef COLORFILL_ROW
2708 /* Now copy first row. */
2709 first = buf;
2710 for (y = 1; y < height; ++y)
2712 buf += pitch;
2713 memcpy(buf, first, width * bpp);
2716 return WINED3D_OK;
2719 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2721 return surface_from_resource(resource);
2724 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2726 TRACE("surface %p.\n", surface);
2728 if (!surface->resource.map_count)
2730 WARN("Trying to unmap unmapped surface.\n");
2731 return WINEDDERR_NOTLOCKED;
2733 --surface->resource.map_count;
2735 surface->surface_ops->surface_unmap(surface);
2737 return WINED3D_OK;
2740 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2741 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
2743 const struct wined3d_format *format = surface->resource.format;
2744 struct wined3d_device *device = surface->resource.device;
2745 struct wined3d_context *context;
2746 const struct wined3d_gl_info *gl_info;
2747 BYTE *base_memory;
2749 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
2750 surface, map_desc, wine_dbgstr_rect(rect), flags);
2752 if (surface->resource.map_count)
2754 WARN("Surface is already mapped.\n");
2755 return WINED3DERR_INVALIDCALL;
2758 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
2759 && !surface_check_block_align(surface, rect))
2761 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
2762 wine_dbgstr_rect(rect), format->block_width, format->block_height);
2764 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2765 return WINED3DERR_INVALIDCALL;
2768 ++surface->resource.map_count;
2770 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2771 WARN("Trying to lock unlockable surface.\n");
2773 /* Performance optimization: Count how often a surface is mapped, if it is
2774 * mapped regularly do not throw away the system memory copy. This avoids
2775 * the need to download the surface from OpenGL all the time. The surface
2776 * is still downloaded if the OpenGL texture is changed. */
2777 if (!(surface->flags & SFLAG_DYNLOCK) && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2779 if (++surface->lockCount > MAXLOCKCOUNT)
2781 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2782 surface->flags |= SFLAG_DYNLOCK;
2786 surface_prepare_map_memory(surface);
2787 if (flags & WINED3D_MAP_DISCARD)
2789 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2790 wined3d_debug_location(surface->resource.map_binding));
2791 surface_validate_location(surface, surface->resource.map_binding);
2793 else
2795 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2796 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2798 surface_load_location(surface, surface->resource.map_binding);
2801 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2802 surface_invalidate_location(surface, ~surface->resource.map_binding);
2804 switch (surface->resource.map_binding)
2806 case WINED3D_LOCATION_SYSMEM:
2807 base_memory = surface->resource.heap_memory;
2808 break;
2810 case WINED3D_LOCATION_USER_MEMORY:
2811 base_memory = surface->user_memory;
2812 break;
2814 case WINED3D_LOCATION_DIB:
2815 base_memory = surface->dib.bitmap_data;
2816 break;
2818 case WINED3D_LOCATION_BUFFER:
2819 context = context_acquire(device, NULL);
2820 gl_info = context->gl_info;
2822 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
2823 base_memory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
2824 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2825 checkGLcall("map PBO");
2827 context_release(context);
2828 break;
2830 default:
2831 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2832 base_memory = NULL;
2835 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2836 map_desc->row_pitch = surface->resource.width * format->byte_count;
2837 else
2838 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2839 map_desc->slice_pitch = 0;
2841 if (!rect)
2843 map_desc->data = base_memory;
2844 surface->lockedRect.left = 0;
2845 surface->lockedRect.top = 0;
2846 surface->lockedRect.right = surface->resource.width;
2847 surface->lockedRect.bottom = surface->resource.height;
2849 else
2851 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2853 /* Compressed textures are block based, so calculate the offset of
2854 * the block that contains the top-left pixel of the locked rectangle. */
2855 map_desc->data = base_memory
2856 + ((rect->top / format->block_height) * map_desc->row_pitch)
2857 + ((rect->left / format->block_width) * format->block_byte_count);
2859 else
2861 map_desc->data = base_memory
2862 + (map_desc->row_pitch * rect->top)
2863 + (rect->left * format->byte_count);
2865 surface->lockedRect.left = rect->left;
2866 surface->lockedRect.top = rect->top;
2867 surface->lockedRect.right = rect->right;
2868 surface->lockedRect.bottom = rect->bottom;
2871 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2872 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2874 return WINED3D_OK;
2877 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
2879 HRESULT hr;
2881 TRACE("surface %p, dc %p.\n", surface, dc);
2883 /* Give more detailed info for ddraw. */
2884 if (surface->flags & SFLAG_DCINUSE)
2885 return WINEDDERR_DCALREADYCREATED;
2887 /* Can't GetDC if the surface is locked. */
2888 if (surface->resource.map_count)
2889 return WINED3DERR_INVALIDCALL;
2891 /* Create a DIB section if there isn't a dc yet. */
2892 if (!surface->hDC)
2894 if (surface->flags & SFLAG_CLIENT)
2896 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
2897 surface_release_client_storage(surface);
2899 hr = surface_create_dib_section(surface);
2900 if (FAILED(hr))
2901 return WINED3DERR_INVALIDCALL;
2902 if (!(surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2903 || surface->flags & SFLAG_PIN_SYSMEM
2904 || surface->pbo))
2905 surface->resource.map_binding = WINED3D_LOCATION_DIB;
2908 surface_load_location(surface, WINED3D_LOCATION_DIB);
2909 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
2911 surface->flags |= SFLAG_DCINUSE;
2912 surface->resource.map_count++;
2914 *dc = surface->hDC;
2915 TRACE("Returning dc %p.\n", *dc);
2917 return WINED3D_OK;
2920 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
2922 TRACE("surface %p, dc %p.\n", surface, dc);
2924 if (!(surface->flags & SFLAG_DCINUSE))
2925 return WINEDDERR_NODC;
2927 if (surface->hDC != dc)
2929 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
2930 dc, surface->hDC);
2931 return WINEDDERR_NODC;
2934 surface->resource.map_count--;
2935 surface->flags &= ~SFLAG_DCINUSE;
2937 if (surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY || (surface->flags & SFLAG_PIN_SYSMEM
2938 && surface->resource.map_binding != WINED3D_LOCATION_DIB))
2940 /* The game Salammbo modifies the surface contents without mapping the surface between
2941 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
2942 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
2943 * Do not only copy the DIB to the map location, but also make sure the map location is
2944 * copied back to the DIB in the next getdc call.
2946 * The same consideration applies to user memory surfaces. */
2947 surface_load_location(surface, surface->resource.map_binding);
2948 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
2951 return WINED3D_OK;
2954 static void read_from_framebuffer(struct wined3d_surface *surface, DWORD dst_location)
2956 struct wined3d_device *device = surface->resource.device;
2957 const struct wined3d_gl_info *gl_info;
2958 struct wined3d_context *context;
2959 BYTE *mem;
2960 BYTE *row, *top, *bottom;
2961 int i;
2962 BOOL srcIsUpsideDown;
2963 struct wined3d_bo_address data;
2965 surface_get_memory(surface, &data, dst_location);
2967 context = context_acquire(device, surface);
2968 context_apply_blit_state(context, device);
2969 gl_info = context->gl_info;
2971 /* Select the correct read buffer, and give some debug output.
2972 * There is no need to keep track of the current read buffer or reset it, every part of the code
2973 * that reads sets the read buffer as desired.
2975 if (wined3d_resource_is_offscreen(&surface->container->resource))
2977 /* Mapping the primary render target which is not on a swapchain.
2978 * Read from the back buffer. */
2979 TRACE("Mapping offscreen render target.\n");
2980 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2981 srcIsUpsideDown = TRUE;
2983 else
2985 /* Onscreen surfaces are always part of a swapchain */
2986 GLenum buffer = surface_get_gl_buffer(surface);
2987 TRACE("Mapping %#x buffer.\n", buffer);
2988 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2989 checkGLcall("glReadBuffer");
2990 srcIsUpsideDown = FALSE;
2993 if (data.buffer_object)
2995 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
2996 checkGLcall("glBindBufferARB");
2999 /* Setup pixel store pack state -- to glReadPixels into the correct place */
3000 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
3001 checkGLcall("glPixelStorei");
3003 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
3004 surface->resource.width, surface->resource.height,
3005 surface->resource.format->glFormat,
3006 surface->resource.format->glType, data.addr);
3007 checkGLcall("glReadPixels");
3009 /* Reset previous pixel store pack state */
3010 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
3011 checkGLcall("glPixelStorei");
3013 if (!srcIsUpsideDown)
3015 /* glReadPixels returns the image upside down, and there is no way to prevent this.
3016 * Flip the lines in software. */
3017 UINT pitch = wined3d_surface_get_pitch(surface);
3019 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
3020 goto error;
3022 if (data.buffer_object)
3024 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3025 checkGLcall("glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB)");
3027 else
3028 mem = data.addr;
3030 top = mem;
3031 bottom = mem + pitch * (surface->resource.height - 1);
3032 for (i = 0; i < surface->resource.height / 2; i++)
3034 memcpy(row, top, pitch);
3035 memcpy(top, bottom, pitch);
3036 memcpy(bottom, row, pitch);
3037 top += pitch;
3038 bottom -= pitch;
3040 HeapFree(GetProcessHeap(), 0, row);
3042 if (data.buffer_object)
3043 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB));
3046 error:
3047 if (data.buffer_object)
3049 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
3050 checkGLcall("glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0)");
3053 context_release(context);
3056 /* Read the framebuffer contents into a texture. Note that this function
3057 * doesn't do any kind of flipping. Using this on an onscreen surface will
3058 * result in a flipped D3D texture. */
3059 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
3061 struct wined3d_device *device = surface->resource.device;
3062 const struct wined3d_gl_info *gl_info;
3063 struct wined3d_context *context;
3065 context = context_acquire(device, surface);
3066 gl_info = context->gl_info;
3067 device_invalidate_state(device, STATE_FRAMEBUFFER);
3069 surface_prepare_texture(surface, context, srgb);
3070 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3072 TRACE("Reading back offscreen render target %p.\n", surface);
3074 if (wined3d_resource_is_offscreen(&surface->container->resource))
3075 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3076 else
3077 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
3078 checkGLcall("glReadBuffer");
3080 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
3081 0, 0, 0, 0, surface->resource.width, surface->resource.height);
3082 checkGLcall("glCopyTexSubImage2D");
3084 context_release(context);
3087 /* Context activation is done by the caller. */
3088 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
3089 struct wined3d_context *context, BOOL srgb)
3091 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
3092 enum wined3d_conversion_type convert;
3093 struct wined3d_format format;
3095 if (surface->flags & alloc_flag) return;
3097 d3dfmt_get_conv(surface->container, TRUE, &format, &convert);
3098 if (convert != WINED3D_CT_NONE || format.convert)
3099 surface->flags |= SFLAG_CONVERTED;
3100 else surface->flags &= ~SFLAG_CONVERTED;
3102 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3103 surface_allocate_surface(surface, context->gl_info, &format, srgb);
3104 surface->flags |= alloc_flag;
3107 /* Context activation is done by the caller. */
3108 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
3110 struct wined3d_texture *texture = surface->container;
3111 UINT sub_count = texture->level_count * texture->layer_count;
3112 UINT i;
3114 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
3116 for (i = 0; i < sub_count; ++i)
3118 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
3119 surface_prepare_texture_internal(s, context, srgb);
3122 return;
3125 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
3127 if (multisample)
3129 if (surface->rb_multisample)
3130 return;
3132 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
3133 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
3134 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
3135 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
3136 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
3138 else
3140 if (surface->rb_resolved)
3141 return;
3143 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
3144 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
3145 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
3146 surface->pow2Width, surface->pow2Height);
3147 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
3151 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
3153 /* FIXME: Is this really how color keys are supposed to work? I think it
3154 * makes more sense to compare the individual channels. */
3155 return color >= color_key->color_space_low_value
3156 && color <= color_key->color_space_high_value;
3159 static void convert_p8_uint_b8g8r8a8_unorm(const BYTE *src, unsigned int src_pitch,
3160 BYTE *dst, unsigned int dst_pitch, unsigned int width, unsigned int height,
3161 const struct wined3d_palette *palette, const struct wined3d_color_key *color_key)
3163 const BYTE *src_row;
3164 unsigned int x, y;
3165 DWORD *dst_row;
3167 if (!palette)
3169 /* FIXME: This should probably use the system palette. */
3170 FIXME("P8 surface loaded without a palette.\n");
3172 for (y = 0; y < height; ++y)
3174 memset(&dst[dst_pitch * y], 0, width * 4);
3177 return;
3180 for (y = 0; y < height; ++y)
3182 src_row = &src[src_pitch * y];
3183 dst_row = (DWORD *)&dst[dst_pitch * y];
3184 for (x = 0; x < width; ++x)
3186 BYTE src_color = src_row[x];
3187 dst_row[x] = 0xff000000
3188 | (palette->colors[src_color].rgbRed << 16)
3189 | (palette->colors[src_color].rgbGreen << 8)
3190 | palette->colors[src_color].rgbBlue;
3195 static void convert_b5g6r5_unorm_b5g5r5a1_unorm_color_key(const BYTE *src, unsigned int src_pitch,
3196 BYTE *dst, unsigned int dst_pitch, unsigned int width, unsigned int height,
3197 const struct wined3d_color_key *color_key)
3199 const WORD *src_row;
3200 unsigned int x, y;
3201 WORD *dst_row;
3203 for (y = 0; y < height; ++y)
3205 src_row = (WORD *)&src[src_pitch * y];
3206 dst_row = (WORD *)&dst[dst_pitch * y];
3207 for (x = 0; x < width; ++x)
3209 WORD src_color = src_row[x];
3210 if (!color_in_range(color_key, src_color))
3211 dst_row[x] = 0x8000 | ((src_color & 0xffc0) >> 1) | (src_color & 0x1f);
3212 else
3213 dst_row[x] = ((src_color & 0xffc0) >> 1) | (src_color & 0x1f);
3218 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
3219 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
3221 struct wined3d_palette *palette = NULL;
3222 struct wined3d_texture *texture;
3223 const BYTE *source;
3224 unsigned int x, y;
3225 BYTE *dest;
3227 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
3228 src, dst, pitch, width, height, outpitch, conversion_type, surface);
3230 texture = surface->container;
3231 switch (conversion_type)
3233 case WINED3D_CT_P8:
3234 if (texture->swapchain && texture->swapchain->palette)
3235 palette = texture->swapchain->palette;
3236 convert_p8_uint_b8g8r8a8_unorm(src, pitch, dst, outpitch,
3237 width, height, palette, &texture->src_blt_color_key);
3238 break;
3240 case WINED3D_CT_CK_B5G6R5:
3241 convert_b5g6r5_unorm_b5g5r5a1_unorm_color_key(src, pitch, dst, outpitch,
3242 width, height, &texture->src_blt_color_key);
3243 break;
3245 case WINED3D_CT_CK_B5G5R5X1:
3247 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
3248 const WORD *Source;
3249 WORD *Dest;
3250 TRACE("Color keyed 5551\n");
3251 for (y = 0; y < height; y++) {
3252 Source = (const WORD *)(src + y * pitch);
3253 Dest = (WORD *) (dst + y * outpitch);
3254 for (x = 0; x < width; x++ ) {
3255 WORD color = *Source++;
3256 *Dest = color;
3257 if (!color_in_range(&texture->src_blt_color_key, color))
3258 *Dest |= (1 << 15);
3259 else
3260 *Dest &= ~(1 << 15);
3261 Dest++;
3265 break;
3267 case WINED3D_CT_CK_B8G8R8:
3268 for (y = 0; y < height; ++y)
3270 source = src + pitch * y;
3271 dest = dst + outpitch * y;
3272 for (x = 0; x < width; ++x)
3274 DWORD color = ((DWORD)source[2] << 16) | ((DWORD)source[1] << 8) | (DWORD)source[0];
3275 if (!color_in_range(&texture->src_blt_color_key, color))
3276 color |= 0xff000000;
3277 *(DWORD *)dest = color;
3278 source += 3;
3279 dest += 4;
3282 break;
3284 case WINED3D_CT_CK_B8G8R8X8:
3285 for (y = 0; y < height; ++y)
3287 source = src + pitch * y;
3288 dest = dst + outpitch * y;
3289 for (x = 0; x < width; ++x)
3291 DWORD color = *(const DWORD *)source;
3292 if (color_in_range(&texture->src_blt_color_key, color))
3293 *(DWORD *)dest = color & ~0xff000000;
3294 else
3295 *(DWORD *)dest = color | 0xff000000;
3296 source += 4;
3297 dest += 4;
3300 break;
3302 case WINED3D_CT_CK_B8G8R8A8:
3303 for (y = 0; y < height; ++y)
3305 source = src + pitch * y;
3306 dest = dst + outpitch * y;
3307 for (x = 0; x < width; ++x)
3309 DWORD color = *(const DWORD *)source;
3310 if (color_in_range(&texture->src_blt_color_key, color))
3311 color &= ~0xff000000;
3312 *(DWORD*)dest = color;
3313 source += 4;
3314 dest += 4;
3317 break;
3319 default:
3320 ERR("Unsupported conversion type %#x.\n", conversion_type);
3322 return WINED3D_OK;
3325 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
3327 if (front->container->level_count != 1 || front->container->layer_count != 1
3328 || back->container->level_count != 1 || back->container->layer_count != 1)
3329 ERR("Flip between surfaces %p and %p not supported.\n", front, back);
3331 /* Flip the surface contents */
3332 /* Flip the DC */
3334 HDC tmp;
3335 tmp = front->hDC;
3336 front->hDC = back->hDC;
3337 back->hDC = tmp;
3340 /* Flip the DIBsection */
3342 HBITMAP tmp = front->dib.DIBsection;
3343 front->dib.DIBsection = back->dib.DIBsection;
3344 back->dib.DIBsection = tmp;
3347 /* Flip the surface data */
3349 void* tmp;
3351 tmp = front->dib.bitmap_data;
3352 front->dib.bitmap_data = back->dib.bitmap_data;
3353 back->dib.bitmap_data = tmp;
3355 tmp = front->resource.heap_memory;
3356 front->resource.heap_memory = back->resource.heap_memory;
3357 back->resource.heap_memory = tmp;
3360 /* Flip the PBO */
3362 GLuint tmp_pbo = front->pbo;
3363 front->pbo = back->pbo;
3364 back->pbo = tmp_pbo;
3367 /* Flip the opengl texture */
3369 GLuint tmp;
3371 tmp = back->container->texture_rgb.name;
3372 back->container->texture_rgb.name = front->container->texture_rgb.name;
3373 front->container->texture_rgb.name = tmp;
3375 tmp = back->container->texture_srgb.name;
3376 back->container->texture_srgb.name = front->container->texture_srgb.name;
3377 front->container->texture_srgb.name = tmp;
3379 tmp = back->rb_multisample;
3380 back->rb_multisample = front->rb_multisample;
3381 front->rb_multisample = tmp;
3383 tmp = back->rb_resolved;
3384 back->rb_resolved = front->rb_resolved;
3385 front->rb_resolved = tmp;
3387 resource_unload(&back->resource);
3388 resource_unload(&front->resource);
3392 DWORD tmp_flags = back->flags;
3393 back->flags = front->flags;
3394 front->flags = tmp_flags;
3396 tmp_flags = back->locations;
3397 back->locations = front->locations;
3398 front->locations = tmp_flags;
3402 /* Does a direct frame buffer -> texture copy. Stretching is done with single
3403 * pixel copy calls. */
3404 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3405 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3407 struct wined3d_device *device = dst_surface->resource.device;
3408 const struct wined3d_gl_info *gl_info;
3409 float xrel, yrel;
3410 struct wined3d_context *context;
3411 BOOL upsidedown = FALSE;
3412 RECT dst_rect = *dst_rect_in;
3414 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3415 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3417 if(dst_rect.top > dst_rect.bottom) {
3418 UINT tmp = dst_rect.bottom;
3419 dst_rect.bottom = dst_rect.top;
3420 dst_rect.top = tmp;
3421 upsidedown = TRUE;
3424 context = context_acquire(device, src_surface);
3425 gl_info = context->gl_info;
3426 context_apply_blit_state(context, device);
3427 wined3d_texture_load(dst_surface->container, context, FALSE);
3429 /* Bind the target texture */
3430 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
3431 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
3433 TRACE("Reading from an offscreen target\n");
3434 upsidedown = !upsidedown;
3435 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3437 else
3439 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3441 checkGLcall("glReadBuffer");
3443 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3444 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3446 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3448 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3450 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3451 ERR("Texture filtering not supported in direct blit.\n");
3453 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3454 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3456 ERR("Texture filtering not supported in direct blit\n");
3459 if (upsidedown
3460 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3461 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3463 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
3464 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3465 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3466 src_rect->left, src_surface->resource.height - src_rect->bottom,
3467 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3469 else
3471 LONG row;
3472 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
3473 /* I have to process this row by row to swap the image,
3474 * otherwise it would be upside down, so stretching in y direction
3475 * doesn't cost extra time
3477 * However, stretching in x direction can be avoided if not necessary
3479 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3480 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3482 /* Well, that stuff works, but it's very slow.
3483 * find a better way instead
3485 LONG col;
3487 for (col = dst_rect.left; col < dst_rect.right; ++col)
3489 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3490 dst_rect.left + col /* x offset */, row /* y offset */,
3491 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3494 else
3496 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3497 dst_rect.left /* x offset */, row /* y offset */,
3498 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3502 checkGLcall("glCopyTexSubImage2D");
3504 context_release(context);
3506 /* The texture is now most up to date - If the surface is a render target
3507 * and has a drawable, this path is never entered. */
3508 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3509 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3512 /* Uses the hardware to stretch and flip the image */
3513 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3514 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3516 struct wined3d_device *device = dst_surface->resource.device;
3517 GLuint src, backup = 0;
3518 float left, right, top, bottom; /* Texture coordinates */
3519 UINT fbwidth = src_surface->resource.width;
3520 UINT fbheight = src_surface->resource.height;
3521 const struct wined3d_gl_info *gl_info;
3522 struct wined3d_context *context;
3523 GLenum drawBuffer = GL_BACK;
3524 GLenum texture_target;
3525 BOOL noBackBufferBackup;
3526 BOOL src_offscreen;
3527 BOOL upsidedown = FALSE;
3528 RECT dst_rect = *dst_rect_in;
3530 TRACE("Using hwstretch blit\n");
3531 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3532 context = context_acquire(device, src_surface);
3533 gl_info = context->gl_info;
3534 context_apply_blit_state(context, device);
3535 wined3d_texture_load(dst_surface->container, context, FALSE);
3537 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
3538 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3539 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3541 /* Get it a description */
3542 wined3d_texture_load(src_surface->container, context, FALSE);
3545 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3546 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3548 if (context->aux_buffers >= 2)
3550 /* Got more than one aux buffer? Use the 2nd aux buffer */
3551 drawBuffer = GL_AUX1;
3553 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3555 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3556 drawBuffer = GL_AUX0;
3559 if (noBackBufferBackup)
3561 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3562 checkGLcall("glGenTextures");
3563 context_bind_texture(context, GL_TEXTURE_2D, backup);
3564 texture_target = GL_TEXTURE_2D;
3566 else
3568 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3569 * we are reading from the back buffer, the backup can be used as source texture
3571 texture_target = src_surface->texture_target;
3572 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3573 gl_info->gl_ops.gl.p_glEnable(texture_target);
3574 checkGLcall("glEnable(texture_target)");
3576 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3577 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3580 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3581 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3583 if(dst_rect.top > dst_rect.bottom) {
3584 UINT tmp = dst_rect.bottom;
3585 dst_rect.bottom = dst_rect.top;
3586 dst_rect.top = tmp;
3587 upsidedown = TRUE;
3590 if (src_offscreen)
3592 TRACE("Reading from an offscreen target\n");
3593 upsidedown = !upsidedown;
3594 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3596 else
3598 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3601 /* TODO: Only back up the part that will be overwritten */
3602 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3604 checkGLcall("glCopyTexSubImage2D");
3606 /* No issue with overriding these - the sampler is dirty due to blit usage */
3607 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3608 wined3d_gl_mag_filter(magLookup, filter));
3609 checkGLcall("glTexParameteri");
3610 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3611 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
3612 checkGLcall("glTexParameteri");
3614 if (!src_surface->container->swapchain
3615 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
3617 src = backup ? backup : src_surface->container->texture_rgb.name;
3619 else
3621 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3622 checkGLcall("glReadBuffer(GL_FRONT)");
3624 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3625 checkGLcall("glGenTextures(1, &src)");
3626 context_bind_texture(context, GL_TEXTURE_2D, src);
3628 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3629 * out for power of 2 sizes
3631 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3632 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3633 checkGLcall("glTexImage2D");
3634 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3636 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3637 checkGLcall("glTexParameteri");
3638 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3639 checkGLcall("glTexParameteri");
3641 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3642 checkGLcall("glReadBuffer(GL_BACK)");
3644 if (texture_target != GL_TEXTURE_2D)
3646 gl_info->gl_ops.gl.p_glDisable(texture_target);
3647 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3648 texture_target = GL_TEXTURE_2D;
3651 checkGLcall("glEnd and previous");
3653 left = src_rect->left;
3654 right = src_rect->right;
3656 if (!upsidedown)
3658 top = src_surface->resource.height - src_rect->top;
3659 bottom = src_surface->resource.height - src_rect->bottom;
3661 else
3663 top = src_surface->resource.height - src_rect->bottom;
3664 bottom = src_surface->resource.height - src_rect->top;
3667 if (src_surface->flags & SFLAG_NORMCOORD)
3669 left /= src_surface->pow2Width;
3670 right /= src_surface->pow2Width;
3671 top /= src_surface->pow2Height;
3672 bottom /= src_surface->pow2Height;
3675 /* draw the source texture stretched and upside down. The correct surface is bound already */
3676 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3677 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3679 context_set_draw_buffer(context, drawBuffer);
3680 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3682 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3683 /* bottom left */
3684 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3685 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3687 /* top left */
3688 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3689 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3691 /* top right */
3692 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3693 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3695 /* bottom right */
3696 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3697 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3698 gl_info->gl_ops.gl.p_glEnd();
3699 checkGLcall("glEnd and previous");
3701 if (texture_target != dst_surface->texture_target)
3703 gl_info->gl_ops.gl.p_glDisable(texture_target);
3704 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3705 texture_target = dst_surface->texture_target;
3708 /* Now read the stretched and upside down image into the destination texture */
3709 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
3710 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
3712 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3713 0, 0, /* We blitted the image to the origin */
3714 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3715 checkGLcall("glCopyTexSubImage2D");
3717 if (drawBuffer == GL_BACK)
3719 /* Write the back buffer backup back. */
3720 if (backup)
3722 if (texture_target != GL_TEXTURE_2D)
3724 gl_info->gl_ops.gl.p_glDisable(texture_target);
3725 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3726 texture_target = GL_TEXTURE_2D;
3728 context_bind_texture(context, GL_TEXTURE_2D, backup);
3730 else
3732 if (texture_target != src_surface->texture_target)
3734 gl_info->gl_ops.gl.p_glDisable(texture_target);
3735 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3736 texture_target = src_surface->texture_target;
3738 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3741 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3742 /* top left */
3743 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3744 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3746 /* bottom left */
3747 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3748 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3750 /* bottom right */
3751 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3752 (float)fbheight / (float)src_surface->pow2Height);
3753 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3755 /* top right */
3756 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3757 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3758 gl_info->gl_ops.gl.p_glEnd();
3760 gl_info->gl_ops.gl.p_glDisable(texture_target);
3761 checkGLcall("glDisable(texture_target)");
3763 /* Cleanup */
3764 if (src != src_surface->container->texture_rgb.name && src != backup)
3766 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3767 checkGLcall("glDeleteTextures(1, &src)");
3769 if (backup)
3771 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3772 checkGLcall("glDeleteTextures(1, &backup)");
3775 if (wined3d_settings.strict_draw_ordering)
3776 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3778 context_release(context);
3780 /* The texture is now most up to date - If the surface is a render target
3781 * and has a drawable, this path is never entered. */
3782 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3783 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3786 /* Front buffer coordinates are always full screen coordinates, but our GL
3787 * drawable is limited to the window's client area. The sysmem and texture
3788 * copies do have the full screen size. Note that GL has a bottom-left
3789 * origin, while D3D has a top-left origin. */
3790 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3792 UINT drawable_height;
3794 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3796 POINT offset = {0, 0};
3797 RECT windowsize;
3799 ScreenToClient(window, &offset);
3800 OffsetRect(rect, offset.x, offset.y);
3802 GetClientRect(window, &windowsize);
3803 drawable_height = windowsize.bottom - windowsize.top;
3805 else
3807 drawable_height = surface->resource.height;
3810 rect->top = drawable_height - rect->top;
3811 rect->bottom = drawable_height - rect->bottom;
3814 static void surface_blt_to_drawable(const struct wined3d_device *device,
3815 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3816 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3817 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3819 const struct wined3d_gl_info *gl_info;
3820 struct wined3d_context *context;
3821 RECT src_rect, dst_rect;
3823 src_rect = *src_rect_in;
3824 dst_rect = *dst_rect_in;
3826 context = context_acquire(device, dst_surface);
3827 gl_info = context->gl_info;
3829 /* Make sure the surface is up-to-date. This should probably use
3830 * surface_load_location() and worry about the destination surface too,
3831 * unless we're overwriting it completely. */
3832 wined3d_texture_load(src_surface->container, context, FALSE);
3834 /* Activate the destination context, set it up for blitting */
3835 context_apply_blit_state(context, device);
3837 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3838 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3840 device->blitter->set_shader(device->blit_priv, context, src_surface);
3842 if (alpha_test)
3844 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3845 checkGLcall("glEnable(GL_ALPHA_TEST)");
3847 /* For P8 surfaces, the alpha component contains the palette index.
3848 * Which means that the colorkey is one of the palette entries. In
3849 * other cases pixels that should be masked away have alpha set to 0. */
3850 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3851 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3852 (float)src_surface->container->src_blt_color_key.color_space_low_value / 256.0f);
3853 else
3854 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3855 checkGLcall("glAlphaFunc");
3857 else
3859 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3860 checkGLcall("glDisable(GL_ALPHA_TEST)");
3863 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3865 if (alpha_test)
3867 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3868 checkGLcall("glDisable(GL_ALPHA_TEST)");
3871 /* Leave the opengl state valid for blitting */
3872 device->blitter->unset_shader(context->gl_info);
3874 if (wined3d_settings.strict_draw_ordering
3875 || (dst_surface->container->swapchain
3876 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3877 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3879 context_release(context);
3882 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3884 struct wined3d_device *device = s->resource.device;
3885 const struct blit_shader *blitter;
3887 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
3888 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
3889 if (!blitter)
3891 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3892 return WINED3DERR_INVALIDCALL;
3895 return blitter->color_fill(device, s, rect, color);
3898 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3899 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3900 enum wined3d_texture_filter_type filter)
3902 struct wined3d_device *device = dst_surface->resource.device;
3903 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3904 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3905 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3907 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3908 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3909 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3911 /* Get the swapchain. One of the surfaces has to be a primary surface */
3912 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3914 WARN("Destination is in sysmem, rejecting gl blt\n");
3915 return WINED3DERR_INVALIDCALL;
3918 dst_swapchain = dst_surface->container->swapchain;
3920 if (src_surface)
3922 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3924 WARN("Src is in sysmem, rejecting gl blt\n");
3925 return WINED3DERR_INVALIDCALL;
3928 src_swapchain = src_surface->container->swapchain;
3930 else
3932 src_swapchain = NULL;
3935 /* Early sort out of cases where no render target is used */
3936 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
3938 TRACE("No surface is render target, not using hardware blit.\n");
3939 return WINED3DERR_INVALIDCALL;
3942 /* No destination color keying supported */
3943 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
3945 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3946 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3947 return WINED3DERR_INVALIDCALL;
3950 if (dst_swapchain && dst_swapchain == src_swapchain)
3952 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3953 return WINED3DERR_INVALIDCALL;
3956 if (dst_swapchain && src_swapchain)
3958 FIXME("Implement hardware blit between two different swapchains\n");
3959 return WINED3DERR_INVALIDCALL;
3962 if (dst_swapchain)
3964 /* Handled with regular texture -> swapchain blit */
3965 if (src_surface == rt)
3966 TRACE("Blit from active render target to a swapchain\n");
3968 else if (src_swapchain && dst_surface == rt)
3970 FIXME("Implement blit from a swapchain to the active render target\n");
3971 return WINED3DERR_INVALIDCALL;
3974 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
3976 /* Blit from render target to texture */
3977 BOOL stretchx;
3979 /* P8 read back is not implemented */
3980 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
3981 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
3983 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3984 return WINED3DERR_INVALIDCALL;
3987 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3989 TRACE("Color keying not supported by frame buffer to texture blit\n");
3990 return WINED3DERR_INVALIDCALL;
3991 /* Destination color key is checked above */
3994 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
3995 stretchx = TRUE;
3996 else
3997 stretchx = FALSE;
3999 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
4000 * flip the image nor scale it.
4002 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
4003 * -> If the app wants an image width an unscaled width, copy it line per line
4004 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
4005 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
4006 * back buffer. This is slower than reading line per line, thus not used for flipping
4007 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
4008 * pixel by pixel. */
4009 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
4010 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
4012 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
4013 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
4015 else
4017 TRACE("Using hardware stretching to flip / stretch the texture.\n");
4018 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
4021 surface_evict_sysmem(dst_surface);
4023 return WINED3D_OK;
4025 else if (src_surface)
4027 /* Blit from offscreen surface to render target */
4028 struct wined3d_color_key old_blt_key = src_surface->container->src_blt_color_key;
4029 DWORD old_color_key_flags = src_surface->container->color_key_flags;
4031 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4033 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4034 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
4035 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
4037 FIXME("Unsupported blit operation falling back to software\n");
4038 return WINED3DERR_INVALIDCALL;
4041 /* Color keying: Check if we have to do a color keyed blt,
4042 * and if not check if a color key is activated.
4044 * Just modify the color keying parameters in the surface and restore them afterwards
4045 * The surface keeps track of the color key last used to load the opengl surface.
4046 * PreLoad will catch the change to the flags and color key and reload if necessary.
4048 if (flags & WINEDDBLT_KEYSRC)
4050 /* Use color key from surface */
4052 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4054 /* Use color key from DDBltFx */
4055 wined3d_texture_set_color_key(src_surface->container, WINEDDSD_CKSRCBLT, &DDBltFx->ddckSrcColorkey);
4057 else
4059 /* Do not use color key */
4060 wined3d_texture_set_color_key(src_surface->container, WINEDDSD_CKSRCBLT, NULL);
4063 surface_blt_to_drawable(device, filter,
4064 flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_ALPHATEST),
4065 src_surface, src_rect, dst_surface, dst_rect);
4067 /* Restore the color key parameters */
4068 wined3d_texture_set_color_key(src_surface->container, WINEDDSD_CKSRCBLT,
4069 (old_color_key_flags & WINEDDSD_CKSRCBLT) ? &old_blt_key : NULL);
4071 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
4072 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
4074 return WINED3D_OK;
4077 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
4078 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
4079 return WINED3DERR_INVALIDCALL;
4082 /* Context activation is done by the caller. */
4083 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
4084 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
4086 struct wined3d_device *device = surface->resource.device;
4087 const struct wined3d_gl_info *gl_info = context->gl_info;
4088 GLint compare_mode = GL_NONE;
4089 struct blt_info info;
4090 GLint old_binding = 0;
4091 RECT rect;
4093 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4095 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
4096 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
4097 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4098 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
4099 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
4100 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
4101 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
4102 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
4103 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4104 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
4105 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
4107 SetRect(&rect, 0, h, w, 0);
4108 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
4109 context_active_texture(context, context->gl_info, 0);
4110 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
4111 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
4112 if (gl_info->supported[ARB_SHADOW])
4114 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
4115 if (compare_mode != GL_NONE)
4116 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
4119 device->shader_backend->shader_select_depth_blt(device->shader_priv,
4120 gl_info, info.tex_type, &surface->ds_current_size);
4122 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
4123 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
4124 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
4125 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
4126 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
4127 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
4128 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
4129 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
4130 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
4131 gl_info->gl_ops.gl.p_glEnd();
4133 if (compare_mode != GL_NONE)
4134 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
4135 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
4137 gl_info->gl_ops.gl.p_glPopAttrib();
4139 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
4142 void surface_modify_ds_location(struct wined3d_surface *surface,
4143 DWORD location, UINT w, UINT h)
4145 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
4147 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
4148 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
4149 wined3d_texture_set_dirty(surface->container);
4151 surface->ds_current_size.cx = w;
4152 surface->ds_current_size.cy = h;
4153 surface->locations = location;
4156 /* Context activation is done by the caller. */
4157 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4159 const struct wined3d_gl_info *gl_info = context->gl_info;
4160 struct wined3d_device *device = surface->resource.device;
4161 GLsizei w, h;
4163 TRACE("surface %p, new location %#x.\n", surface, location);
4165 /* TODO: Make this work for modes other than FBO */
4166 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4168 if (!(surface->locations & location))
4170 w = surface->ds_current_size.cx;
4171 h = surface->ds_current_size.cy;
4172 surface->ds_current_size.cx = 0;
4173 surface->ds_current_size.cy = 0;
4175 else
4177 w = surface->resource.width;
4178 h = surface->resource.height;
4181 if (surface->ds_current_size.cx == surface->resource.width
4182 && surface->ds_current_size.cy == surface->resource.height)
4184 TRACE("Location (%#x) is already up to date.\n", location);
4185 return;
4188 if (surface->current_renderbuffer)
4190 FIXME("Not supported with fixed up depth stencil.\n");
4191 return;
4194 if (surface->locations & WINED3D_LOCATION_DISCARDED)
4196 TRACE("Surface was discarded, no need copy data.\n");
4197 switch (location)
4199 case WINED3D_LOCATION_TEXTURE_RGB:
4200 surface_prepare_texture(surface, context, FALSE);
4201 break;
4202 case WINED3D_LOCATION_RB_MULTISAMPLE:
4203 surface_prepare_rb(surface, gl_info, TRUE);
4204 break;
4205 case WINED3D_LOCATION_DRAWABLE:
4206 /* Nothing to do */
4207 break;
4208 default:
4209 FIXME("Unhandled location %#x\n", location);
4211 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
4212 surface->locations |= location;
4213 surface->ds_current_size.cx = surface->resource.width;
4214 surface->ds_current_size.cy = surface->resource.height;
4215 return;
4218 if (!surface->locations)
4220 FIXME("No up to date depth stencil location.\n");
4221 surface->locations |= location;
4222 surface->ds_current_size.cx = surface->resource.width;
4223 surface->ds_current_size.cy = surface->resource.height;
4224 return;
4227 if (location == WINED3D_LOCATION_TEXTURE_RGB)
4229 GLint old_binding = 0;
4230 GLenum bind_target;
4232 /* The render target is allowed to be smaller than the depth/stencil
4233 * buffer, so the onscreen depth/stencil buffer is potentially smaller
4234 * than the offscreen surface. Don't overwrite the offscreen surface
4235 * with undefined data. */
4236 w = min(w, context->swapchain->desc.backbuffer_width);
4237 h = min(h, context->swapchain->desc.backbuffer_height);
4239 TRACE("Copying onscreen depth buffer to depth texture.\n");
4241 if (!device->depth_blt_texture)
4242 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
4244 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4245 * directly on the FBO texture. That's because we need to flip. */
4246 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4247 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
4248 NULL, WINED3D_LOCATION_DRAWABLE);
4249 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4251 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4252 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4254 else
4256 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4257 bind_target = GL_TEXTURE_2D;
4259 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
4260 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
4261 * internal format, because the internal format might include stencil
4262 * data. In principle we should copy stencil data as well, but unless
4263 * the driver supports stencil export it's hard to do, and doesn't
4264 * seem to be needed in practice. If the hardware doesn't support
4265 * writing stencil data, the glCopyTexImage2D() call might trigger
4266 * software fallbacks. */
4267 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
4268 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4269 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4270 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4271 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4272 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4273 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4274 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
4276 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4277 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
4278 context_set_draw_buffer(context, GL_NONE);
4280 /* Do the actual blit */
4281 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
4282 checkGLcall("depth_blt");
4284 context_invalidate_state(context, STATE_FRAMEBUFFER);
4286 if (wined3d_settings.strict_draw_ordering)
4287 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4289 else if (location == WINED3D_LOCATION_DRAWABLE)
4291 TRACE("Copying depth texture to onscreen depth buffer.\n");
4293 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4294 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
4295 NULL, WINED3D_LOCATION_DRAWABLE);
4296 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
4297 0, surface->pow2Height - h, w, h, surface->texture_target);
4298 checkGLcall("depth_blt");
4300 context_invalidate_state(context, STATE_FRAMEBUFFER);
4302 if (wined3d_settings.strict_draw_ordering)
4303 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4305 else
4307 ERR("Invalid location (%#x) specified.\n", location);
4310 surface->locations |= location;
4311 surface->ds_current_size.cx = surface->resource.width;
4312 surface->ds_current_size.cy = surface->resource.height;
4315 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
4317 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4319 surface->locations |= location;
4322 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
4324 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4326 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4327 wined3d_texture_set_dirty(surface->container);
4328 surface->locations &= ~location;
4330 if (!surface->locations)
4331 ERR("Surface %p does not have any up to date location.\n", surface);
4334 static DWORD resource_access_from_location(DWORD location)
4336 switch (location)
4338 case WINED3D_LOCATION_SYSMEM:
4339 case WINED3D_LOCATION_USER_MEMORY:
4340 case WINED3D_LOCATION_DIB:
4341 case WINED3D_LOCATION_BUFFER:
4342 return WINED3D_RESOURCE_ACCESS_CPU;
4344 case WINED3D_LOCATION_DRAWABLE:
4345 case WINED3D_LOCATION_TEXTURE_SRGB:
4346 case WINED3D_LOCATION_TEXTURE_RGB:
4347 case WINED3D_LOCATION_RB_MULTISAMPLE:
4348 case WINED3D_LOCATION_RB_RESOLVED:
4349 return WINED3D_RESOURCE_ACCESS_GPU;
4351 default:
4352 FIXME("Unhandled location %#x.\n", location);
4353 return 0;
4357 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
4359 struct wined3d_device *device = surface->resource.device;
4360 struct wined3d_context *context;
4361 const struct wined3d_gl_info *gl_info;
4362 struct wined3d_bo_address dst, src;
4363 UINT size = surface->resource.size;
4365 surface_get_memory(surface, &dst, location);
4366 surface_get_memory(surface, &src, surface->locations);
4368 if (dst.buffer_object)
4370 context = context_acquire(device, NULL);
4371 gl_info = context->gl_info;
4372 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, dst.buffer_object));
4373 GL_EXTCALL(glBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, size, src.addr));
4374 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4375 checkGLcall("Upload PBO");
4376 context_release(context);
4377 return;
4379 if (src.buffer_object)
4381 context = context_acquire(device, NULL);
4382 gl_info = context->gl_info;
4383 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, src.buffer_object));
4384 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_PACK_BUFFER_ARB, 0, size, dst.addr));
4385 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4386 checkGLcall("Download PBO");
4387 context_release(context);
4388 return;
4390 memcpy(dst.addr, src.addr, size);
4393 static void surface_load_sysmem(struct wined3d_surface *surface,
4394 const struct wined3d_gl_info *gl_info, DWORD dst_location)
4396 if (surface->locations & surface_simple_locations)
4398 surface_copy_simple_location(surface, dst_location);
4399 return;
4402 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
4403 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4405 /* Download the surface to system memory. */
4406 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4408 struct wined3d_device *device = surface->resource.device;
4409 struct wined3d_context *context;
4411 /* TODO: Use already acquired context when possible. */
4412 context = context_acquire(device, NULL);
4414 wined3d_texture_bind_and_dirtify(surface->container, context,
4415 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
4416 surface_download_data(surface, gl_info, dst_location);
4418 context_release(context);
4420 return;
4423 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
4425 read_from_framebuffer(surface, dst_location);
4426 return;
4429 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
4430 surface, wined3d_debug_location(surface->locations));
4433 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
4434 const struct wined3d_gl_info *gl_info)
4436 RECT r;
4438 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
4439 && wined3d_resource_is_offscreen(&surface->container->resource))
4441 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
4442 return WINED3DERR_INVALIDCALL;
4445 surface_get_rect(surface, NULL, &r);
4446 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4447 surface_blt_to_drawable(surface->resource.device,
4448 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
4450 return WINED3D_OK;
4453 static HRESULT surface_load_texture(struct wined3d_surface *surface,
4454 const struct wined3d_gl_info *gl_info, BOOL srgb)
4456 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
4457 struct wined3d_device *device = surface->resource.device;
4458 enum wined3d_conversion_type convert;
4459 struct wined3d_context *context;
4460 UINT width, src_pitch, dst_pitch;
4461 struct wined3d_bo_address data;
4462 struct wined3d_format format;
4463 POINT dst_point = {0, 0};
4464 BYTE *mem = NULL;
4466 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
4467 && wined3d_resource_is_offscreen(&surface->container->resource)
4468 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
4470 surface_load_fb_texture(surface, srgb);
4472 return WINED3D_OK;
4475 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
4476 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
4477 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4478 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4479 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4481 if (srgb)
4482 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
4483 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
4484 else
4485 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
4486 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
4488 return WINED3D_OK;
4491 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
4492 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
4493 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4494 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4495 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4497 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
4498 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
4499 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
4500 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4502 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
4503 &rect, surface, dst_location, &rect);
4505 return WINED3D_OK;
4508 /* Upload from system memory */
4510 d3dfmt_get_conv(surface->container, TRUE /* We need color keying */, &format, &convert);
4512 if (srgb)
4514 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
4515 == WINED3D_LOCATION_TEXTURE_RGB)
4517 /* Performance warning... */
4518 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
4519 surface_prepare_map_memory(surface);
4520 surface_load_location(surface, surface->resource.map_binding);
4523 else
4525 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
4526 == WINED3D_LOCATION_TEXTURE_SRGB)
4528 /* Performance warning... */
4529 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
4530 surface_prepare_map_memory(surface);
4531 surface_load_location(surface, surface->resource.map_binding);
4535 if (!(surface->locations & surface_simple_locations))
4537 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
4538 /* Lets hope we get it from somewhere... */
4539 surface_prepare_system_memory(surface);
4540 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
4543 /* TODO: Use already acquired context when possible. */
4544 context = context_acquire(device, NULL);
4546 surface_prepare_texture(surface, context, srgb);
4547 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
4549 if (surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
4551 surface->flags |= SFLAG_GLCKEY;
4552 surface->gl_color_key = surface->container->src_blt_color_key;
4554 else surface->flags &= ~SFLAG_GLCKEY;
4556 width = surface->resource.width;
4557 src_pitch = wined3d_surface_get_pitch(surface);
4559 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
4560 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
4561 * called. */
4562 if ((convert != WINED3D_CT_NONE || format.convert) && surface->pbo)
4564 TRACE("Removing the pbo attached to surface %p.\n", surface);
4566 if (surface->flags & SFLAG_DIBSECTION)
4567 surface->resource.map_binding = WINED3D_LOCATION_DIB;
4568 else
4569 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
4571 surface_prepare_map_memory(surface);
4572 surface_load_location(surface, surface->resource.map_binding);
4573 surface_remove_pbo(surface, gl_info);
4576 surface_get_memory(surface, &data, surface->locations);
4577 if (format.convert)
4579 /* This code is entered for texture formats which need a fixup. */
4580 UINT height = surface->resource.height;
4582 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4583 dst_pitch = width * format.conv_byte_count;
4584 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4586 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4588 ERR("Out of memory (%u).\n", dst_pitch * height);
4589 context_release(context);
4590 return E_OUTOFMEMORY;
4592 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4593 dst_pitch, dst_pitch * height, width, height, 1);
4594 format.byte_count = format.conv_byte_count;
4595 src_pitch = dst_pitch;
4596 data.addr = mem;
4598 else if (convert != WINED3D_CT_NONE)
4600 /* This code is only entered for color keying fixups */
4601 UINT height = surface->resource.height;
4603 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4604 dst_pitch = width * format.conv_byte_count;
4605 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4607 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4609 ERR("Out of memory (%u).\n", dst_pitch * height);
4610 context_release(context);
4611 return E_OUTOFMEMORY;
4613 d3dfmt_convert_surface(data.addr, mem, src_pitch,
4614 width, height, dst_pitch, convert, surface);
4615 format.byte_count = format.conv_byte_count;
4616 src_pitch = dst_pitch;
4617 data.addr = mem;
4620 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
4622 context_release(context);
4624 HeapFree(GetProcessHeap(), 0, mem);
4626 return WINED3D_OK;
4629 static void surface_multisample_resolve(struct wined3d_surface *surface)
4631 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4633 if (!(surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE))
4634 ERR("Trying to resolve multisampled surface %p, but location WINED3D_LOCATION_RB_MULTISAMPLE not current.\n",
4635 surface);
4637 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
4638 surface, WINED3D_LOCATION_RB_MULTISAMPLE, &rect, surface, WINED3D_LOCATION_RB_RESOLVED, &rect);
4641 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
4643 struct wined3d_device *device = surface->resource.device;
4644 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4645 HRESULT hr;
4647 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4649 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4651 if (location == WINED3D_LOCATION_TEXTURE_RGB
4652 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4654 struct wined3d_context *context = context_acquire(device, NULL);
4655 surface_load_ds_location(surface, context, location);
4656 context_release(context);
4657 return WINED3D_OK;
4659 else if (location & surface->locations
4660 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
4662 /* Already up to date, nothing to do. */
4663 return WINED3D_OK;
4665 else
4667 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4668 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4669 return WINED3DERR_INVALIDCALL;
4673 if (surface->locations & location)
4675 TRACE("Location already up to date.\n");
4676 return WINED3D_OK;
4679 if (WARN_ON(d3d_surface))
4681 DWORD required_access = resource_access_from_location(location);
4682 if ((surface->resource.access_flags & required_access) != required_access)
4683 WARN("Operation requires %#x access, but surface only has %#x.\n",
4684 required_access, surface->resource.access_flags);
4687 if (!surface->locations)
4689 ERR("Surface %p does not have any up to date location.\n", surface);
4690 surface->flags |= SFLAG_LOST;
4691 return WINED3DERR_DEVICELOST;
4694 switch (location)
4696 case WINED3D_LOCATION_DIB:
4697 case WINED3D_LOCATION_USER_MEMORY:
4698 case WINED3D_LOCATION_SYSMEM:
4699 case WINED3D_LOCATION_BUFFER:
4700 surface_load_sysmem(surface, gl_info, location);
4701 break;
4703 case WINED3D_LOCATION_DRAWABLE:
4704 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
4705 return hr;
4706 break;
4708 case WINED3D_LOCATION_RB_RESOLVED:
4709 surface_multisample_resolve(surface);
4710 break;
4712 case WINED3D_LOCATION_TEXTURE_RGB:
4713 case WINED3D_LOCATION_TEXTURE_SRGB:
4714 if (FAILED(hr = surface_load_texture(surface, gl_info, location == WINED3D_LOCATION_TEXTURE_SRGB)))
4715 return hr;
4716 break;
4718 default:
4719 ERR("Don't know how to handle location %#x.\n", location);
4720 break;
4723 surface_validate_location(surface, location);
4725 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
4726 surface_evict_sysmem(surface);
4728 return WINED3D_OK;
4731 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
4732 /* Context activation is done by the caller. */
4733 static void ffp_blit_free(struct wined3d_device *device) { }
4735 /* Context activation is done by the caller. */
4736 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4738 const struct wined3d_gl_info *gl_info = context->gl_info;
4740 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
4741 checkGLcall("glEnable(target)");
4743 return WINED3D_OK;
4746 /* Context activation is done by the caller. */
4747 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
4749 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
4750 checkGLcall("glDisable(GL_TEXTURE_2D)");
4751 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4753 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4754 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4756 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4758 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
4759 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4763 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4764 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4765 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4767 switch (blit_op)
4769 case WINED3D_BLIT_OP_COLOR_BLIT:
4770 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4771 return FALSE;
4773 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4775 TRACE("Checking support for fixup:\n");
4776 dump_color_fixup_desc(src_format->color_fixup);
4779 /* We only support identity conversions. */
4780 if (!is_identity_fixup(src_format->color_fixup)
4781 || !is_identity_fixup(dst_format->color_fixup))
4783 TRACE("Fixups are not supported.\n");
4784 return FALSE;
4787 return TRUE;
4789 case WINED3D_BLIT_OP_COLOR_FILL:
4790 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
4791 return FALSE;
4793 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4795 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4796 return FALSE;
4798 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4800 TRACE("Color fill not supported\n");
4801 return FALSE;
4804 /* FIXME: We should reject color fills on formats with fixups,
4805 * but this would break P8 color fills for example. */
4807 return TRUE;
4809 case WINED3D_BLIT_OP_DEPTH_FILL:
4810 return TRUE;
4812 default:
4813 TRACE("Unsupported blit_op=%d\n", blit_op);
4814 return FALSE;
4818 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4819 const RECT *dst_rect, const struct wined3d_color *color)
4821 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4822 struct wined3d_rendertarget_view *view;
4823 struct wined3d_fb_state fb = {&view, NULL};
4824 HRESULT hr;
4826 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4827 NULL, &wined3d_null_parent_ops, &view)))
4829 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4830 return hr;
4833 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4834 wined3d_rendertarget_view_decref(view);
4836 return WINED3D_OK;
4839 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4840 const RECT *dst_rect, float depth)
4842 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4843 struct wined3d_fb_state fb = {NULL, NULL};
4844 HRESULT hr;
4846 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4847 NULL, &wined3d_null_parent_ops, &fb.depth_stencil)))
4849 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4850 return hr;
4853 device_clear_render_targets(device, 0, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4854 wined3d_rendertarget_view_decref(fb.depth_stencil);
4856 return WINED3D_OK;
4859 const struct blit_shader ffp_blit = {
4860 ffp_blit_alloc,
4861 ffp_blit_free,
4862 ffp_blit_set,
4863 ffp_blit_unset,
4864 ffp_blit_supported,
4865 ffp_blit_color_fill,
4866 ffp_blit_depth_fill,
4869 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4871 return WINED3D_OK;
4874 /* Context activation is done by the caller. */
4875 static void cpu_blit_free(struct wined3d_device *device)
4879 /* Context activation is done by the caller. */
4880 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4882 return WINED3D_OK;
4885 /* Context activation is done by the caller. */
4886 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4890 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4891 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4892 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4894 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4896 return TRUE;
4899 return FALSE;
4902 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4903 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4904 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4906 UINT row_block_count;
4907 const BYTE *src_row;
4908 BYTE *dst_row;
4909 UINT x, y;
4911 src_row = src_data;
4912 dst_row = dst_data;
4914 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4916 if (!flags)
4918 for (y = 0; y < update_h; y += format->block_height)
4920 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4921 src_row += src_pitch;
4922 dst_row += dst_pitch;
4925 return WINED3D_OK;
4928 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4930 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4932 switch (format->id)
4934 case WINED3DFMT_DXT1:
4935 for (y = 0; y < update_h; y += format->block_height)
4937 struct block
4939 WORD color[2];
4940 BYTE control_row[4];
4943 const struct block *s = (const struct block *)src_row;
4944 struct block *d = (struct block *)dst_row;
4946 for (x = 0; x < row_block_count; ++x)
4948 d[x].color[0] = s[x].color[0];
4949 d[x].color[1] = s[x].color[1];
4950 d[x].control_row[0] = s[x].control_row[3];
4951 d[x].control_row[1] = s[x].control_row[2];
4952 d[x].control_row[2] = s[x].control_row[1];
4953 d[x].control_row[3] = s[x].control_row[0];
4955 src_row -= src_pitch;
4956 dst_row += dst_pitch;
4958 return WINED3D_OK;
4960 case WINED3DFMT_DXT2:
4961 case WINED3DFMT_DXT3:
4962 for (y = 0; y < update_h; y += format->block_height)
4964 struct block
4966 WORD alpha_row[4];
4967 WORD color[2];
4968 BYTE control_row[4];
4971 const struct block *s = (const struct block *)src_row;
4972 struct block *d = (struct block *)dst_row;
4974 for (x = 0; x < row_block_count; ++x)
4976 d[x].alpha_row[0] = s[x].alpha_row[3];
4977 d[x].alpha_row[1] = s[x].alpha_row[2];
4978 d[x].alpha_row[2] = s[x].alpha_row[1];
4979 d[x].alpha_row[3] = s[x].alpha_row[0];
4980 d[x].color[0] = s[x].color[0];
4981 d[x].color[1] = s[x].color[1];
4982 d[x].control_row[0] = s[x].control_row[3];
4983 d[x].control_row[1] = s[x].control_row[2];
4984 d[x].control_row[2] = s[x].control_row[1];
4985 d[x].control_row[3] = s[x].control_row[0];
4987 src_row -= src_pitch;
4988 dst_row += dst_pitch;
4990 return WINED3D_OK;
4992 default:
4993 FIXME("Compressed flip not implemented for format %s.\n",
4994 debug_d3dformat(format->id));
4995 return E_NOTIMPL;
4999 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
5000 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
5002 return E_NOTIMPL;
5005 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5006 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
5007 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5009 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
5010 const struct wined3d_format *src_format, *dst_format;
5011 struct wined3d_texture *src_texture = NULL;
5012 struct wined3d_map_desc dst_map, src_map;
5013 const BYTE *sbase = NULL;
5014 HRESULT hr = WINED3D_OK;
5015 const BYTE *sbuf;
5016 BYTE *dbuf;
5017 int x, y;
5019 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5020 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5021 flags, fx, debug_d3dtexturefiltertype(filter));
5023 if (src_surface == dst_surface)
5025 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
5026 src_map = dst_map;
5027 src_format = dst_surface->resource.format;
5028 dst_format = src_format;
5030 else
5032 dst_format = dst_surface->resource.format;
5033 if (src_surface)
5035 if (dst_surface->resource.format->id != src_surface->resource.format->id)
5037 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
5039 /* The conv function writes a FIXME */
5040 WARN("Cannot convert source surface format to dest format.\n");
5041 goto release;
5043 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
5045 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
5046 src_format = src_surface->resource.format;
5048 else
5050 src_format = dst_format;
5053 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
5056 bpp = dst_surface->resource.format->byte_count;
5057 srcheight = src_rect->bottom - src_rect->top;
5058 srcwidth = src_rect->right - src_rect->left;
5059 dstheight = dst_rect->bottom - dst_rect->top;
5060 dstwidth = dst_rect->right - dst_rect->left;
5061 width = (dst_rect->right - dst_rect->left) * bpp;
5063 if (src_surface)
5064 sbase = (BYTE *)src_map.data
5065 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
5066 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
5067 if (src_surface != dst_surface)
5068 dbuf = dst_map.data;
5069 else
5070 dbuf = (BYTE *)dst_map.data
5071 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
5072 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
5074 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
5076 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
5078 if (src_surface == dst_surface)
5080 FIXME("Only plain blits supported on compressed surfaces.\n");
5081 hr = E_NOTIMPL;
5082 goto release;
5085 if (srcheight != dstheight || srcwidth != dstwidth)
5087 WARN("Stretching not supported on compressed surfaces.\n");
5088 hr = WINED3DERR_INVALIDCALL;
5089 goto release;
5092 if (!surface_check_block_align(src_surface, src_rect))
5094 WARN("Source rectangle not block-aligned.\n");
5095 hr = WINED3DERR_INVALIDCALL;
5096 goto release;
5099 if (!surface_check_block_align(dst_surface, dst_rect))
5101 WARN("Destination rectangle not block-aligned.\n");
5102 hr = WINED3DERR_INVALIDCALL;
5103 goto release;
5106 hr = surface_cpu_blt_compressed(sbase, dbuf,
5107 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
5108 src_format, flags, fx);
5109 goto release;
5112 /* First, all the 'source-less' blits */
5113 if (flags & WINEDDBLT_COLORFILL)
5115 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
5116 flags &= ~WINEDDBLT_COLORFILL;
5119 if (flags & WINEDDBLT_DEPTHFILL)
5121 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
5123 if (flags & WINEDDBLT_ROP)
5125 /* Catch some degenerate cases here. */
5126 switch (fx->dwROP)
5128 case BLACKNESS:
5129 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
5130 break;
5131 case 0xaa0029: /* No-op */
5132 break;
5133 case WHITENESS:
5134 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
5135 break;
5136 case SRCCOPY: /* Well, we do that below? */
5137 break;
5138 default:
5139 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
5140 goto error;
5142 flags &= ~WINEDDBLT_ROP;
5144 if (flags & WINEDDBLT_DDROPS)
5146 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
5148 /* Now the 'with source' blits. */
5149 if (src_surface)
5151 int sx, xinc, sy, yinc;
5153 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
5154 goto release;
5156 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
5157 && (srcwidth != dstwidth || srcheight != dstheight))
5159 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
5160 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
5163 xinc = (srcwidth << 16) / dstwidth;
5164 yinc = (srcheight << 16) / dstheight;
5166 if (!flags)
5168 /* No effects, we can cheat here. */
5169 if (dstwidth == srcwidth)
5171 if (dstheight == srcheight)
5173 /* No stretching in either direction. This needs to be as
5174 * fast as possible. */
5175 sbuf = sbase;
5177 /* Check for overlapping surfaces. */
5178 if (src_surface != dst_surface || dst_rect->top < src_rect->top
5179 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
5181 /* No overlap, or dst above src, so copy from top downwards. */
5182 for (y = 0; y < dstheight; ++y)
5184 memcpy(dbuf, sbuf, width);
5185 sbuf += src_map.row_pitch;
5186 dbuf += dst_map.row_pitch;
5189 else if (dst_rect->top > src_rect->top)
5191 /* Copy from bottom upwards. */
5192 sbuf += src_map.row_pitch * dstheight;
5193 dbuf += dst_map.row_pitch * dstheight;
5194 for (y = 0; y < dstheight; ++y)
5196 sbuf -= src_map.row_pitch;
5197 dbuf -= dst_map.row_pitch;
5198 memcpy(dbuf, sbuf, width);
5201 else
5203 /* Src and dst overlapping on the same line, use memmove. */
5204 for (y = 0; y < dstheight; ++y)
5206 memmove(dbuf, sbuf, width);
5207 sbuf += src_map.row_pitch;
5208 dbuf += dst_map.row_pitch;
5212 else
5214 /* Stretching in y direction only. */
5215 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5217 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5218 memcpy(dbuf, sbuf, width);
5219 dbuf += dst_map.row_pitch;
5223 else
5225 /* Stretching in X direction. */
5226 int last_sy = -1;
5227 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5229 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5231 if ((sy >> 16) == (last_sy >> 16))
5233 /* This source row is the same as last source row -
5234 * Copy the already stretched row. */
5235 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
5237 else
5239 #define STRETCH_ROW(type) \
5240 do { \
5241 const type *s = (const type *)sbuf; \
5242 type *d = (type *)dbuf; \
5243 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5244 d[x] = s[sx >> 16]; \
5245 } while(0)
5247 switch(bpp)
5249 case 1:
5250 STRETCH_ROW(BYTE);
5251 break;
5252 case 2:
5253 STRETCH_ROW(WORD);
5254 break;
5255 case 4:
5256 STRETCH_ROW(DWORD);
5257 break;
5258 case 3:
5260 const BYTE *s;
5261 BYTE *d = dbuf;
5262 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
5264 DWORD pixel;
5266 s = sbuf + 3 * (sx >> 16);
5267 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5268 d[0] = (pixel ) & 0xff;
5269 d[1] = (pixel >> 8) & 0xff;
5270 d[2] = (pixel >> 16) & 0xff;
5271 d += 3;
5273 break;
5275 default:
5276 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
5277 hr = WINED3DERR_NOTAVAILABLE;
5278 goto error;
5280 #undef STRETCH_ROW
5282 dbuf += dst_map.row_pitch;
5283 last_sy = sy;
5287 else
5289 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
5290 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
5291 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
5292 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
5294 /* The color keying flags are checked for correctness in ddraw */
5295 if (flags & WINEDDBLT_KEYSRC)
5297 keylow = src_surface->container->src_blt_color_key.color_space_low_value;
5298 keyhigh = src_surface->container->src_blt_color_key.color_space_high_value;
5300 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5302 keylow = fx->ddckSrcColorkey.color_space_low_value;
5303 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
5306 if (flags & WINEDDBLT_KEYDEST)
5308 /* Destination color keys are taken from the source surface! */
5309 destkeylow = src_surface->container->dst_blt_color_key.color_space_low_value;
5310 destkeyhigh = src_surface->container->dst_blt_color_key.color_space_high_value;
5312 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
5314 destkeylow = fx->ddckDestColorkey.color_space_low_value;
5315 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
5318 if (bpp == 1)
5320 keymask = 0xff;
5322 else
5324 DWORD masks[3];
5325 get_color_masks(src_format, masks);
5326 keymask = masks[0]
5327 | masks[1]
5328 | masks[2];
5330 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
5333 if (flags & WINEDDBLT_DDFX)
5335 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
5336 LONG tmpxy;
5337 dTopLeft = dbuf;
5338 dTopRight = dbuf + ((dstwidth - 1) * bpp);
5339 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
5340 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
5342 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
5344 /* I don't think we need to do anything about this flag */
5345 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
5347 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
5349 tmp = dTopRight;
5350 dTopRight = dTopLeft;
5351 dTopLeft = tmp;
5352 tmp = dBottomRight;
5353 dBottomRight = dBottomLeft;
5354 dBottomLeft = tmp;
5355 dstxinc = dstxinc * -1;
5357 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
5359 tmp = dTopLeft;
5360 dTopLeft = dBottomLeft;
5361 dBottomLeft = tmp;
5362 tmp = dTopRight;
5363 dTopRight = dBottomRight;
5364 dBottomRight = tmp;
5365 dstyinc = dstyinc * -1;
5367 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
5369 /* I don't think we need to do anything about this flag */
5370 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
5372 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
5374 tmp = dBottomRight;
5375 dBottomRight = dTopLeft;
5376 dTopLeft = tmp;
5377 tmp = dBottomLeft;
5378 dBottomLeft = dTopRight;
5379 dTopRight = tmp;
5380 dstxinc = dstxinc * -1;
5381 dstyinc = dstyinc * -1;
5383 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
5385 tmp = dTopLeft;
5386 dTopLeft = dBottomLeft;
5387 dBottomLeft = dBottomRight;
5388 dBottomRight = dTopRight;
5389 dTopRight = tmp;
5390 tmpxy = dstxinc;
5391 dstxinc = dstyinc;
5392 dstyinc = tmpxy;
5393 dstxinc = dstxinc * -1;
5395 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
5397 tmp = dTopLeft;
5398 dTopLeft = dTopRight;
5399 dTopRight = dBottomRight;
5400 dBottomRight = dBottomLeft;
5401 dBottomLeft = tmp;
5402 tmpxy = dstxinc;
5403 dstxinc = dstyinc;
5404 dstyinc = tmpxy;
5405 dstyinc = dstyinc * -1;
5407 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
5409 /* I don't think we need to do anything about this flag */
5410 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
5412 dbuf = dTopLeft;
5413 flags &= ~(WINEDDBLT_DDFX);
5416 #define COPY_COLORKEY_FX(type) \
5417 do { \
5418 const type *s; \
5419 type *d = (type *)dbuf, *dx, tmp; \
5420 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
5422 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
5423 dx = d; \
5424 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5426 tmp = s[sx >> 16]; \
5427 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
5428 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
5430 dx[0] = tmp; \
5432 dx = (type *)(((BYTE *)dx) + dstxinc); \
5434 d = (type *)(((BYTE *)d) + dstyinc); \
5436 } while(0)
5438 switch (bpp)
5440 case 1:
5441 COPY_COLORKEY_FX(BYTE);
5442 break;
5443 case 2:
5444 COPY_COLORKEY_FX(WORD);
5445 break;
5446 case 4:
5447 COPY_COLORKEY_FX(DWORD);
5448 break;
5449 case 3:
5451 const BYTE *s;
5452 BYTE *d = dbuf, *dx;
5453 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5455 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5456 dx = d;
5457 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
5459 DWORD pixel, dpixel = 0;
5460 s = sbuf + 3 * (sx>>16);
5461 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5462 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
5463 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
5464 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
5466 dx[0] = (pixel ) & 0xff;
5467 dx[1] = (pixel >> 8) & 0xff;
5468 dx[2] = (pixel >> 16) & 0xff;
5470 dx += dstxinc;
5472 d += dstyinc;
5474 break;
5476 default:
5477 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
5478 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
5479 hr = WINED3DERR_NOTAVAILABLE;
5480 goto error;
5481 #undef COPY_COLORKEY_FX
5486 error:
5487 if (flags && FIXME_ON(d3d_surface))
5489 FIXME("\tUnsupported flags: %#x.\n", flags);
5492 release:
5493 wined3d_surface_unmap(dst_surface);
5494 if (src_surface && src_surface != dst_surface)
5495 wined3d_surface_unmap(src_surface);
5496 /* Release the converted surface, if any. */
5497 if (src_texture)
5498 wined3d_texture_decref(src_texture);
5500 return hr;
5503 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5504 const RECT *dst_rect, const struct wined3d_color *color)
5506 static const RECT src_rect;
5507 WINEDDBLTFX BltFx;
5509 memset(&BltFx, 0, sizeof(BltFx));
5510 BltFx.dwSize = sizeof(BltFx);
5511 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
5512 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
5513 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
5516 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
5517 struct wined3d_surface *surface, const RECT *rect, float depth)
5519 FIXME("Depth filling not implemented by cpu_blit.\n");
5520 return WINED3DERR_INVALIDCALL;
5523 const struct blit_shader cpu_blit = {
5524 cpu_blit_alloc,
5525 cpu_blit_free,
5526 cpu_blit_set,
5527 cpu_blit_unset,
5528 cpu_blit_supported,
5529 cpu_blit_color_fill,
5530 cpu_blit_depth_fill,
5533 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5534 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5535 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5537 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5538 struct wined3d_device *device = dst_surface->resource.device;
5539 DWORD src_ds_flags, dst_ds_flags;
5540 RECT src_rect, dst_rect;
5541 BOOL scale, convert;
5542 enum wined3d_conversion_type dst_convert_type;
5543 struct wined3d_format dst_conv_fmt;
5545 static const DWORD simple_blit = WINEDDBLT_ASYNC
5546 | WINEDDBLT_COLORFILL
5547 | WINEDDBLT_WAIT
5548 | WINEDDBLT_DEPTHFILL
5549 | WINEDDBLT_DONOTWAIT;
5551 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5552 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
5553 flags, fx, debug_d3dtexturefiltertype(filter));
5554 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5556 if (fx)
5558 TRACE("dwSize %#x.\n", fx->dwSize);
5559 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5560 TRACE("dwROP %#x.\n", fx->dwROP);
5561 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5562 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5563 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5564 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5565 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5566 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5567 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5568 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5569 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5570 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5571 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5572 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5573 TRACE("dwReserved %#x.\n", fx->dwReserved);
5574 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5575 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5576 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5577 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5578 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5579 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5580 fx->ddckDestColorkey.color_space_low_value,
5581 fx->ddckDestColorkey.color_space_high_value);
5582 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5583 fx->ddckSrcColorkey.color_space_low_value,
5584 fx->ddckSrcColorkey.color_space_high_value);
5587 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5589 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5590 return WINEDDERR_SURFACEBUSY;
5593 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5595 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5596 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5597 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5598 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5599 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5601 WARN("The application gave us a bad destination rectangle.\n");
5602 return WINEDDERR_INVALIDRECT;
5605 if (src_surface)
5607 surface_get_rect(src_surface, src_rect_in, &src_rect);
5609 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5610 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5611 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5612 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5613 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5615 WARN("Application gave us bad source rectangle for Blt.\n");
5616 return WINEDDERR_INVALIDRECT;
5619 else
5621 memset(&src_rect, 0, sizeof(src_rect));
5624 if (!fx || !(fx->dwDDFX))
5625 flags &= ~WINEDDBLT_DDFX;
5627 if (flags & WINEDDBLT_WAIT)
5628 flags &= ~WINEDDBLT_WAIT;
5630 if (flags & WINEDDBLT_ASYNC)
5632 static unsigned int once;
5634 if (!once++)
5635 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5636 flags &= ~WINEDDBLT_ASYNC;
5639 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5640 if (flags & WINEDDBLT_DONOTWAIT)
5642 static unsigned int once;
5644 if (!once++)
5645 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5646 flags &= ~WINEDDBLT_DONOTWAIT;
5649 if (!device->d3d_initialized)
5651 WARN("D3D not initialized, using fallback.\n");
5652 goto cpu;
5655 /* We want to avoid invalidating the sysmem location for converted
5656 * surfaces, since otherwise we'd have to convert the data back when
5657 * locking them. */
5658 d3dfmt_get_conv(dst_surface->container, TRUE, &dst_conv_fmt, &dst_convert_type);
5659 if (dst_convert_type != WINED3D_CT_NONE || dst_conv_fmt.convert || dst_surface->flags & SFLAG_CONVERTED)
5661 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5662 goto cpu;
5665 if (flags & ~simple_blit)
5667 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5668 goto fallback;
5671 if (src_surface)
5672 src_swapchain = src_surface->container->swapchain;
5673 else
5674 src_swapchain = NULL;
5676 dst_swapchain = dst_surface->container->swapchain;
5678 /* This isn't strictly needed. FBO blits for example could deal with
5679 * cross-swapchain blits by first downloading the source to a texture
5680 * before switching to the destination context. We just have this here to
5681 * not have to deal with the issue, since cross-swapchain blits should be
5682 * rare. */
5683 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5685 FIXME("Using fallback for cross-swapchain blit.\n");
5686 goto fallback;
5689 scale = src_surface
5690 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5691 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5692 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5694 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5695 if (src_surface)
5696 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5697 else
5698 src_ds_flags = 0;
5700 if (src_ds_flags || dst_ds_flags)
5702 if (flags & WINEDDBLT_DEPTHFILL)
5704 float depth;
5706 TRACE("Depth fill.\n");
5708 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5709 return WINED3DERR_INVALIDCALL;
5711 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
5712 return WINED3D_OK;
5714 else
5716 if (src_ds_flags != dst_ds_flags)
5718 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
5719 return WINED3DERR_INVALIDCALL;
5722 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
5723 &src_rect, dst_surface, dst_surface->container->resource.draw_binding, &dst_rect)))
5724 return WINED3D_OK;
5727 else
5729 /* In principle this would apply to depth blits as well, but we don't
5730 * implement those in the CPU blitter at the moment. */
5731 if ((dst_surface->locations & dst_surface->resource.map_binding)
5732 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
5734 if (scale)
5735 TRACE("Not doing sysmem blit because of scaling.\n");
5736 else if (convert)
5737 TRACE("Not doing sysmem blit because of format conversion.\n");
5738 else
5739 goto cpu;
5742 if (flags & WINEDDBLT_COLORFILL)
5744 struct wined3d_color color;
5746 TRACE("Color fill.\n");
5748 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
5749 goto fallback;
5751 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
5752 return WINED3D_OK;
5754 else
5756 TRACE("Color blit.\n");
5758 /* Upload */
5759 if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5760 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5762 if (scale)
5763 TRACE("Not doing upload because of scaling.\n");
5764 else if (convert)
5765 TRACE("Not doing upload because of format conversion.\n");
5766 else
5768 POINT dst_point = {dst_rect.left, dst_rect.top};
5770 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
5772 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
5773 surface_load_location(dst_surface, dst_surface->container->resource.draw_binding);
5774 return WINED3D_OK;
5779 /* Use present for back -> front blits. The idea behind this is
5780 * that present is potentially faster than a blit, in particular
5781 * when FBO blits aren't available. Some ddraw applications like
5782 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5783 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5784 * applications can't blit directly to the frontbuffer. */
5785 if (dst_swapchain && dst_swapchain->back_buffers
5786 && dst_surface->container == dst_swapchain->front_buffer
5787 && src_surface->container == dst_swapchain->back_buffers[0])
5789 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5791 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5793 /* Set the swap effect to COPY, we don't want the backbuffer
5794 * to become undefined. */
5795 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5796 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5797 dst_swapchain->desc.swap_effect = swap_effect;
5799 return WINED3D_OK;
5802 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5803 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5804 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5806 TRACE("Using FBO blit.\n");
5808 surface_blt_fbo(device, filter,
5809 src_surface, src_surface->container->resource.draw_binding, &src_rect,
5810 dst_surface, dst_surface->container->resource.draw_binding, &dst_rect);
5811 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5812 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5814 return WINED3D_OK;
5817 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5818 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5819 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5821 TRACE("Using arbfp blit.\n");
5823 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
5824 return WINED3D_OK;
5829 fallback:
5830 /* Special cases for render targets. */
5831 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
5832 return WINED3D_OK;
5834 cpu:
5836 /* For the rest call the X11 surface implementation. For render targets
5837 * this should be implemented OpenGL accelerated in surface_blt_special(),
5838 * other blits are rather rare. */
5839 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
5842 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5843 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5845 struct wined3d_device *device = container->resource.device;
5846 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5847 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5848 UINT multisample_quality = desc->multisample_quality;
5849 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
5850 unsigned int resource_size;
5851 HRESULT hr;
5853 if (multisample_quality > 0)
5855 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
5856 multisample_quality = 0;
5859 /* Quick lockable sanity check.
5860 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5861 * this function is too deep to need to care about things like this.
5862 * Levels need to be checked too, since they all affect what can be done. */
5863 switch (desc->pool)
5865 case WINED3D_POOL_MANAGED:
5866 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5867 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5868 break;
5870 case WINED3D_POOL_DEFAULT:
5871 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5872 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5873 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5874 break;
5876 case WINED3D_POOL_SCRATCH:
5877 case WINED3D_POOL_SYSTEM_MEM:
5878 break;
5880 default:
5881 FIXME("Unknown pool %#x.\n", desc->pool);
5882 break;
5885 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5886 FIXME("Trying to create a render target that isn't in the default pool.\n");
5888 /* FIXME: Check that the format is supported by the device. */
5890 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5891 if (!resource_size)
5892 return WINED3DERR_INVALIDCALL;
5894 if (device->wined3d->flags & WINED3D_NO3D)
5895 surface->surface_ops = &gdi_surface_ops;
5896 else
5897 surface->surface_ops = &surface_ops;
5899 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
5900 desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height, 1,
5901 resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5903 WARN("Failed to initialize resource, returning %#x.\n", hr);
5904 return hr;
5907 surface->container = container;
5908 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5909 list_init(&surface->renderbuffers);
5910 list_init(&surface->overlays);
5912 /* Flags */
5913 if (target != GL_TEXTURE_RECTANGLE_ARB)
5914 surface->flags |= SFLAG_NORMCOORD;
5915 if (flags & WINED3D_SURFACE_DISCARD)
5916 surface->flags |= SFLAG_DISCARD;
5917 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
5918 surface->flags |= SFLAG_PIN_SYSMEM;
5919 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5920 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5922 surface->texture_target = target;
5923 surface->texture_level = level;
5924 surface->texture_layer = layer;
5926 /* Call the private setup routine */
5927 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5929 ERR("Private setup failed, hr %#x.\n", hr);
5930 surface_cleanup(surface);
5931 return hr;
5934 /* Similar to lockable rendertargets above, creating the DIB section
5935 * during surface initialization prevents the sysmem pointer from changing
5936 * after a wined3d_surface_getdc() call. */
5937 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5938 && SUCCEEDED(surface_create_dib_section(surface)))
5939 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5941 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5943 wined3d_resource_free_sysmem(&surface->resource);
5944 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5945 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5948 return hr;
5951 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5952 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
5954 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
5955 const struct wined3d_parent_ops *parent_ops;
5956 struct wined3d_surface *object;
5957 void *parent;
5958 HRESULT hr;
5960 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
5961 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
5962 container, desc->width, desc->height, debug_d3dformat(desc->format),
5963 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
5964 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
5966 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
5967 return E_OUTOFMEMORY;
5969 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
5971 WARN("Failed to initialize surface, returning %#x.\n", hr);
5972 HeapFree(GetProcessHeap(), 0, object);
5973 return hr;
5976 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
5977 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
5979 WARN("Failed to create surface parent, hr %#x.\n", hr);
5980 wined3d_surface_destroy(object);
5981 return hr;
5984 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
5986 object->resource.parent = parent;
5987 object->resource.parent_ops = parent_ops;
5988 *surface = object;
5990 return hr;