wined3d: Don't check for render target usage in wined3d_surface_set_mem.
[wine.git] / dlls / wined3d / surface.c
blobf18bab17aea3921afb327fdbe1f9b01b353db60c
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 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 void surface_cleanup(struct wined3d_surface *surface)
41 struct wined3d_surface *overlay, *cur;
43 TRACE("surface %p.\n", surface);
45 if ((surface->flags & SFLAG_PBO) || surface->rb_multisample
46 || surface->rb_resolved || !list_empty(&surface->renderbuffers))
48 struct wined3d_renderbuffer_entry *entry, *entry2;
49 const struct wined3d_gl_info *gl_info;
50 struct wined3d_context *context;
52 context = context_acquire(surface->resource.device, NULL);
53 gl_info = context->gl_info;
55 if (surface->flags & SFLAG_PBO)
57 TRACE("Deleting PBO %u.\n", surface->pbo);
58 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
61 if (surface->rb_multisample)
63 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
64 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
67 if (surface->rb_resolved)
69 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
70 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
73 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
75 TRACE("Deleting renderbuffer %u.\n", entry->id);
76 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
77 HeapFree(GetProcessHeap(), 0, entry);
80 context_release(context);
83 if (surface->flags & SFLAG_DIBSECTION)
85 DeleteDC(surface->hDC);
86 DeleteObject(surface->dib.DIBsection);
87 surface->dib.bitmap_data = NULL;
88 surface->resource.allocatedMemory = NULL;
91 if (surface->flags & SFLAG_USERPTR)
92 surface->resource.allocatedMemory = 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 surface_update_draw_binding(struct wined3d_surface *surface)
108 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
109 surface->draw_binding = SFLAG_INDRAWABLE;
110 else if (surface->resource.multisample_type)
111 surface->draw_binding = SFLAG_INRB_MULTISAMPLE;
112 else
113 surface->draw_binding = SFLAG_INTEXTURE;
116 void surface_set_swapchain(struct wined3d_surface *surface, struct wined3d_swapchain *swapchain)
118 TRACE("surface %p, swapchain %p.\n", surface, swapchain);
120 if (swapchain)
122 surface->get_drawable_size = get_drawable_size_swapchain;
124 else
126 switch (wined3d_settings.offscreen_rendering_mode)
128 case ORM_FBO:
129 surface->get_drawable_size = get_drawable_size_fbo;
130 break;
132 case ORM_BACKBUFFER:
133 surface->get_drawable_size = get_drawable_size_backbuffer;
134 break;
136 default:
137 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
138 return;
142 surface->swapchain = swapchain;
143 surface_update_draw_binding(surface);
146 void surface_set_container(struct wined3d_surface *surface, struct wined3d_texture *container)
148 TRACE("surface %p, container %p.\n", surface, container);
150 if (!surface->swapchain)
152 switch (wined3d_settings.offscreen_rendering_mode)
154 case ORM_FBO:
155 surface->get_drawable_size = get_drawable_size_fbo;
156 break;
158 case ORM_BACKBUFFER:
159 surface->get_drawable_size = get_drawable_size_backbuffer;
160 break;
162 default:
163 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
164 return;
168 surface->container = container;
169 surface_update_draw_binding(surface);
172 struct blt_info
174 GLenum binding;
175 GLenum bind_target;
176 enum tex_types tex_type;
177 GLfloat coords[4][3];
180 struct float_rect
182 float l;
183 float t;
184 float r;
185 float b;
188 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
190 f->l = ((r->left * 2.0f) / w) - 1.0f;
191 f->t = ((r->top * 2.0f) / h) - 1.0f;
192 f->r = ((r->right * 2.0f) / w) - 1.0f;
193 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
196 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
198 GLfloat (*coords)[3] = info->coords;
199 struct float_rect f;
201 switch (target)
203 default:
204 FIXME("Unsupported texture target %#x\n", target);
205 /* Fall back to GL_TEXTURE_2D */
206 case GL_TEXTURE_2D:
207 info->binding = GL_TEXTURE_BINDING_2D;
208 info->bind_target = GL_TEXTURE_2D;
209 info->tex_type = tex_2d;
210 coords[0][0] = (float)rect->left / w;
211 coords[0][1] = (float)rect->top / h;
212 coords[0][2] = 0.0f;
214 coords[1][0] = (float)rect->right / w;
215 coords[1][1] = (float)rect->top / h;
216 coords[1][2] = 0.0f;
218 coords[2][0] = (float)rect->left / w;
219 coords[2][1] = (float)rect->bottom / h;
220 coords[2][2] = 0.0f;
222 coords[3][0] = (float)rect->right / w;
223 coords[3][1] = (float)rect->bottom / h;
224 coords[3][2] = 0.0f;
225 break;
227 case GL_TEXTURE_RECTANGLE_ARB:
228 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
229 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
230 info->tex_type = tex_rect;
231 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
232 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
233 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
234 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
235 break;
237 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
238 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
239 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
240 info->tex_type = tex_cube;
241 cube_coords_float(rect, w, h, &f);
243 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
244 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
245 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
246 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
247 break;
249 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
250 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
251 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
252 info->tex_type = tex_cube;
253 cube_coords_float(rect, w, h, &f);
255 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
256 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
257 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
258 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
259 break;
261 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
262 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
263 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
264 info->tex_type = tex_cube;
265 cube_coords_float(rect, w, h, &f);
267 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
268 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
269 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
270 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
271 break;
273 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
274 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
275 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
276 info->tex_type = tex_cube;
277 cube_coords_float(rect, w, h, &f);
279 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
280 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
281 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
282 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
283 break;
285 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
286 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
287 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
288 info->tex_type = tex_cube;
289 cube_coords_float(rect, w, h, &f);
291 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
292 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
293 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
294 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
295 break;
297 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
298 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
299 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
300 info->tex_type = tex_cube;
301 cube_coords_float(rect, w, h, &f);
303 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
304 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
305 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
306 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
307 break;
311 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
313 if (rect_in)
314 *rect_out = *rect_in;
315 else
317 rect_out->left = 0;
318 rect_out->top = 0;
319 rect_out->right = surface->resource.width;
320 rect_out->bottom = surface->resource.height;
324 /* Context activation is done by the caller. */
325 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
326 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
328 const struct wined3d_gl_info *gl_info = context->gl_info;
329 struct wined3d_texture *texture = src_surface->container;
330 struct blt_info info;
332 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
334 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
335 checkGLcall("glEnable(bind_target)");
337 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
339 /* Filtering for StretchRect */
340 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
341 wined3d_gl_mag_filter(magLookup, filter));
342 checkGLcall("glTexParameteri");
343 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
344 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
345 checkGLcall("glTexParameteri");
346 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
347 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
348 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
349 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
350 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
351 checkGLcall("glTexEnvi");
353 /* Draw a quad */
354 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
355 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
356 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
358 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
359 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
361 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
362 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
364 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
365 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
366 gl_info->gl_ops.gl.p_glEnd();
368 /* Unbind the texture */
369 context_bind_texture(context, info.bind_target, 0);
371 /* We changed the filtering settings on the texture. Inform the
372 * container about this to get the filters reset properly next draw. */
373 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_POINT;
374 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
375 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_NONE;
376 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
379 /* Works correctly only for <= 4 bpp formats. */
380 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
382 masks[0] = ((1 << format->red_size) - 1) << format->red_offset;
383 masks[1] = ((1 << format->green_size) - 1) << format->green_offset;
384 masks[2] = ((1 << format->blue_size) - 1) << format->blue_offset;
387 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
389 const struct wined3d_format *format = surface->resource.format;
390 SYSTEM_INFO sysInfo;
391 BITMAPINFO *b_info;
392 int extraline = 0;
393 DWORD *masks;
395 TRACE("surface %p.\n", surface);
397 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
399 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
400 return WINED3DERR_INVALIDCALL;
403 switch (format->byte_count)
405 case 2:
406 case 4:
407 /* Allocate extra space to store the RGB bit masks. */
408 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
409 break;
411 case 3:
412 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
413 break;
415 default:
416 /* Allocate extra space for a palette. */
417 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
418 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
419 break;
422 if (!b_info)
423 return E_OUTOFMEMORY;
425 /* Some applications access the surface in via DWORDs, and do not take
426 * the necessary care at the end of the surface. So we need at least
427 * 4 extra bytes at the end of the surface. Check against the page size,
428 * if the last page used for the surface has at least 4 spare bytes we're
429 * safe, otherwise add an extra line to the DIB section. */
430 GetSystemInfo(&sysInfo);
431 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
433 extraline = 1;
434 TRACE("Adding an extra line to the DIB section.\n");
437 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
438 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
439 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
440 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
441 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
442 * wined3d_surface_get_pitch(surface);
443 b_info->bmiHeader.biPlanes = 1;
444 b_info->bmiHeader.biBitCount = format->byte_count * 8;
446 b_info->bmiHeader.biXPelsPerMeter = 0;
447 b_info->bmiHeader.biYPelsPerMeter = 0;
448 b_info->bmiHeader.biClrUsed = 0;
449 b_info->bmiHeader.biClrImportant = 0;
451 /* Get the bit masks */
452 masks = (DWORD *)b_info->bmiColors;
453 switch (surface->resource.format->id)
455 case WINED3DFMT_B8G8R8_UNORM:
456 b_info->bmiHeader.biCompression = BI_RGB;
457 break;
459 case WINED3DFMT_B5G5R5X1_UNORM:
460 case WINED3DFMT_B5G5R5A1_UNORM:
461 case WINED3DFMT_B4G4R4A4_UNORM:
462 case WINED3DFMT_B4G4R4X4_UNORM:
463 case WINED3DFMT_B2G3R3_UNORM:
464 case WINED3DFMT_B2G3R3A8_UNORM:
465 case WINED3DFMT_R10G10B10A2_UNORM:
466 case WINED3DFMT_R8G8B8A8_UNORM:
467 case WINED3DFMT_R8G8B8X8_UNORM:
468 case WINED3DFMT_B10G10R10A2_UNORM:
469 case WINED3DFMT_B5G6R5_UNORM:
470 case WINED3DFMT_R16G16B16A16_UNORM:
471 b_info->bmiHeader.biCompression = BI_BITFIELDS;
472 get_color_masks(format, masks);
473 break;
475 default:
476 /* Don't know palette */
477 b_info->bmiHeader.biCompression = BI_RGB;
478 break;
481 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
482 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
483 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
484 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
486 if (!surface->dib.DIBsection)
488 ERR("Failed to create DIB section.\n");
489 HeapFree(GetProcessHeap(), 0, b_info);
490 return HRESULT_FROM_WIN32(GetLastError());
493 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
494 /* Copy the existing surface to the dib section. */
495 if (surface->resource.allocatedMemory)
497 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
498 surface->resource.height * wined3d_surface_get_pitch(surface));
500 else
502 /* This is to make maps read the GL texture although memory is allocated. */
503 surface->flags &= ~SFLAG_INSYSMEM;
505 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
507 HeapFree(GetProcessHeap(), 0, b_info);
509 /* Now allocate a DC. */
510 surface->hDC = CreateCompatibleDC(0);
511 SelectObject(surface->hDC, surface->dib.DIBsection);
512 TRACE("Using wined3d palette %p.\n", surface->palette);
513 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
515 surface->flags |= SFLAG_DIBSECTION;
517 return WINED3D_OK;
520 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data)
522 if (surface->flags & SFLAG_PBO)
524 data->addr = NULL;
525 data->buffer_object = surface->pbo;
527 else
529 data->addr = surface->resource.allocatedMemory;
530 data->buffer_object = 0;
534 static BOOL surface_need_pbo(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
536 if (surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
537 return FALSE;
538 if (!(surface->flags & SFLAG_DYNLOCK))
539 return FALSE;
540 if (surface->flags & (SFLAG_CONVERTED | SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM))
541 return FALSE;
542 if (!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT])
543 return FALSE;
545 return TRUE;
548 static void surface_create_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
550 struct wined3d_context *context;
551 GLenum error;
552 struct wined3d_bo_address data;
553 surface_get_memory(surface, &data);
555 context = context_acquire(surface->resource.device, NULL);
557 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
558 error = gl_info->gl_ops.gl.p_glGetError();
559 if (!surface->pbo || error != GL_NO_ERROR)
560 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
562 TRACE("Binding PBO %u.\n", surface->pbo);
564 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
565 checkGLcall("glBindBufferARB");
567 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
568 data.addr, GL_STREAM_DRAW_ARB));
569 checkGLcall("glBufferDataARB");
571 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
572 checkGLcall("glBindBufferARB");
574 /* We don't need the system memory anymore and we can't even use it for PBOs. */
575 if (!(surface->flags & SFLAG_CLIENT))
576 wined3d_resource_free_sysmem(&surface->resource);
577 surface->resource.allocatedMemory = NULL;
578 surface->flags |= SFLAG_PBO;
579 context_release(context);
582 static void surface_prepare_system_memory(struct wined3d_surface *surface)
584 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
586 TRACE("surface %p.\n", surface);
588 if (!(surface->flags & SFLAG_PBO) && surface_need_pbo(surface, gl_info))
589 surface_create_pbo(surface, gl_info);
590 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
592 /* Whatever surface we have, make sure that there is memory allocated
593 * for the downloaded copy, or a PBO to map. */
594 if (!surface->resource.heap_memory && !wined3d_resource_allocate_sysmem(&surface->resource))
595 ERR("Failed to allocate system memory.\n");
596 surface->resource.allocatedMemory = surface->resource.heap_memory;
598 if (surface->flags & SFLAG_INSYSMEM)
599 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
603 static void surface_evict_sysmem(struct wined3d_surface *surface)
605 if (surface->resource.map_count || (surface->flags & SFLAG_DONOTFREE))
606 return;
608 wined3d_resource_free_sysmem(&surface->resource);
609 surface->resource.allocatedMemory = NULL;
610 surface_invalidate_location(surface, SFLAG_INSYSMEM);
613 static void surface_force_reload(struct wined3d_surface *surface)
615 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
618 static void surface_release_client_storage(struct wined3d_surface *surface)
620 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
621 const struct wined3d_gl_info *gl_info = context->gl_info;
623 if (surface->container->texture_rgb.name)
625 wined3d_texture_bind_and_dirtify(surface->container, context, FALSE);
626 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
627 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
629 if (surface->container->texture_srgb.name)
631 wined3d_texture_bind_and_dirtify(surface->container, context, TRUE);
632 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
633 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
636 context_release(context);
638 surface_invalidate_location(surface, SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
639 surface_force_reload(surface);
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 surface->texture_target = GL_TEXTURE_2D;
652 /* Non-power2 support */
653 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
655 pow2Width = surface->resource.width;
656 pow2Height = surface->resource.height;
658 else
660 /* Find the nearest pow2 match */
661 pow2Width = pow2Height = 1;
662 while (pow2Width < surface->resource.width)
663 pow2Width <<= 1;
664 while (pow2Height < surface->resource.height)
665 pow2Height <<= 1;
667 surface->pow2Width = pow2Width;
668 surface->pow2Height = pow2Height;
670 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
672 /* TODO: Add support for non power two compressed textures. */
673 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
675 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
676 surface, surface->resource.width, surface->resource.height);
677 return WINED3DERR_NOTAVAILABLE;
681 if (pow2Width != surface->resource.width
682 || pow2Height != surface->resource.height)
684 surface->flags |= SFLAG_NONPOW2;
687 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
688 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
690 /* One of three options:
691 * 1: Do the same as we do with NPOT and scale the texture, (any
692 * texture ops would require the texture to be scaled which is
693 * potentially slow)
694 * 2: Set the texture to the maximum size (bad idea).
695 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
696 * 4: Create the surface, but allow it to be used only for DirectDraw
697 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
698 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
699 * the render target. */
700 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
702 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
703 return WINED3DERR_NOTAVAILABLE;
706 /* We should never use this surface in combination with OpenGL! */
707 TRACE("Creating an oversized surface: %ux%u.\n",
708 surface->pow2Width, surface->pow2Height);
711 switch (wined3d_settings.offscreen_rendering_mode)
713 case ORM_FBO:
714 surface->get_drawable_size = get_drawable_size_fbo;
715 break;
717 case ORM_BACKBUFFER:
718 surface->get_drawable_size = get_drawable_size_backbuffer;
719 break;
721 default:
722 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
723 return WINED3DERR_INVALIDCALL;
726 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
727 surface->flags |= SFLAG_DISCARDED;
729 return WINED3D_OK;
732 static void surface_realize_palette(struct wined3d_surface *surface)
734 struct wined3d_palette *palette = surface->palette;
736 TRACE("surface %p.\n", surface);
738 if (!palette) return;
740 if (surface->resource.format->id == WINED3DFMT_P8_UINT
741 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
743 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
745 /* Make sure the texture is up to date. This call doesn't do
746 * anything if the texture is already up to date. */
747 surface_load_location(surface, SFLAG_INTEXTURE);
749 /* We want to force a palette refresh, so mark the drawable as not being up to date */
750 if (!surface_is_offscreen(surface))
751 surface_invalidate_location(surface, SFLAG_INDRAWABLE);
753 else
755 if (!(surface->flags & SFLAG_INSYSMEM))
757 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
758 surface_load_location(surface, SFLAG_INSYSMEM);
760 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
764 if (surface->flags & SFLAG_DIBSECTION)
766 RGBQUAD col[256];
767 unsigned int i;
769 TRACE("Updating the DC's palette.\n");
771 for (i = 0; i < 256; ++i)
773 col[i].rgbRed = palette->palents[i].peRed;
774 col[i].rgbGreen = palette->palents[i].peGreen;
775 col[i].rgbBlue = palette->palents[i].peBlue;
776 col[i].rgbReserved = 0;
778 SetDIBColorTable(surface->hDC, 0, 256, col);
781 /* Propagate the changes to the drawable when we have a palette. */
782 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
783 surface_load_location(surface, surface->draw_binding);
786 static BYTE *surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
788 struct wined3d_device *device = surface->resource.device;
790 TRACE("surface %p, rect %s, flags %#x.\n",
791 surface, wine_dbgstr_rect(rect), flags);
793 if (flags & WINED3D_MAP_DISCARD)
795 TRACE("WINED3D_MAP_DISCARD flag passed, marking SYSMEM as up to date.\n");
796 surface_prepare_system_memory(surface);
797 surface_validate_location(surface, SFLAG_INSYSMEM);
799 else
801 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
802 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
804 surface_load_location(surface, SFLAG_INSYSMEM);
807 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
808 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
810 if (surface->flags & SFLAG_PBO)
812 BYTE *ret;
813 const struct wined3d_gl_info *gl_info;
814 struct wined3d_context *context;
816 context = context_acquire(device, NULL);
817 gl_info = context->gl_info;
819 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
820 checkGLcall("glBindBufferARB");
822 ret = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
823 checkGLcall("glMapBufferARB");
825 /* Make sure the PBO isn't set anymore in order not to break non-PBO
826 * calls. */
827 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
828 checkGLcall("glBindBufferARB");
830 context_release(context);
831 return ret;
834 return surface->resource.allocatedMemory;
837 static void surface_unmap(struct wined3d_surface *surface)
839 struct wined3d_device *device = surface->resource.device;
841 TRACE("surface %p.\n", surface);
843 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
845 if (surface->flags & SFLAG_PBO)
847 const struct wined3d_gl_info *gl_info;
848 struct wined3d_context *context;
850 TRACE("Freeing PBO memory.\n");
852 context = context_acquire(device, NULL);
853 gl_info = context->gl_info;
855 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
856 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
857 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
858 checkGLcall("glUnmapBufferARB");
859 context_release(context);
862 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
864 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
866 TRACE("Not dirtified, nothing to do.\n");
867 return;
870 if (surface->swapchain && surface->swapchain->front_buffer == surface)
871 surface_load_location(surface, surface->draw_binding);
872 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
873 FIXME("Depth / stencil buffer locking is not implemented.\n");
876 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
878 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
879 return FALSE;
880 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
881 return FALSE;
882 return TRUE;
885 static void surface_depth_blt_fbo(const struct wined3d_device *device,
886 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
887 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
889 const struct wined3d_gl_info *gl_info;
890 struct wined3d_context *context;
891 DWORD src_mask, dst_mask;
892 GLbitfield gl_mask;
894 TRACE("device %p\n", device);
895 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
896 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect));
897 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
898 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect));
900 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
901 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
903 if (src_mask != dst_mask)
905 ERR("Incompatible formats %s and %s.\n",
906 debug_d3dformat(src_surface->resource.format->id),
907 debug_d3dformat(dst_surface->resource.format->id));
908 return;
911 if (!src_mask)
913 ERR("Not a depth / stencil format: %s.\n",
914 debug_d3dformat(src_surface->resource.format->id));
915 return;
918 gl_mask = 0;
919 if (src_mask & WINED3DFMT_FLAG_DEPTH)
920 gl_mask |= GL_DEPTH_BUFFER_BIT;
921 if (src_mask & WINED3DFMT_FLAG_STENCIL)
922 gl_mask |= GL_STENCIL_BUFFER_BIT;
924 /* Make sure the locations are up-to-date. Loading the destination
925 * surface isn't required if the entire surface is overwritten. */
926 surface_load_location(src_surface, src_location);
927 if (!surface_is_full_rect(dst_surface, dst_rect))
928 surface_load_location(dst_surface, dst_location);
930 context = context_acquire(device, NULL);
931 if (!context->valid)
933 context_release(context);
934 WARN("Invalid context, skipping blit.\n");
935 return;
938 gl_info = context->gl_info;
940 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
941 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
943 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
944 context_set_draw_buffer(context, GL_NONE);
945 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
946 context_invalidate_state(context, STATE_FRAMEBUFFER);
948 if (gl_mask & GL_DEPTH_BUFFER_BIT)
950 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
951 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
953 if (gl_mask & GL_STENCIL_BUFFER_BIT)
955 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
957 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
958 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
960 gl_info->gl_ops.gl.p_glStencilMask(~0U);
961 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
964 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
965 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
967 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
968 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
969 checkGLcall("glBlitFramebuffer()");
971 if (wined3d_settings.strict_draw_ordering)
972 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
974 context_release(context);
977 /* Blit between surface locations. Onscreen on different swapchains is not supported.
978 * Depth / stencil is not supported. */
979 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
980 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
981 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
983 const struct wined3d_gl_info *gl_info;
984 struct wined3d_context *context;
985 RECT src_rect, dst_rect;
986 GLenum gl_filter;
987 GLenum buffer;
989 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
990 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
991 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
992 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
993 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
995 src_rect = *src_rect_in;
996 dst_rect = *dst_rect_in;
998 switch (filter)
1000 case WINED3D_TEXF_LINEAR:
1001 gl_filter = GL_LINEAR;
1002 break;
1004 default:
1005 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1006 case WINED3D_TEXF_NONE:
1007 case WINED3D_TEXF_POINT:
1008 gl_filter = GL_NEAREST;
1009 break;
1012 /* Resolve the source surface first if needed. */
1013 if (src_location == SFLAG_INRB_MULTISAMPLE
1014 && (src_surface->resource.format->id != dst_surface->resource.format->id
1015 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1016 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1017 src_location = SFLAG_INRB_RESOLVED;
1019 /* Make sure the locations are up-to-date. Loading the destination
1020 * surface isn't required if the entire surface is overwritten. (And is
1021 * in fact harmful if we're being called by surface_load_location() with
1022 * the purpose of loading the destination surface.) */
1023 surface_load_location(src_surface, src_location);
1024 if (!surface_is_full_rect(dst_surface, &dst_rect))
1025 surface_load_location(dst_surface, dst_location);
1027 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1028 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1029 else context = context_acquire(device, NULL);
1031 if (!context->valid)
1033 context_release(context);
1034 WARN("Invalid context, skipping blit.\n");
1035 return;
1038 gl_info = context->gl_info;
1040 if (src_location == SFLAG_INDRAWABLE)
1042 TRACE("Source surface %p is onscreen.\n", src_surface);
1043 buffer = surface_get_gl_buffer(src_surface);
1044 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1046 else
1048 TRACE("Source surface %p is offscreen.\n", src_surface);
1049 buffer = GL_COLOR_ATTACHMENT0;
1052 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1053 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1054 checkGLcall("glReadBuffer()");
1055 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1057 if (dst_location == SFLAG_INDRAWABLE)
1059 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1060 buffer = surface_get_gl_buffer(dst_surface);
1061 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1063 else
1065 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1066 buffer = GL_COLOR_ATTACHMENT0;
1069 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1070 context_set_draw_buffer(context, buffer);
1071 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1072 context_invalidate_state(context, STATE_FRAMEBUFFER);
1074 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1075 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
1076 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
1077 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
1078 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
1080 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
1081 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1083 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1084 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1085 checkGLcall("glBlitFramebuffer()");
1087 if (wined3d_settings.strict_draw_ordering
1088 || (dst_location == SFLAG_INDRAWABLE
1089 && dst_surface->swapchain->front_buffer == dst_surface))
1090 gl_info->gl_ops.gl.p_glFlush();
1092 context_release(context);
1095 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1096 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
1097 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
1099 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1100 return FALSE;
1102 /* Source and/or destination need to be on the GL side */
1103 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
1104 return FALSE;
1106 switch (blit_op)
1108 case WINED3D_BLIT_OP_COLOR_BLIT:
1109 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1110 return FALSE;
1111 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1112 return FALSE;
1113 break;
1115 case WINED3D_BLIT_OP_DEPTH_BLIT:
1116 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1117 return FALSE;
1118 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1119 return FALSE;
1120 break;
1122 default:
1123 return FALSE;
1126 if (!(src_format->id == dst_format->id
1127 || (is_identity_fixup(src_format->color_fixup)
1128 && is_identity_fixup(dst_format->color_fixup))))
1129 return FALSE;
1131 return TRUE;
1134 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1135 DWORD color, struct wined3d_color *float_color)
1137 const struct wined3d_format *format = surface->resource.format;
1138 const struct wined3d_device *device = surface->resource.device;
1140 switch (format->id)
1142 case WINED3DFMT_P8_UINT:
1143 if (surface->palette)
1145 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1146 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1147 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1149 else
1151 float_color->r = 0.0f;
1152 float_color->g = 0.0f;
1153 float_color->b = 0.0f;
1155 float_color->a = swapchain_is_p8(device->swapchains[0]) ? color / 255.0f : 1.0f;
1156 break;
1158 case WINED3DFMT_B5G6R5_UNORM:
1159 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1160 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1161 float_color->b = (color & 0x1f) / 31.0f;
1162 float_color->a = 1.0f;
1163 break;
1165 case WINED3DFMT_B8G8R8_UNORM:
1166 case WINED3DFMT_B8G8R8X8_UNORM:
1167 float_color->r = D3DCOLOR_R(color);
1168 float_color->g = D3DCOLOR_G(color);
1169 float_color->b = D3DCOLOR_B(color);
1170 float_color->a = 1.0f;
1171 break;
1173 case WINED3DFMT_B8G8R8A8_UNORM:
1174 float_color->r = D3DCOLOR_R(color);
1175 float_color->g = D3DCOLOR_G(color);
1176 float_color->b = D3DCOLOR_B(color);
1177 float_color->a = D3DCOLOR_A(color);
1178 break;
1180 default:
1181 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1182 return FALSE;
1185 return TRUE;
1188 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1190 const struct wined3d_format *format = surface->resource.format;
1192 switch (format->id)
1194 case WINED3DFMT_S1_UINT_D15_UNORM:
1195 *float_depth = depth / (float)0x00007fff;
1196 break;
1198 case WINED3DFMT_D16_UNORM:
1199 *float_depth = depth / (float)0x0000ffff;
1200 break;
1202 case WINED3DFMT_D24_UNORM_S8_UINT:
1203 case WINED3DFMT_X8D24_UNORM:
1204 *float_depth = depth / (float)0x00ffffff;
1205 break;
1207 case WINED3DFMT_D32_UNORM:
1208 *float_depth = depth / (float)0xffffffff;
1209 break;
1211 default:
1212 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1213 return FALSE;
1216 return TRUE;
1219 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1221 const struct wined3d_resource *resource = &surface->resource;
1222 struct wined3d_device *device = resource->device;
1223 const struct blit_shader *blitter;
1225 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1226 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1227 if (!blitter)
1229 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1230 return WINED3DERR_INVALIDCALL;
1233 return blitter->depth_fill(device, surface, rect, depth);
1236 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1237 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1239 struct wined3d_device *device = src_surface->resource.device;
1241 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1242 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1243 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1244 return WINED3DERR_INVALIDCALL;
1246 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1248 surface_modify_ds_location(dst_surface, dst_location,
1249 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1251 return WINED3D_OK;
1254 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1255 struct wined3d_surface *render_target)
1257 TRACE("surface %p, render_target %p.\n", surface, render_target);
1259 /* TODO: Check surface sizes, pools, etc. */
1261 if (render_target->resource.multisample_type)
1262 return WINED3DERR_INVALIDCALL;
1264 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1267 /* Context activation is done by the caller. */
1268 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1270 if (surface->flags & SFLAG_DIBSECTION)
1272 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1274 else
1276 if (!surface->resource.heap_memory)
1277 wined3d_resource_allocate_sysmem(&surface->resource);
1278 else if (!(surface->flags & SFLAG_CLIENT))
1279 ERR("Surface %p has heap_memory %p and flags %#x.\n",
1280 surface, surface->resource.heap_memory, surface->flags);
1282 surface->resource.allocatedMemory = surface->resource.heap_memory;
1285 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1286 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1287 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1288 surface->resource.size, surface->resource.allocatedMemory));
1289 checkGLcall("glGetBufferSubDataARB");
1290 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1291 checkGLcall("glDeleteBuffersARB");
1293 surface->pbo = 0;
1294 surface->flags &= ~SFLAG_PBO;
1297 static BOOL surface_init_sysmem(struct wined3d_surface *surface)
1299 if (!surface->resource.allocatedMemory)
1301 if (!surface->resource.heap_memory)
1303 if (!wined3d_resource_allocate_sysmem(&surface->resource))
1305 ERR("Failed to allocate system memory.\n");
1306 return FALSE;
1309 else if (!(surface->flags & SFLAG_CLIENT))
1311 ERR("Surface %p has heap_memory %p and flags %#x.\n",
1312 surface, surface->resource.heap_memory, surface->flags);
1315 surface->resource.allocatedMemory = surface->resource.heap_memory;
1317 else
1319 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
1322 surface_validate_location(surface, SFLAG_INSYSMEM);
1323 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
1325 return TRUE;
1328 static void surface_unload(struct wined3d_resource *resource)
1330 struct wined3d_surface *surface = surface_from_resource(resource);
1331 struct wined3d_renderbuffer_entry *entry, *entry2;
1332 struct wined3d_device *device = resource->device;
1333 const struct wined3d_gl_info *gl_info;
1334 struct wined3d_context *context;
1336 TRACE("surface %p.\n", surface);
1338 if (resource->pool == WINED3D_POOL_DEFAULT)
1340 /* Default pool resources are supposed to be destroyed before Reset is called.
1341 * Implicit resources stay however. So this means we have an implicit render target
1342 * or depth stencil. The content may be destroyed, but we still have to tear down
1343 * opengl resources, so we cannot leave early.
1345 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1346 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1347 * or the depth stencil into an FBO the texture or render buffer will be removed
1348 * and all flags get lost
1350 if (!(surface->flags & SFLAG_PBO))
1351 surface_init_sysmem(surface);
1352 /* We also get here when the ddraw swapchain is destroyed, for example
1353 * for a mode switch. In this case this surface won't necessarily be
1354 * an implicit surface. We have to mark it lost so that the
1355 * application can restore it after the mode switch. */
1356 surface->flags |= SFLAG_LOST;
1358 else
1360 surface_load_location(surface, SFLAG_INSYSMEM);
1362 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
1363 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1365 context = context_acquire(device, NULL);
1366 gl_info = context->gl_info;
1368 /* Destroy PBOs, but load them into real sysmem before */
1369 if (surface->flags & SFLAG_PBO)
1370 surface_remove_pbo(surface, gl_info);
1372 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1373 * all application-created targets the application has to release the surface
1374 * before calling _Reset
1376 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1378 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1379 list_remove(&entry->entry);
1380 HeapFree(GetProcessHeap(), 0, entry);
1382 list_init(&surface->renderbuffers);
1383 surface->current_renderbuffer = NULL;
1385 if (surface->rb_multisample)
1387 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1388 surface->rb_multisample = 0;
1390 if (surface->rb_resolved)
1392 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1393 surface->rb_resolved = 0;
1396 context_release(context);
1398 resource_unload(resource);
1401 static const struct wined3d_resource_ops surface_resource_ops =
1403 surface_unload,
1406 static const struct wined3d_surface_ops surface_ops =
1408 surface_private_setup,
1409 surface_realize_palette,
1410 surface_map,
1411 surface_unmap,
1414 /*****************************************************************************
1415 * Initializes the GDI surface, aka creates the DIB section we render to
1416 * The DIB section creation is done by calling GetDC, which will create the
1417 * section and releasing the dc to allow the app to use it. The dib section
1418 * will stay until the surface is released
1420 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1421 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1422 * avoid confusion in the shared surface code.
1424 * Returns:
1425 * WINED3D_OK on success
1426 * The return values of called methods on failure
1428 *****************************************************************************/
1429 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1431 HRESULT hr;
1433 TRACE("surface %p.\n", surface);
1435 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1437 ERR("Overlays not yet supported by GDI surfaces.\n");
1438 return WINED3DERR_INVALIDCALL;
1441 /* Sysmem textures have memory already allocated - release it,
1442 * this avoids an unnecessary memcpy. */
1443 hr = surface_create_dib_section(surface);
1444 if (SUCCEEDED(hr))
1446 wined3d_resource_free_sysmem(&surface->resource);
1447 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1450 /* We don't mind the nonpow2 stuff in GDI. */
1451 surface->pow2Width = surface->resource.width;
1452 surface->pow2Height = surface->resource.height;
1454 return WINED3D_OK;
1457 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1459 struct wined3d_palette *palette = surface->palette;
1461 TRACE("surface %p.\n", surface);
1463 if (!palette) return;
1465 if (surface->flags & SFLAG_DIBSECTION)
1467 RGBQUAD col[256];
1468 unsigned int i;
1470 TRACE("Updating the DC's palette.\n");
1472 for (i = 0; i < 256; ++i)
1474 col[i].rgbRed = palette->palents[i].peRed;
1475 col[i].rgbGreen = palette->palents[i].peGreen;
1476 col[i].rgbBlue = palette->palents[i].peBlue;
1477 col[i].rgbReserved = 0;
1479 SetDIBColorTable(surface->hDC, 0, 256, col);
1482 /* Update the image because of the palette change. Some games like e.g.
1483 * Red Alert call SetEntries a lot to implement fading. */
1484 /* Tell the swapchain to update the screen. */
1485 if (surface->swapchain && surface == surface->swapchain->front_buffer)
1486 x11_copy_to_screen(surface->swapchain, NULL);
1489 static BYTE *gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
1491 TRACE("surface %p, rect %s, flags %#x.\n",
1492 surface, wine_dbgstr_rect(rect), flags);
1494 return surface->resource.allocatedMemory;
1497 static void gdi_surface_unmap(struct wined3d_surface *surface)
1499 TRACE("surface %p.\n", surface);
1501 /* Tell the swapchain to update the screen. */
1502 if (surface->swapchain && surface == surface->swapchain->front_buffer)
1503 x11_copy_to_screen(surface->swapchain, &surface->lockedRect);
1505 memset(&surface->lockedRect, 0, sizeof(RECT));
1508 static const struct wined3d_surface_ops gdi_surface_ops =
1510 gdi_surface_private_setup,
1511 gdi_surface_realize_palette,
1512 gdi_surface_map,
1513 gdi_surface_unmap,
1516 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target, GLint level)
1518 TRACE("surface %p, target %#x.\n", surface, target);
1520 if (surface->texture_target != target)
1522 if (target == GL_TEXTURE_RECTANGLE_ARB)
1524 surface->flags &= ~SFLAG_NORMCOORD;
1526 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
1528 surface->flags |= SFLAG_NORMCOORD;
1531 surface->texture_target = target;
1532 surface->texture_level = level;
1533 surface_force_reload(surface);
1536 /* This call just downloads data, the caller is responsible for binding the
1537 * correct texture. */
1538 /* Context activation is done by the caller. */
1539 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1541 const struct wined3d_format *format = surface->resource.format;
1542 struct wined3d_bo_address data;
1544 /* Only support read back of converted P8 surfaces. */
1545 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1547 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1548 return;
1551 surface_get_memory(surface, &data);
1553 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1555 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
1556 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1558 if (data.buffer_object)
1560 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1561 checkGLcall("glBindBufferARB");
1562 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
1563 checkGLcall("glGetCompressedTexImageARB");
1564 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1565 checkGLcall("glBindBufferARB");
1567 else
1569 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
1570 surface->texture_level, data.addr));
1571 checkGLcall("glGetCompressedTexImageARB");
1574 else
1576 void *mem;
1577 GLenum gl_format = format->glFormat;
1578 GLenum gl_type = format->glType;
1579 int src_pitch = 0;
1580 int dst_pitch = 0;
1582 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
1583 if (format->id == WINED3DFMT_P8_UINT && swapchain_is_p8(surface->resource.device->swapchains[0]))
1585 gl_format = GL_ALPHA;
1586 gl_type = GL_UNSIGNED_BYTE;
1589 if (surface->flags & SFLAG_NONPOW2)
1591 unsigned char alignment = surface->resource.device->surface_alignment;
1592 src_pitch = format->byte_count * surface->pow2Width;
1593 dst_pitch = wined3d_surface_get_pitch(surface);
1594 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1595 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1597 else
1599 mem = data.addr;
1602 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1603 surface, surface->texture_level, gl_format, gl_type, mem);
1605 if (data.buffer_object)
1607 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1608 checkGLcall("glBindBufferARB");
1610 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1611 gl_format, gl_type, NULL);
1612 checkGLcall("glGetTexImage");
1614 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1615 checkGLcall("glBindBufferARB");
1617 else
1619 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1620 gl_format, gl_type, mem);
1621 checkGLcall("glGetTexImage");
1624 if (surface->flags & SFLAG_NONPOW2)
1626 const BYTE *src_data;
1627 BYTE *dst_data;
1628 UINT y;
1630 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1631 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1632 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1634 * We're doing this...
1636 * instead of boxing the texture :
1637 * |<-texture width ->| -->pow2width| /\
1638 * |111111111111111111| | |
1639 * |222 Texture 222222| boxed empty | texture height
1640 * |3333 Data 33333333| | |
1641 * |444444444444444444| | \/
1642 * ----------------------------------- |
1643 * | boxed empty | boxed empty | pow2height
1644 * | | | \/
1645 * -----------------------------------
1648 * we're repacking the data to the expected texture width
1650 * |<-texture width ->| -->pow2width| /\
1651 * |111111111111111111222222222222222| |
1652 * |222333333333333333333444444444444| texture height
1653 * |444444 | |
1654 * | | \/
1655 * | | |
1656 * | empty | pow2height
1657 * | | \/
1658 * -----------------------------------
1660 * == is the same as
1662 * |<-texture width ->| /\
1663 * |111111111111111111|
1664 * |222222222222222222|texture height
1665 * |333333333333333333|
1666 * |444444444444444444| \/
1667 * --------------------
1669 * this also means that any references to surface memory should work with the data as if were a
1670 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
1672 * internally the texture is still stored in a boxed format so any references to textureName will
1673 * get a boxed texture with width pow2width and not a texture of width resource.width.
1675 * Performance should not be an issue, because applications normally do not lock the surfaces when
1676 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
1677 * and doesn't have to be re-read. */
1678 src_data = mem;
1679 dst_data = data.addr;
1680 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1681 for (y = 0; y < surface->resource.height; ++y)
1683 memcpy(dst_data, src_data, dst_pitch);
1684 src_data += src_pitch;
1685 dst_data += dst_pitch;
1688 HeapFree(GetProcessHeap(), 0, mem);
1693 /* This call just uploads data, the caller is responsible for binding the
1694 * correct texture. */
1695 /* Context activation is done by the caller. */
1696 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1697 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1698 BOOL srgb, const struct wined3d_bo_address *data)
1700 UINT update_w = src_rect->right - src_rect->left;
1701 UINT update_h = src_rect->bottom - src_rect->top;
1703 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1704 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1705 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1707 if (surface->resource.map_count)
1709 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
1710 surface->flags |= SFLAG_PIN_SYSMEM;
1713 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1715 update_h *= format->height_scale.numerator;
1716 update_h /= format->height_scale.denominator;
1719 if (data->buffer_object)
1721 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
1722 checkGLcall("glBindBufferARB");
1725 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1727 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1728 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1729 const BYTE *addr = data->addr;
1730 GLenum internal;
1732 addr += (src_rect->top / format->block_height) * src_pitch;
1733 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1735 if (srgb)
1736 internal = format->glGammaInternal;
1737 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
1738 internal = format->rtInternal;
1739 else
1740 internal = format->glInternal;
1742 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
1743 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1744 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1746 if (row_length == src_pitch)
1748 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1749 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1751 else
1753 UINT row, y;
1755 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
1756 * can't use the unpack row length like below. */
1757 for (row = 0, y = dst_point->y; row < row_count; ++row)
1759 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1760 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1761 y += format->block_height;
1762 addr += src_pitch;
1765 checkGLcall("glCompressedTexSubImage2DARB");
1767 else
1769 const BYTE *addr = data->addr;
1771 addr += src_rect->top * src_pitch;
1772 addr += src_rect->left * format->byte_count;
1774 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1775 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1776 update_w, update_h, format->glFormat, format->glType, addr);
1778 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1779 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1780 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1781 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1782 checkGLcall("glTexSubImage2D");
1785 if (data->buffer_object)
1787 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1788 checkGLcall("glBindBufferARB");
1791 if (wined3d_settings.strict_draw_ordering)
1792 gl_info->gl_ops.gl.p_glFlush();
1794 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1796 struct wined3d_device *device = surface->resource.device;
1797 unsigned int i;
1799 for (i = 0; i < device->context_count; ++i)
1801 context_surface_update(device->contexts[i], surface);
1806 static HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck, BOOL use_texturing,
1807 struct wined3d_format *format, enum wined3d_conversion_type *conversion_type)
1809 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
1810 const struct wined3d_device *device = surface->resource.device;
1811 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1812 BOOL blit_supported = FALSE;
1814 /* Copy the default values from the surface. Below we might perform fixups */
1815 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
1816 *format = *surface->resource.format;
1817 *conversion_type = WINED3D_CT_NONE;
1819 /* Ok, now look if we have to do any conversion */
1820 switch (surface->resource.format->id)
1822 case WINED3DFMT_P8_UINT:
1823 /* Below the call to blit_supported is disabled for Wine 1.2
1824 * because the function isn't operating correctly yet. At the
1825 * moment 8-bit blits are handled in software and if certain GL
1826 * extensions are around, surface conversion is performed at
1827 * upload time. The blit_supported call recognizes it as a
1828 * destination fixup. This type of upload 'fixup' and 8-bit to
1829 * 8-bit blits need to be handled by the blit_shader.
1830 * TODO: get rid of this #if 0. */
1831 #if 0
1832 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1833 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
1834 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
1835 #endif
1836 blit_supported = gl_info->supported[ARB_FRAGMENT_PROGRAM];
1838 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
1839 * texturing. Further also use conversion in case of color keying.
1840 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1841 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1842 * conflicts with this.
1844 if (!((blit_supported && surface->swapchain && surface == surface->swapchain->front_buffer))
1845 || colorkey_active || !use_texturing)
1847 format->glFormat = GL_RGBA;
1848 format->glInternal = GL_RGBA;
1849 format->glType = GL_UNSIGNED_BYTE;
1850 format->conv_byte_count = 4;
1851 if (colorkey_active)
1852 *conversion_type = WINED3D_CT_PALETTED_CK;
1853 else
1854 *conversion_type = WINED3D_CT_PALETTED;
1856 break;
1858 case WINED3DFMT_B2G3R3_UNORM:
1859 /* **********************
1860 GL_UNSIGNED_BYTE_3_3_2
1861 ********************** */
1862 if (colorkey_active) {
1863 /* This texture format will never be used.. So do not care about color keying
1864 up until the point in time it will be needed :-) */
1865 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1867 break;
1869 case WINED3DFMT_B5G6R5_UNORM:
1870 if (colorkey_active)
1872 *conversion_type = WINED3D_CT_CK_565;
1873 format->glFormat = GL_RGBA;
1874 format->glInternal = GL_RGB5_A1;
1875 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
1876 format->conv_byte_count = 2;
1878 break;
1880 case WINED3DFMT_B5G5R5X1_UNORM:
1881 if (colorkey_active)
1883 *conversion_type = WINED3D_CT_CK_5551;
1884 format->glFormat = GL_BGRA;
1885 format->glInternal = GL_RGB5_A1;
1886 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1887 format->conv_byte_count = 2;
1889 break;
1891 case WINED3DFMT_B8G8R8_UNORM:
1892 if (colorkey_active)
1894 *conversion_type = WINED3D_CT_CK_RGB24;
1895 format->glFormat = GL_RGBA;
1896 format->glInternal = GL_RGBA8;
1897 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1898 format->conv_byte_count = 4;
1900 break;
1902 case WINED3DFMT_B8G8R8X8_UNORM:
1903 if (colorkey_active)
1905 *conversion_type = WINED3D_CT_RGB32_888;
1906 format->glFormat = GL_RGBA;
1907 format->glInternal = GL_RGBA8;
1908 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1909 format->conv_byte_count = 4;
1911 break;
1913 case WINED3DFMT_B8G8R8A8_UNORM:
1914 if (colorkey_active)
1916 *conversion_type = WINED3D_CT_CK_ARGB32;
1917 format->conv_byte_count = 4;
1919 break;
1921 default:
1922 break;
1925 if (*conversion_type != WINED3D_CT_NONE)
1927 format->rtInternal = format->glInternal;
1928 format->glGammaInternal = format->glInternal;
1931 return WINED3D_OK;
1934 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
1936 UINT width_mask, height_mask;
1938 if (!rect->left && !rect->top
1939 && rect->right == surface->resource.width
1940 && rect->bottom == surface->resource.height)
1941 return TRUE;
1943 /* This assumes power of two block sizes, but NPOT block sizes would be
1944 * silly anyway. */
1945 width_mask = surface->resource.format->block_width - 1;
1946 height_mask = surface->resource.format->block_height - 1;
1948 if (!(rect->left & width_mask) && !(rect->top & height_mask)
1949 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
1950 return TRUE;
1952 return FALSE;
1955 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1956 struct wined3d_surface *src_surface, const RECT *src_rect)
1958 const struct wined3d_format *src_format;
1959 const struct wined3d_format *dst_format;
1960 const struct wined3d_gl_info *gl_info;
1961 enum wined3d_conversion_type convert;
1962 struct wined3d_context *context;
1963 struct wined3d_bo_address data;
1964 struct wined3d_format format;
1965 UINT update_w, update_h;
1966 UINT dst_w, dst_h;
1967 RECT r, dst_rect;
1968 UINT src_pitch;
1969 POINT p;
1971 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1972 dst_surface, wine_dbgstr_point(dst_point),
1973 src_surface, wine_dbgstr_rect(src_rect));
1975 src_format = src_surface->resource.format;
1976 dst_format = dst_surface->resource.format;
1978 if (src_format->id != dst_format->id)
1980 WARN("Source and destination surfaces should have the same format.\n");
1981 return WINED3DERR_INVALIDCALL;
1984 if (!dst_point)
1986 p.x = 0;
1987 p.y = 0;
1988 dst_point = &p;
1990 else if (dst_point->x < 0 || dst_point->y < 0)
1992 WARN("Invalid destination point.\n");
1993 return WINED3DERR_INVALIDCALL;
1996 if (!src_rect)
1998 r.left = 0;
1999 r.top = 0;
2000 r.right = src_surface->resource.width;
2001 r.bottom = src_surface->resource.height;
2002 src_rect = &r;
2004 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
2005 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
2007 WARN("Invalid source rectangle.\n");
2008 return WINED3DERR_INVALIDCALL;
2011 dst_w = dst_surface->resource.width;
2012 dst_h = dst_surface->resource.height;
2014 update_w = src_rect->right - src_rect->left;
2015 update_h = src_rect->bottom - src_rect->top;
2017 if (update_w > dst_w || dst_point->x > dst_w - update_w
2018 || update_h > dst_h || dst_point->y > dst_h - update_h)
2020 WARN("Destination out of bounds.\n");
2021 return WINED3DERR_INVALIDCALL;
2024 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
2026 WARN("Source rectangle not block-aligned.\n");
2027 return WINED3DERR_INVALIDCALL;
2030 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
2031 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
2033 WARN("Destination rectangle not block-aligned.\n");
2034 return WINED3DERR_INVALIDCALL;
2037 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
2038 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
2039 if (convert != WINED3D_CT_NONE || format.convert)
2040 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
2042 context = context_acquire(dst_surface->resource.device, NULL);
2043 gl_info = context->gl_info;
2045 /* Only load the surface for partial updates. For newly allocated texture
2046 * the texture wouldn't be the current location, and we'd upload zeroes
2047 * just to overwrite them again. */
2048 if (update_w == dst_w && update_h == dst_h)
2049 surface_prepare_texture(dst_surface, context, FALSE);
2050 else
2051 surface_load_location(dst_surface, SFLAG_INTEXTURE);
2052 wined3d_texture_bind(dst_surface->container, context, FALSE);
2054 surface_get_memory(src_surface, &data);
2055 src_pitch = wined3d_surface_get_pitch(src_surface);
2057 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
2059 context_invalidate_active_texture(context);
2061 context_release(context);
2063 surface_validate_location(dst_surface, SFLAG_INTEXTURE);
2064 surface_invalidate_location(dst_surface, ~SFLAG_INTEXTURE);
2066 return WINED3D_OK;
2069 /* This call just allocates the texture, the caller is responsible for binding
2070 * the correct texture. */
2071 /* Context activation is done by the caller. */
2072 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2073 const struct wined3d_format *format, BOOL srgb)
2075 BOOL disable_client_storage = FALSE;
2076 GLsizei width = surface->pow2Width;
2077 GLsizei height = surface->pow2Height;
2078 const BYTE *mem = NULL;
2079 GLenum internal;
2081 if (srgb)
2083 internal = format->glGammaInternal;
2085 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2087 internal = format->rtInternal;
2089 else
2091 internal = format->glInternal;
2094 if (!internal)
2095 FIXME("No GL internal format for format %s.\n", debug_d3dformat(format->id));
2097 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
2099 height *= format->height_scale.numerator;
2100 height /= format->height_scale.denominator;
2103 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",
2104 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2105 internal, width, height, format->glFormat, format->glType);
2107 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2109 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2110 || !surface->resource.allocatedMemory)
2112 /* In some cases we want to disable client storage.
2113 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2114 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2115 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2116 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2118 surface->flags &= ~SFLAG_CLIENT;
2120 else
2122 surface->flags |= SFLAG_CLIENT;
2124 /* Point OpenGL to our allocated texture memory. Do not use
2125 * resource.allocatedMemory here because it might point into a
2126 * PBO. Instead use heap_memory. */
2127 mem = surface->resource.heap_memory;
2129 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2130 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2131 disable_client_storage = TRUE;
2135 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2137 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2138 internal, width, height, 0, surface->resource.size, mem));
2139 checkGLcall("glCompressedTexImage2DARB");
2141 else
2143 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
2144 internal, width, height, 0, format->glFormat, format->glType, mem);
2145 checkGLcall("glTexImage2D");
2148 if (disable_client_storage)
2150 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2151 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2155 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2156 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2157 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2158 /* Context activation is done by the caller. */
2159 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2161 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2162 struct wined3d_renderbuffer_entry *entry;
2163 GLuint renderbuffer = 0;
2164 unsigned int src_width, src_height;
2165 unsigned int width, height;
2167 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2169 width = rt->pow2Width;
2170 height = rt->pow2Height;
2172 else
2174 width = surface->pow2Width;
2175 height = surface->pow2Height;
2178 src_width = surface->pow2Width;
2179 src_height = surface->pow2Height;
2181 /* A depth stencil smaller than the render target is not valid */
2182 if (width > src_width || height > src_height) return;
2184 /* Remove any renderbuffer set if the sizes match */
2185 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2186 || (width == src_width && height == src_height))
2188 surface->current_renderbuffer = NULL;
2189 return;
2192 /* Look if we've already got a renderbuffer of the correct dimensions */
2193 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2195 if (entry->width == width && entry->height == height)
2197 renderbuffer = entry->id;
2198 surface->current_renderbuffer = entry;
2199 break;
2203 if (!renderbuffer)
2205 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2206 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2207 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2208 surface->resource.format->glInternal, width, height);
2210 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2211 entry->width = width;
2212 entry->height = height;
2213 entry->id = renderbuffer;
2214 list_add_head(&surface->renderbuffers, &entry->entry);
2216 surface->current_renderbuffer = entry;
2219 checkGLcall("set_compatible_renderbuffer");
2222 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2224 const struct wined3d_swapchain *swapchain = surface->swapchain;
2226 TRACE("surface %p.\n", surface);
2228 if (!swapchain)
2230 ERR("Surface %p is not on a swapchain.\n", surface);
2231 return GL_NONE;
2234 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2236 if (swapchain->render_to_fbo)
2238 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2239 return GL_COLOR_ATTACHMENT0;
2241 TRACE("Returning GL_BACK\n");
2242 return GL_BACK;
2244 else if (surface == swapchain->front_buffer)
2246 TRACE("Returning GL_FRONT\n");
2247 return GL_FRONT;
2250 FIXME("Higher back buffer, returning GL_BACK\n");
2251 return GL_BACK;
2254 void surface_load(struct wined3d_surface *surface, BOOL srgb)
2256 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2257 BOOL ck_changed;
2259 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2261 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
2262 ERR("Not supported on scratch surfaces.\n");
2264 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2266 /* Reload if either the texture and sysmem have different ideas about the
2267 * color key, or the actual key values changed. */
2268 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2269 && (surface->gl_color_key.color_space_low_value != surface->src_blt_color_key.color_space_low_value
2270 || surface->gl_color_key.color_space_high_value != surface->src_blt_color_key.color_space_high_value)))
2272 TRACE("Reloading because of color keying\n");
2273 /* To perform the color key conversion we need a sysmem copy of
2274 * the surface. Make sure we have it. */
2276 surface_load_location(surface, SFLAG_INSYSMEM);
2277 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
2278 /* Switching color keying on / off may change the internal format. */
2279 if (ck_changed)
2280 surface_force_reload(surface);
2282 else if (!(surface->flags & flag))
2284 TRACE("Reloading because surface is dirty.\n");
2286 else
2288 TRACE("surface is already in texture\n");
2289 return;
2292 surface_load_location(surface, flag);
2293 surface_evict_sysmem(surface);
2296 /* See also float_16_to_32() in wined3d_private.h */
2297 static inline unsigned short float_32_to_16(const float *in)
2299 int exp = 0;
2300 float tmp = fabsf(*in);
2301 unsigned int mantissa;
2302 unsigned short ret;
2304 /* Deal with special numbers */
2305 if (*in == 0.0f)
2306 return 0x0000;
2307 if (isnan(*in))
2308 return 0x7c01;
2309 if (isinf(*in))
2310 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2312 if (tmp < powf(2, 10))
2316 tmp = tmp * 2.0f;
2317 exp--;
2318 } while (tmp < powf(2, 10));
2320 else if (tmp >= powf(2, 11))
2324 tmp /= 2.0f;
2325 exp++;
2326 } while (tmp >= powf(2, 11));
2329 mantissa = (unsigned int)tmp;
2330 if (tmp - mantissa >= 0.5f)
2331 ++mantissa; /* Round to nearest, away from zero. */
2333 exp += 10; /* Normalize the mantissa. */
2334 exp += 15; /* Exponent is encoded with excess 15. */
2336 if (exp > 30) /* too big */
2338 ret = 0x7c00; /* INF */
2340 else if (exp <= 0)
2342 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2343 while (exp <= 0)
2345 mantissa = mantissa >> 1;
2346 ++exp;
2348 ret = mantissa & 0x3ff;
2350 else
2352 ret = (exp << 10) | (mantissa & 0x3ff);
2355 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2356 return ret;
2359 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2361 ULONG refcount;
2363 TRACE("surface %p, swapchain %p, container %p.\n",
2364 surface, surface->swapchain, surface->container);
2366 if (surface->swapchain)
2367 return wined3d_swapchain_incref(surface->swapchain);
2369 if (surface->container)
2370 return wined3d_texture_incref(surface->container);
2372 refcount = InterlockedIncrement(&surface->resource.ref);
2373 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2375 return refcount;
2378 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2380 ULONG refcount;
2382 TRACE("surface %p, swapchain %p, container %p.\n",
2383 surface, surface->swapchain, surface->container);
2385 if (surface->swapchain)
2386 return wined3d_swapchain_decref(surface->swapchain);
2388 if (surface->container)
2389 return wined3d_texture_decref(surface->container);
2391 refcount = InterlockedDecrement(&surface->resource.ref);
2392 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2394 if (!refcount)
2396 surface_cleanup(surface);
2397 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2399 TRACE("Destroyed surface %p.\n", surface);
2400 HeapFree(GetProcessHeap(), 0, surface);
2403 return refcount;
2406 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2408 return resource_set_priority(&surface->resource, priority);
2411 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2413 return resource_get_priority(&surface->resource);
2416 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2418 TRACE("surface %p.\n", surface);
2420 if (!surface->resource.device->d3d_initialized)
2422 ERR("D3D not initialized.\n");
2423 return;
2426 wined3d_texture_preload(surface->container);
2429 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2431 TRACE("surface %p.\n", surface);
2433 return surface->resource.parent;
2436 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2438 TRACE("surface %p.\n", surface);
2440 return &surface->resource;
2443 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2445 TRACE("surface %p, flags %#x.\n", surface, flags);
2447 switch (flags)
2449 case WINEDDGBS_CANBLT:
2450 case WINEDDGBS_ISBLTDONE:
2451 return WINED3D_OK;
2453 default:
2454 return WINED3DERR_INVALIDCALL;
2458 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2460 TRACE("surface %p, flags %#x.\n", surface, flags);
2462 /* XXX: DDERR_INVALIDSURFACETYPE */
2464 switch (flags)
2466 case WINEDDGFS_CANFLIP:
2467 case WINEDDGFS_ISFLIPDONE:
2468 return WINED3D_OK;
2470 default:
2471 return WINED3DERR_INVALIDCALL;
2475 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2477 TRACE("surface %p.\n", surface);
2479 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2480 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2483 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2485 TRACE("surface %p.\n", surface);
2487 surface->flags &= ~SFLAG_LOST;
2488 return WINED3D_OK;
2491 void CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2493 TRACE("surface %p, palette %p.\n", surface, palette);
2495 if (surface->palette == palette)
2497 TRACE("Nop palette change.\n");
2498 return;
2501 surface->palette = palette;
2502 if (palette)
2503 surface->surface_ops->surface_realize_palette(surface);
2506 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
2507 DWORD flags, const struct wined3d_color_key *color_key)
2509 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
2511 if (flags & WINEDDCKEY_COLORSPACE)
2513 FIXME(" colorkey value not supported (%08x) !\n", flags);
2514 return WINED3DERR_INVALIDCALL;
2517 /* Dirtify the surface, but only if a key was changed. */
2518 if (color_key)
2520 switch (flags & ~WINEDDCKEY_COLORSPACE)
2522 case WINEDDCKEY_DESTBLT:
2523 surface->dst_blt_color_key = *color_key;
2524 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
2525 break;
2527 case WINEDDCKEY_DESTOVERLAY:
2528 surface->dst_overlay_color_key = *color_key;
2529 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
2530 break;
2532 case WINEDDCKEY_SRCOVERLAY:
2533 surface->src_overlay_color_key = *color_key;
2534 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
2535 break;
2537 case WINEDDCKEY_SRCBLT:
2538 surface->src_blt_color_key = *color_key;
2539 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
2540 break;
2543 else
2545 switch (flags & ~WINEDDCKEY_COLORSPACE)
2547 case WINEDDCKEY_DESTBLT:
2548 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
2549 break;
2551 case WINEDDCKEY_DESTOVERLAY:
2552 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
2553 break;
2555 case WINEDDCKEY_SRCOVERLAY:
2556 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
2557 break;
2559 case WINEDDCKEY_SRCBLT:
2560 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
2561 break;
2565 return WINED3D_OK;
2568 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
2570 TRACE("surface %p.\n", surface);
2572 return surface->palette;
2575 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2577 const struct wined3d_format *format = surface->resource.format;
2578 DWORD pitch;
2580 TRACE("surface %p.\n", surface);
2582 if (surface->pitch)
2583 return surface->pitch;
2585 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
2587 /* Since compressed formats are block based, pitch means the amount of
2588 * bytes to the next row of block rather than the next row of pixels. */
2589 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
2590 pitch = row_block_count * format->block_byte_count;
2592 else
2594 unsigned char alignment = surface->resource.device->surface_alignment;
2595 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
2596 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2599 TRACE("Returning %u.\n", pitch);
2601 return pitch;
2604 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem, UINT pitch)
2606 TRACE("surface %p, mem %p.\n", surface, mem);
2608 if (surface->resource.map_count || (surface->flags & SFLAG_DCINUSE))
2610 WARN("Surface is mapped or the DC is in use.\n");
2611 return WINED3DERR_INVALIDCALL;
2614 if (mem && mem != surface->resource.allocatedMemory)
2616 /* Do I have to copy the old surface content? */
2617 if (surface->flags & SFLAG_DIBSECTION)
2619 DeleteDC(surface->hDC);
2620 DeleteObject(surface->dib.DIBsection);
2621 surface->dib.bitmap_data = NULL;
2622 surface->resource.allocatedMemory = NULL;
2623 surface->hDC = NULL;
2624 surface->flags &= ~SFLAG_DIBSECTION;
2626 else if (!(surface->flags & SFLAG_USERPTR))
2628 wined3d_resource_free_sysmem(&surface->resource);
2630 surface->resource.allocatedMemory = mem;
2631 surface->flags |= SFLAG_USERPTR;
2633 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
2634 surface_validate_location(surface, SFLAG_INSYSMEM);
2635 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
2637 /* For client textures OpenGL has to be notified. */
2638 if (surface->flags & SFLAG_CLIENT)
2639 surface_release_client_storage(surface);
2641 else if (surface->flags & SFLAG_USERPTR)
2643 /* heap_memory should be NULL already. */
2644 if (surface->resource.heap_memory)
2645 ERR("User pointer surface has heap memory allocated.\n");
2647 if (!mem)
2649 surface->resource.allocatedMemory = NULL;
2650 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
2652 if (surface->flags & SFLAG_CLIENT)
2653 surface_release_client_storage(surface);
2655 surface_prepare_system_memory(surface);
2658 surface_validate_location(surface, SFLAG_INSYSMEM);
2659 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
2662 surface->pitch = pitch;
2664 return WINED3D_OK;
2667 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2669 LONG w, h;
2671 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2673 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2675 WARN("Not an overlay surface.\n");
2676 return WINEDDERR_NOTAOVERLAYSURFACE;
2679 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2680 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2681 surface->overlay_destrect.left = x;
2682 surface->overlay_destrect.top = y;
2683 surface->overlay_destrect.right = x + w;
2684 surface->overlay_destrect.bottom = y + h;
2686 return WINED3D_OK;
2689 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2691 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2693 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2695 TRACE("Not an overlay surface.\n");
2696 return WINEDDERR_NOTAOVERLAYSURFACE;
2699 if (!surface->overlay_dest)
2701 TRACE("Overlay not visible.\n");
2702 *x = 0;
2703 *y = 0;
2704 return WINEDDERR_OVERLAYNOTVISIBLE;
2707 *x = surface->overlay_destrect.left;
2708 *y = surface->overlay_destrect.top;
2710 TRACE("Returning position %d, %d.\n", *x, *y);
2712 return WINED3D_OK;
2715 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2716 DWORD flags, struct wined3d_surface *ref)
2718 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2720 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2722 TRACE("Not an overlay surface.\n");
2723 return WINEDDERR_NOTAOVERLAYSURFACE;
2726 return WINED3D_OK;
2729 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2730 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2732 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2733 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2735 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2737 WARN("Not an overlay surface.\n");
2738 return WINEDDERR_NOTAOVERLAYSURFACE;
2740 else if (!dst_surface)
2742 WARN("Dest surface is NULL.\n");
2743 return WINED3DERR_INVALIDCALL;
2746 if (src_rect)
2748 surface->overlay_srcrect = *src_rect;
2750 else
2752 surface->overlay_srcrect.left = 0;
2753 surface->overlay_srcrect.top = 0;
2754 surface->overlay_srcrect.right = surface->resource.width;
2755 surface->overlay_srcrect.bottom = surface->resource.height;
2758 if (dst_rect)
2760 surface->overlay_destrect = *dst_rect;
2762 else
2764 surface->overlay_destrect.left = 0;
2765 surface->overlay_destrect.top = 0;
2766 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2767 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2770 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2772 surface->overlay_dest = NULL;
2773 list_remove(&surface->overlay_entry);
2776 if (flags & WINEDDOVER_SHOW)
2778 if (surface->overlay_dest != dst_surface)
2780 surface->overlay_dest = dst_surface;
2781 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2784 else if (flags & WINEDDOVER_HIDE)
2786 /* tests show that the rectangles are erased on hide */
2787 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2788 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2789 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2790 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2791 surface->overlay_dest = NULL;
2794 return WINED3D_OK;
2797 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
2798 UINT width, UINT height, enum wined3d_format_id format_id,
2799 enum wined3d_multisample_type multisample_type, UINT multisample_quality)
2801 struct wined3d_device *device = surface->resource.device;
2802 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2803 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
2804 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height, 1);
2805 BOOL create_dib = FALSE;
2806 HRESULT hr;
2808 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u.\n",
2809 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type);
2811 if (!resource_size)
2812 return WINED3DERR_INVALIDCALL;
2814 if (device->d3d_initialized)
2815 surface->resource.resource_ops->resource_unload(&surface->resource);
2817 if (surface->flags & SFLAG_DIBSECTION)
2819 DeleteDC(surface->hDC);
2820 DeleteObject(surface->dib.DIBsection);
2821 surface->dib.bitmap_data = NULL;
2822 surface->flags &= ~SFLAG_DIBSECTION;
2823 create_dib = TRUE;
2826 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_USERPTR);
2827 surface->resource.allocatedMemory = NULL;
2828 wined3d_resource_free_sysmem(&surface->resource);
2830 surface->resource.width = width;
2831 surface->resource.height = height;
2832 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2833 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2835 surface->pow2Width = width;
2836 surface->pow2Height = height;
2838 else
2840 surface->pow2Width = surface->pow2Height = 1;
2841 while (surface->pow2Width < width)
2842 surface->pow2Width <<= 1;
2843 while (surface->pow2Height < height)
2844 surface->pow2Height <<= 1;
2847 if (surface->pow2Width != width || surface->pow2Height != height)
2848 surface->flags |= SFLAG_NONPOW2;
2849 else
2850 surface->flags &= ~SFLAG_NONPOW2;
2852 surface->resource.format = format;
2853 surface->resource.multisample_type = multisample_type;
2854 surface->resource.multisample_quality = multisample_quality;
2855 surface->resource.size = resource_size;
2857 if (create_dib)
2859 if (FAILED(hr = surface_create_dib_section(surface)))
2861 ERR("Failed to create dib section, hr %#x.\n", hr);
2862 return hr;
2864 surface->resource.allocatedMemory = surface->dib.bitmap_data;
2866 else if (!surface_init_sysmem(surface))
2867 return E_OUTOFMEMORY;
2869 return WINED3D_OK;
2872 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2873 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2875 unsigned short *dst_s;
2876 const float *src_f;
2877 unsigned int x, y;
2879 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2881 for (y = 0; y < h; ++y)
2883 src_f = (const float *)(src + y * pitch_in);
2884 dst_s = (unsigned short *) (dst + y * pitch_out);
2885 for (x = 0; x < w; ++x)
2887 dst_s[x] = float_32_to_16(src_f + x);
2892 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2893 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2895 static const unsigned char convert_5to8[] =
2897 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2898 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2899 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2900 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2902 static const unsigned char convert_6to8[] =
2904 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2905 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2906 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2907 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2908 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2909 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2910 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2911 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2913 unsigned int x, y;
2915 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2917 for (y = 0; y < h; ++y)
2919 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2920 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2921 for (x = 0; x < w; ++x)
2923 WORD pixel = src_line[x];
2924 dst_line[x] = 0xff000000
2925 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2926 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2927 | convert_5to8[(pixel & 0x001f)];
2932 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2933 * in both cases we're just setting the X / Alpha channel to 0xff. */
2934 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2935 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2937 unsigned int x, y;
2939 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2941 for (y = 0; y < h; ++y)
2943 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2944 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2946 for (x = 0; x < w; ++x)
2948 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2953 static inline BYTE cliptobyte(int x)
2955 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2958 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2959 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2961 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2962 unsigned int x, y;
2964 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2966 for (y = 0; y < h; ++y)
2968 const BYTE *src_line = src + y * pitch_in;
2969 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2970 for (x = 0; x < w; ++x)
2972 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2973 * C = Y - 16; D = U - 128; E = V - 128;
2974 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2975 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2976 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2977 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2978 * U and V are shared between the pixels. */
2979 if (!(x & 1)) /* For every even pixel, read new U and V. */
2981 d = (int) src_line[1] - 128;
2982 e = (int) src_line[3] - 128;
2983 r2 = 409 * e + 128;
2984 g2 = - 100 * d - 208 * e + 128;
2985 b2 = 516 * d + 128;
2987 c2 = 298 * ((int) src_line[0] - 16);
2988 dst_line[x] = 0xff000000
2989 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2990 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2991 | cliptobyte((c2 + b2) >> 8); /* blue */
2992 /* Scale RGB values to 0..255 range,
2993 * then clip them if still not in range (may be negative),
2994 * then shift them within DWORD if necessary. */
2995 src_line += 2;
3000 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3001 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3003 unsigned int x, y;
3004 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3006 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3008 for (y = 0; y < h; ++y)
3010 const BYTE *src_line = src + y * pitch_in;
3011 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3012 for (x = 0; x < w; ++x)
3014 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3015 * C = Y - 16; D = U - 128; E = V - 128;
3016 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3017 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3018 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3019 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3020 * U and V are shared between the pixels. */
3021 if (!(x & 1)) /* For every even pixel, read new U and V. */
3023 d = (int) src_line[1] - 128;
3024 e = (int) src_line[3] - 128;
3025 r2 = 409 * e + 128;
3026 g2 = - 100 * d - 208 * e + 128;
3027 b2 = 516 * d + 128;
3029 c2 = 298 * ((int) src_line[0] - 16);
3030 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3031 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3032 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3033 /* Scale RGB values to 0..255 range,
3034 * then clip them if still not in range (may be negative),
3035 * then shift them within DWORD if necessary. */
3036 src_line += 2;
3041 struct d3dfmt_converter_desc
3043 enum wined3d_format_id from, to;
3044 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3047 static const struct d3dfmt_converter_desc converters[] =
3049 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3050 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3051 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3052 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3053 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3054 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3057 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
3058 enum wined3d_format_id to)
3060 unsigned int i;
3062 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
3064 if (converters[i].from == from && converters[i].to == to)
3065 return &converters[i];
3068 return NULL;
3071 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3073 struct wined3d_map_desc src_map, dst_map;
3074 const struct d3dfmt_converter_desc *conv;
3075 struct wined3d_texture *ret = NULL;
3076 struct wined3d_resource_desc desc;
3077 struct wined3d_surface *dst;
3079 conv = find_converter(source->resource.format->id, to_fmt);
3080 if (!conv)
3082 FIXME("Cannot find a conversion function from format %s to %s.\n",
3083 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3084 return NULL;
3087 /* FIXME: Multisampled conversion? */
3088 wined3d_resource_get_desc(&source->resource, &desc);
3089 desc.resource_type = WINED3D_RTYPE_TEXTURE;
3090 desc.format = to_fmt;
3091 desc.usage = 0;
3092 desc.pool = WINED3D_POOL_SCRATCH;
3093 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
3094 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, &wined3d_null_parent_ops, &ret)))
3096 ERR("Failed to create a destination surface for conversion.\n");
3097 return NULL;
3099 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
3101 memset(&src_map, 0, sizeof(src_map));
3102 memset(&dst_map, 0, sizeof(dst_map));
3104 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
3106 ERR("Failed to lock the source surface.\n");
3107 wined3d_texture_decref(ret);
3108 return NULL;
3110 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
3112 ERR("Failed to lock the destination surface.\n");
3113 wined3d_surface_unmap(source);
3114 wined3d_texture_decref(ret);
3115 return NULL;
3118 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
3119 source->resource.width, source->resource.height);
3121 wined3d_surface_unmap(dst);
3122 wined3d_surface_unmap(source);
3124 return ret;
3127 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3128 unsigned int bpp, UINT pitch, DWORD color)
3130 BYTE *first;
3131 unsigned int x, y;
3133 /* Do first row */
3135 #define COLORFILL_ROW(type) \
3136 do { \
3137 type *d = (type *)buf; \
3138 for (x = 0; x < width; ++x) \
3139 d[x] = (type)color; \
3140 } while(0)
3142 switch (bpp)
3144 case 1:
3145 COLORFILL_ROW(BYTE);
3146 break;
3148 case 2:
3149 COLORFILL_ROW(WORD);
3150 break;
3152 case 3:
3154 BYTE *d = buf;
3155 for (x = 0; x < width; ++x, d += 3)
3157 d[0] = (color ) & 0xff;
3158 d[1] = (color >> 8) & 0xff;
3159 d[2] = (color >> 16) & 0xff;
3161 break;
3163 case 4:
3164 COLORFILL_ROW(DWORD);
3165 break;
3167 default:
3168 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3169 return WINED3DERR_NOTAVAILABLE;
3172 #undef COLORFILL_ROW
3174 /* Now copy first row. */
3175 first = buf;
3176 for (y = 1; y < height; ++y)
3178 buf += pitch;
3179 memcpy(buf, first, width * bpp);
3182 return WINED3D_OK;
3185 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
3187 return surface_from_resource(resource);
3190 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3192 TRACE("surface %p.\n", surface);
3194 if (!surface->resource.map_count)
3196 WARN("Trying to unmap unmapped surface.\n");
3197 return WINEDDERR_NOTLOCKED;
3199 --surface->resource.map_count;
3201 surface->surface_ops->surface_unmap(surface);
3203 return WINED3D_OK;
3206 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3207 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
3209 const struct wined3d_format *format = surface->resource.format;
3210 BYTE *base_memory;
3212 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
3213 surface, map_desc, wine_dbgstr_rect(rect), flags);
3215 if (surface->resource.map_count)
3217 WARN("Surface is already mapped.\n");
3218 return WINED3DERR_INVALIDCALL;
3221 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
3222 && !surface_check_block_align(surface, rect))
3224 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3225 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3227 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
3228 return WINED3DERR_INVALIDCALL;
3231 ++surface->resource.map_count;
3233 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
3234 WARN("Trying to lock unlockable surface.\n");
3236 /* Performance optimization: Count how often a surface is mapped, if it is
3237 * mapped regularly do not throw away the system memory copy. This avoids
3238 * the need to download the surface from OpenGL all the time. The surface
3239 * is still downloaded if the OpenGL texture is changed. */
3240 if (!(surface->flags & SFLAG_DYNLOCK))
3242 if (++surface->lockCount > MAXLOCKCOUNT)
3244 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3245 surface->flags |= SFLAG_DYNLOCK;
3249 base_memory = surface->surface_ops->surface_map(surface, rect, flags);
3251 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3252 map_desc->row_pitch = surface->resource.width * format->byte_count;
3253 else
3254 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
3255 map_desc->slice_pitch = 0;
3257 if (!rect)
3259 map_desc->data = base_memory;
3260 surface->lockedRect.left = 0;
3261 surface->lockedRect.top = 0;
3262 surface->lockedRect.right = surface->resource.width;
3263 surface->lockedRect.bottom = surface->resource.height;
3265 else
3267 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3269 /* Compressed textures are block based, so calculate the offset of
3270 * the block that contains the top-left pixel of the locked rectangle. */
3271 map_desc->data = base_memory
3272 + ((rect->top / format->block_height) * map_desc->row_pitch)
3273 + ((rect->left / format->block_width) * format->block_byte_count);
3275 else
3277 map_desc->data = base_memory
3278 + (map_desc->row_pitch * rect->top)
3279 + (rect->left * format->byte_count);
3281 surface->lockedRect.left = rect->left;
3282 surface->lockedRect.top = rect->top;
3283 surface->lockedRect.right = rect->right;
3284 surface->lockedRect.bottom = rect->bottom;
3287 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3288 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
3290 return WINED3D_OK;
3293 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3295 struct wined3d_map_desc map;
3296 HRESULT hr;
3298 TRACE("surface %p, dc %p.\n", surface, dc);
3300 /* Give more detailed info for ddraw. */
3301 if (surface->flags & SFLAG_DCINUSE)
3302 return WINEDDERR_DCALREADYCREATED;
3304 /* Can't GetDC if the surface is locked. */
3305 if (surface->resource.map_count)
3306 return WINED3DERR_INVALIDCALL;
3308 /* Create a DIB section if there isn't a dc yet. */
3309 if (!surface->hDC)
3311 if (surface->flags & SFLAG_CLIENT)
3313 surface_load_location(surface, SFLAG_INSYSMEM);
3314 surface_release_client_storage(surface);
3316 hr = surface_create_dib_section(surface);
3317 if (FAILED(hr))
3318 return WINED3DERR_INVALIDCALL;
3320 /* Use the DIB section from now on if we are not using a PBO or user memory. */
3321 if (!(surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM | SFLAG_USERPTR)))
3323 wined3d_resource_free_sysmem(&surface->resource);
3324 surface->resource.allocatedMemory = surface->dib.bitmap_data;
3328 /* Map the surface. */
3329 hr = wined3d_surface_map(surface, &map, NULL, 0);
3330 if (FAILED(hr))
3332 ERR("Map failed, hr %#x.\n", hr);
3333 return hr;
3335 surface->getdc_map_mem = map.data;
3337 if (surface->dib.bitmap_data != surface->getdc_map_mem)
3338 memcpy(surface->dib.bitmap_data, surface->getdc_map_mem, surface->resource.size);
3340 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3341 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3343 /* GetDC on palettized formats is unsupported in D3D9, and the method
3344 * is missing in D3D8, so this should only be used for DX <=7
3345 * surfaces (with non-device palettes). */
3346 const PALETTEENTRY *pal = NULL;
3348 if (surface->palette)
3350 pal = surface->palette->palents;
3352 else
3354 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3355 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3357 if (dds_primary && dds_primary->palette)
3358 pal = dds_primary->palette->palents;
3361 if (pal)
3363 RGBQUAD col[256];
3364 unsigned int i;
3366 for (i = 0; i < 256; ++i)
3368 col[i].rgbRed = pal[i].peRed;
3369 col[i].rgbGreen = pal[i].peGreen;
3370 col[i].rgbBlue = pal[i].peBlue;
3371 col[i].rgbReserved = 0;
3373 SetDIBColorTable(surface->hDC, 0, 256, col);
3377 surface->flags |= SFLAG_DCINUSE;
3379 *dc = surface->hDC;
3380 TRACE("Returning dc %p.\n", *dc);
3382 return WINED3D_OK;
3385 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3387 TRACE("surface %p, dc %p.\n", surface, dc);
3389 if (!(surface->flags & SFLAG_DCINUSE))
3390 return WINEDDERR_NODC;
3392 if (surface->hDC != dc)
3394 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3395 dc, surface->hDC);
3396 return WINEDDERR_NODC;
3399 if (surface->dib.bitmap_data != surface->getdc_map_mem)
3400 memcpy(surface->getdc_map_mem, surface->dib.bitmap_data, surface->resource.size);
3402 /* We locked first, so unlock now. */
3403 surface->getdc_map_mem = NULL;
3404 wined3d_surface_unmap(surface);
3406 surface->flags &= ~SFLAG_DCINUSE;
3408 return WINED3D_OK;
3411 static void read_from_framebuffer(struct wined3d_surface *surface)
3413 struct wined3d_device *device = surface->resource.device;
3414 const struct wined3d_gl_info *gl_info;
3415 struct wined3d_context *context;
3416 BYTE *mem;
3417 GLint fmt;
3418 GLint type;
3419 BYTE *row, *top, *bottom;
3420 int i;
3421 BOOL bpp;
3422 BOOL srcIsUpsideDown;
3423 struct wined3d_bo_address data;
3424 UINT pitch = wined3d_surface_get_pitch(surface);
3426 surface_get_memory(surface, &data);
3428 context = context_acquire(device, surface);
3429 context_apply_blit_state(context, device);
3430 gl_info = context->gl_info;
3432 /* Select the correct read buffer, and give some debug output.
3433 * There is no need to keep track of the current read buffer or reset it, every part of the code
3434 * that reads sets the read buffer as desired.
3436 if (surface_is_offscreen(surface))
3438 /* Mapping the primary render target which is not on a swapchain.
3439 * Read from the back buffer. */
3440 TRACE("Mapping offscreen render target.\n");
3441 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3442 srcIsUpsideDown = TRUE;
3444 else
3446 /* Onscreen surfaces are always part of a swapchain */
3447 GLenum buffer = surface_get_gl_buffer(surface);
3448 TRACE("Mapping %#x buffer.\n", buffer);
3449 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
3450 checkGLcall("glReadBuffer");
3451 srcIsUpsideDown = FALSE;
3454 switch (surface->resource.format->id)
3456 case WINED3DFMT_P8_UINT:
3458 if (swapchain_is_p8(context->swapchain))
3460 /* In case of P8 render targets the index is stored in the alpha component */
3461 fmt = GL_ALPHA;
3462 type = GL_UNSIGNED_BYTE;
3463 mem = data.addr;
3464 bpp = surface->resource.format->byte_count;
3466 else
3468 /* GL can't return palettized data, so read ARGB pixels into a
3469 * separate block of memory and convert them into palettized format
3470 * in software. Slow, but if the app means to use palettized render
3471 * targets and locks it...
3473 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
3474 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
3475 * for the color channels when palettizing the colors.
3477 fmt = GL_RGB;
3478 type = GL_UNSIGNED_BYTE;
3479 pitch *= 3;
3480 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
3481 if (!mem)
3483 ERR("Out of memory\n");
3484 return;
3486 bpp = surface->resource.format->byte_count * 3;
3489 break;
3491 default:
3492 mem = data.addr;
3493 fmt = surface->resource.format->glFormat;
3494 type = surface->resource.format->glType;
3495 bpp = surface->resource.format->byte_count;
3498 if (data.buffer_object)
3500 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
3501 checkGLcall("glBindBufferARB");
3502 if (mem)
3503 ERR("mem not null for pbo -- unexpected\n");
3506 /* Setup pixel store pack state -- to glReadPixels into the correct place */
3507 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
3508 checkGLcall("glPixelStorei");
3510 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
3511 surface->resource.width, surface->resource.height,
3512 fmt, type, mem);
3513 checkGLcall("glReadPixels");
3515 /* Reset previous pixel store pack state */
3516 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
3517 checkGLcall("glPixelStorei");
3519 if (data.buffer_object && !srcIsUpsideDown)
3521 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
3522 * to get a pointer to it and perform the flipping in software. This is a lot
3523 * faster than calling glReadPixels for each line. In case we want more speed
3524 * we should rerender it flipped in a FBO and read the data back from the FBO. */
3525 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3526 checkGLcall("glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB)");
3529 /* TODO: Merge this with the palettization loop below for P8 targets */
3530 if (!srcIsUpsideDown)
3532 UINT len;
3533 /* glReadPixels returns the image upside down, and there is no way to prevent this.
3534 Flip the lines in software */
3535 len = surface->resource.width * bpp;
3537 row = HeapAlloc(GetProcessHeap(), 0, len);
3538 if (!row)
3540 ERR("Out of memory\n");
3541 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
3542 HeapFree(GetProcessHeap(), 0, mem);
3543 return;
3546 top = mem;
3547 bottom = mem + pitch * (surface->resource.height - 1);
3548 for (i = 0; i < surface->resource.height / 2; i++)
3550 memcpy(row, top, len);
3551 memcpy(top, bottom, len);
3552 memcpy(bottom, row, len);
3553 top += pitch;
3554 bottom -= pitch;
3556 HeapFree(GetProcessHeap(), 0, row);
3559 if (data.buffer_object)
3561 if (!srcIsUpsideDown)
3562 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB));
3564 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
3565 checkGLcall("glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0)");
3568 context_release(context);
3570 /* For P8 textures we need to perform an inverse palette lookup. This is
3571 * done by searching for a palette index which matches the RGB value.
3572 * Note this isn't guaranteed to work when there are multiple entries for
3573 * the same color but we have no choice. In case of P8 render targets,
3574 * the index is stored in the alpha component so no conversion is needed. */
3575 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !swapchain_is_p8(context->swapchain))
3577 const PALETTEENTRY *pal = NULL;
3578 DWORD width = pitch / 3;
3579 int x, y, c;
3581 if (surface->palette)
3583 pal = surface->palette->palents;
3585 else
3587 ERR("Palette is missing, cannot perform inverse palette lookup\n");
3588 HeapFree(GetProcessHeap(), 0, mem);
3589 return;
3592 for (y = 0; y < surface->resource.height; y++)
3594 for (x = 0; x < surface->resource.width; x++)
3596 /* start lines pixels */
3597 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
3598 const BYTE *green = blue + 1;
3599 const BYTE *red = green + 1;
3601 for (c = 0; c < 256; c++)
3603 if (*red == pal[c].peRed
3604 && *green == pal[c].peGreen
3605 && *blue == pal[c].peBlue)
3607 *((BYTE *)data.addr + y * width + x) = c;
3608 break;
3613 HeapFree(GetProcessHeap(), 0, mem);
3617 /* Read the framebuffer contents into a texture. Note that this function
3618 * doesn't do any kind of flipping. Using this on an onscreen surface will
3619 * result in a flipped D3D texture. */
3620 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
3622 struct wined3d_device *device = surface->resource.device;
3623 const struct wined3d_gl_info *gl_info;
3624 struct wined3d_context *context;
3626 context = context_acquire(device, surface);
3627 gl_info = context->gl_info;
3628 device_invalidate_state(device, STATE_FRAMEBUFFER);
3630 surface_prepare_texture(surface, context, srgb);
3631 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3633 TRACE("Reading back offscreen render target %p.\n", surface);
3635 if (surface_is_offscreen(surface))
3636 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3637 else
3638 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
3639 checkGLcall("glReadBuffer");
3641 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
3642 0, 0, 0, 0, surface->resource.width, surface->resource.height);
3643 checkGLcall("glCopyTexSubImage2D");
3645 context_release(context);
3648 /* Context activation is done by the caller. */
3649 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
3650 struct wined3d_context *context, BOOL srgb)
3652 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
3653 enum wined3d_conversion_type convert;
3654 struct wined3d_format format;
3656 if (surface->flags & alloc_flag) return;
3658 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
3659 if (convert != WINED3D_CT_NONE || format.convert)
3660 surface->flags |= SFLAG_CONVERTED;
3661 else surface->flags &= ~SFLAG_CONVERTED;
3663 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3664 surface_allocate_surface(surface, context->gl_info, &format, srgb);
3665 surface->flags |= alloc_flag;
3668 /* Context activation is done by the caller. */
3669 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
3671 struct wined3d_texture *texture = surface->container;
3672 UINT sub_count = texture->level_count * texture->layer_count;
3673 UINT i;
3675 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
3677 for (i = 0; i < sub_count; ++i)
3679 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
3680 surface_prepare_texture_internal(s, context, srgb);
3683 return;
3686 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
3688 if (multisample)
3690 if (surface->rb_multisample)
3691 return;
3693 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
3694 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
3695 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
3696 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
3697 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
3699 else
3701 if (surface->rb_resolved)
3702 return;
3704 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
3705 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
3706 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
3707 surface->pow2Width, surface->pow2Height);
3708 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
3712 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
3714 /* FIXME: Is this really how color keys are supposed to work? I think it
3715 * makes more sense to compare the individual channels. */
3716 return color >= color_key->color_space_low_value
3717 && color <= color_key->color_space_high_value;
3720 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
3722 const struct wined3d_device *device = surface->resource.device;
3723 const struct wined3d_palette *pal = surface->palette;
3724 BOOL index_in_alpha = FALSE;
3725 unsigned int i;
3727 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
3728 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
3729 * is slow. Further RGB->P8 conversion is not possible because palettes can have
3730 * duplicate entries. Store the color key in the unused alpha component to speed the
3731 * download up and to make conversion unneeded. */
3732 index_in_alpha = swapchain_is_p8(device->swapchains[0]);
3734 if (!pal)
3736 FIXME("No palette set.\n");
3737 if (index_in_alpha)
3739 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
3740 * there's no palette at this time. */
3741 for (i = 0; i < 256; i++) table[i][3] = i;
3744 else
3746 TRACE("Using surface palette %p\n", pal);
3747 /* Get the surface's palette */
3748 for (i = 0; i < 256; ++i)
3750 table[i][0] = pal->palents[i].peRed;
3751 table[i][1] = pal->palents[i].peGreen;
3752 table[i][2] = pal->palents[i].peBlue;
3754 /* When index_in_alpha is set the palette index is stored in the
3755 * alpha component. In case of a readback we can then read
3756 * GL_ALPHA. Color keying is handled in surface_blt_special() using a
3757 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
3758 * color key itself is passed to glAlphaFunc in other cases the
3759 * alpha component of pixels that should be masked away is set to 0. */
3760 if (index_in_alpha)
3761 table[i][3] = i;
3762 else if (colorkey && color_in_range(&surface->src_blt_color_key, i))
3763 table[i][3] = 0x00;
3764 else if (pal->flags & WINED3D_PALETTE_ALPHA)
3765 table[i][3] = pal->palents[i].peFlags;
3766 else
3767 table[i][3] = 0xff;
3772 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
3773 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
3775 const BYTE *source;
3776 BYTE *dest;
3778 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
3779 src, dst, pitch, width, height, outpitch, conversion_type, surface);
3781 switch (conversion_type)
3783 case WINED3D_CT_NONE:
3785 memcpy(dst, src, pitch * height);
3786 break;
3789 case WINED3D_CT_PALETTED:
3790 case WINED3D_CT_PALETTED_CK:
3792 BYTE table[256][4];
3793 unsigned int x, y;
3795 d3dfmt_p8_init_palette(surface, table, (conversion_type == WINED3D_CT_PALETTED_CK));
3797 for (y = 0; y < height; y++)
3799 source = src + pitch * y;
3800 dest = dst + outpitch * y;
3801 /* This is an 1 bpp format, using the width here is fine */
3802 for (x = 0; x < width; x++) {
3803 BYTE color = *source++;
3804 *dest++ = table[color][0];
3805 *dest++ = table[color][1];
3806 *dest++ = table[color][2];
3807 *dest++ = table[color][3];
3811 break;
3813 case WINED3D_CT_CK_565:
3815 /* Converting the 565 format in 5551 packed to emulate color-keying.
3817 Note : in all these conversion, it would be best to average the averaging
3818 pixels to get the color of the pixel that will be color-keyed to
3819 prevent 'color bleeding'. This will be done later on if ever it is
3820 too visible.
3822 Note2: Nvidia documents say that their driver does not support alpha + color keying
3823 on the same surface and disables color keying in such a case
3825 unsigned int x, y;
3826 const WORD *Source;
3827 WORD *Dest;
3829 TRACE("Color keyed 565\n");
3831 for (y = 0; y < height; y++) {
3832 Source = (const WORD *)(src + y * pitch);
3833 Dest = (WORD *) (dst + y * outpitch);
3834 for (x = 0; x < width; x++ ) {
3835 WORD color = *Source++;
3836 *Dest = ((color & 0xffc0) | ((color & 0x1f) << 1));
3837 if (!color_in_range(&surface->src_blt_color_key, color))
3838 *Dest |= 0x0001;
3839 Dest++;
3843 break;
3845 case WINED3D_CT_CK_5551:
3847 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
3848 unsigned int x, y;
3849 const WORD *Source;
3850 WORD *Dest;
3851 TRACE("Color keyed 5551\n");
3852 for (y = 0; y < height; y++) {
3853 Source = (const WORD *)(src + y * pitch);
3854 Dest = (WORD *) (dst + y * outpitch);
3855 for (x = 0; x < width; x++ ) {
3856 WORD color = *Source++;
3857 *Dest = color;
3858 if (!color_in_range(&surface->src_blt_color_key, color))
3859 *Dest |= (1 << 15);
3860 else
3861 *Dest &= ~(1 << 15);
3862 Dest++;
3866 break;
3868 case WINED3D_CT_CK_RGB24:
3870 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
3871 unsigned int x, y;
3872 for (y = 0; y < height; y++)
3874 source = src + pitch * y;
3875 dest = dst + outpitch * y;
3876 for (x = 0; x < width; x++) {
3877 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
3878 DWORD dstcolor = color << 8;
3879 if (!color_in_range(&surface->src_blt_color_key, color))
3880 dstcolor |= 0xff;
3881 *(DWORD*)dest = dstcolor;
3882 source += 3;
3883 dest += 4;
3887 break;
3889 case WINED3D_CT_RGB32_888:
3891 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
3892 unsigned int x, y;
3893 for (y = 0; y < height; y++)
3895 source = src + pitch * y;
3896 dest = dst + outpitch * y;
3897 for (x = 0; x < width; x++) {
3898 DWORD color = 0xffffff & *(const DWORD*)source;
3899 DWORD dstcolor = color << 8;
3900 if (!color_in_range(&surface->src_blt_color_key, color))
3901 dstcolor |= 0xff;
3902 *(DWORD*)dest = dstcolor;
3903 source += 4;
3904 dest += 4;
3908 break;
3910 case WINED3D_CT_CK_ARGB32:
3912 unsigned int x, y;
3913 for (y = 0; y < height; ++y)
3915 source = src + pitch * y;
3916 dest = dst + outpitch * y;
3917 for (x = 0; x < width; ++x)
3919 DWORD color = *(const DWORD *)source;
3920 if (color_in_range(&surface->src_blt_color_key, color))
3921 color &= ~0xff000000;
3922 *(DWORD*)dest = color;
3923 source += 4;
3924 dest += 4;
3928 break;
3930 default:
3931 ERR("Unsupported conversion type %#x.\n", conversion_type);
3933 return WINED3D_OK;
3936 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
3938 if (front->container->level_count != 1 || front->container->layer_count != 1
3939 || back->container->level_count != 1 || back->container->layer_count != 1)
3940 ERR("Flip between surfaces %p and %p not supported.\n", front, back);
3942 /* Flip the surface contents */
3943 /* Flip the DC */
3945 HDC tmp;
3946 tmp = front->hDC;
3947 front->hDC = back->hDC;
3948 back->hDC = tmp;
3951 /* Flip the DIBsection */
3953 HBITMAP tmp = front->dib.DIBsection;
3954 front->dib.DIBsection = back->dib.DIBsection;
3955 back->dib.DIBsection = tmp;
3958 /* Flip the surface data */
3960 void* tmp;
3962 tmp = front->dib.bitmap_data;
3963 front->dib.bitmap_data = back->dib.bitmap_data;
3964 back->dib.bitmap_data = tmp;
3966 tmp = front->resource.allocatedMemory;
3967 front->resource.allocatedMemory = back->resource.allocatedMemory;
3968 back->resource.allocatedMemory = tmp;
3970 tmp = front->resource.heap_memory;
3971 front->resource.heap_memory = back->resource.heap_memory;
3972 back->resource.heap_memory = tmp;
3975 /* Flip the PBO */
3977 GLuint tmp_pbo = front->pbo;
3978 front->pbo = back->pbo;
3979 back->pbo = tmp_pbo;
3982 /* Flip the opengl texture */
3984 GLuint tmp;
3986 tmp = back->container->texture_rgb.name;
3987 back->container->texture_rgb.name = front->container->texture_rgb.name;
3988 front->container->texture_rgb.name = tmp;
3990 tmp = back->container->texture_srgb.name;
3991 back->container->texture_srgb.name = front->container->texture_srgb.name;
3992 front->container->texture_srgb.name = tmp;
3994 tmp = back->rb_multisample;
3995 back->rb_multisample = front->rb_multisample;
3996 front->rb_multisample = tmp;
3998 tmp = back->rb_resolved;
3999 back->rb_resolved = front->rb_resolved;
4000 front->rb_resolved = tmp;
4002 resource_unload(&back->resource);
4003 resource_unload(&front->resource);
4007 DWORD tmp_flags = back->flags;
4008 back->flags = front->flags;
4009 front->flags = tmp_flags;
4013 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4014 * pixel copy calls. */
4015 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4016 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4018 struct wined3d_device *device = dst_surface->resource.device;
4019 const struct wined3d_gl_info *gl_info;
4020 float xrel, yrel;
4021 struct wined3d_context *context;
4022 BOOL upsidedown = FALSE;
4023 RECT dst_rect = *dst_rect_in;
4025 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4026 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4028 if(dst_rect.top > dst_rect.bottom) {
4029 UINT tmp = dst_rect.bottom;
4030 dst_rect.bottom = dst_rect.top;
4031 dst_rect.top = tmp;
4032 upsidedown = TRUE;
4035 context = context_acquire(device, src_surface);
4036 gl_info = context->gl_info;
4037 context_apply_blit_state(context, device);
4038 wined3d_texture_load(dst_surface->container, context, FALSE);
4040 /* Bind the target texture */
4041 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
4042 if (surface_is_offscreen(src_surface))
4044 TRACE("Reading from an offscreen target\n");
4045 upsidedown = !upsidedown;
4046 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
4048 else
4050 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
4052 checkGLcall("glReadBuffer");
4054 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4055 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4057 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4059 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4061 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4062 ERR("Texture filtering not supported in direct blit.\n");
4064 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4065 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4067 ERR("Texture filtering not supported in direct blit\n");
4070 if (upsidedown
4071 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4072 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4074 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
4075 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4076 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4077 src_rect->left, src_surface->resource.height - src_rect->bottom,
4078 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4080 else
4082 LONG row;
4083 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4084 /* I have to process this row by row to swap the image,
4085 * otherwise it would be upside down, so stretching in y direction
4086 * doesn't cost extra time
4088 * However, stretching in x direction can be avoided if not necessary
4090 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4091 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4093 /* Well, that stuff works, but it's very slow.
4094 * find a better way instead
4096 LONG col;
4098 for (col = dst_rect.left; col < dst_rect.right; ++col)
4100 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4101 dst_rect.left + col /* x offset */, row /* y offset */,
4102 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4105 else
4107 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4108 dst_rect.left /* x offset */, row /* y offset */,
4109 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4113 checkGLcall("glCopyTexSubImage2D");
4115 context_release(context);
4117 /* The texture is now most up to date - If the surface is a render target
4118 * and has a drawable, this path is never entered. */
4119 surface_validate_location(dst_surface, SFLAG_INTEXTURE);
4120 surface_invalidate_location(dst_surface, ~SFLAG_INTEXTURE);
4123 /* Uses the hardware to stretch and flip the image */
4124 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4125 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4127 struct wined3d_device *device = dst_surface->resource.device;
4128 GLuint src, backup = 0;
4129 float left, right, top, bottom; /* Texture coordinates */
4130 UINT fbwidth = src_surface->resource.width;
4131 UINT fbheight = src_surface->resource.height;
4132 const struct wined3d_gl_info *gl_info;
4133 struct wined3d_context *context;
4134 GLenum drawBuffer = GL_BACK;
4135 GLenum texture_target;
4136 BOOL noBackBufferBackup;
4137 BOOL src_offscreen;
4138 BOOL upsidedown = FALSE;
4139 RECT dst_rect = *dst_rect_in;
4141 TRACE("Using hwstretch blit\n");
4142 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4143 context = context_acquire(device, src_surface);
4144 gl_info = context->gl_info;
4145 context_apply_blit_state(context, device);
4146 wined3d_texture_load(dst_surface->container, context, FALSE);
4148 src_offscreen = surface_is_offscreen(src_surface);
4149 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4150 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
4152 /* Get it a description */
4153 wined3d_texture_load(src_surface->container, context, FALSE);
4156 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4157 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4159 if (context->aux_buffers >= 2)
4161 /* Got more than one aux buffer? Use the 2nd aux buffer */
4162 drawBuffer = GL_AUX1;
4164 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4166 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4167 drawBuffer = GL_AUX0;
4170 if (noBackBufferBackup)
4172 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
4173 checkGLcall("glGenTextures");
4174 context_bind_texture(context, GL_TEXTURE_2D, backup);
4175 texture_target = GL_TEXTURE_2D;
4177 else
4179 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4180 * we are reading from the back buffer, the backup can be used as source texture
4182 texture_target = src_surface->texture_target;
4183 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
4184 gl_info->gl_ops.gl.p_glEnable(texture_target);
4185 checkGLcall("glEnable(texture_target)");
4187 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4188 src_surface->flags &= ~SFLAG_INTEXTURE;
4191 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4192 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4194 if(dst_rect.top > dst_rect.bottom) {
4195 UINT tmp = dst_rect.bottom;
4196 dst_rect.bottom = dst_rect.top;
4197 dst_rect.top = tmp;
4198 upsidedown = TRUE;
4201 if (src_offscreen)
4203 TRACE("Reading from an offscreen target\n");
4204 upsidedown = !upsidedown;
4205 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
4207 else
4209 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
4212 /* TODO: Only back up the part that will be overwritten */
4213 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
4215 checkGLcall("glCopyTexSubImage2D");
4217 /* No issue with overriding these - the sampler is dirty due to blit usage */
4218 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
4219 wined3d_gl_mag_filter(magLookup, filter));
4220 checkGLcall("glTexParameteri");
4221 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
4222 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
4223 checkGLcall("glTexParameteri");
4225 if (!src_surface->swapchain || src_surface == src_surface->swapchain->back_buffers[0])
4227 src = backup ? backup : src_surface->container->texture_rgb.name;
4229 else
4231 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
4232 checkGLcall("glReadBuffer(GL_FRONT)");
4234 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
4235 checkGLcall("glGenTextures(1, &src)");
4236 context_bind_texture(context, GL_TEXTURE_2D, src);
4238 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
4239 * out for power of 2 sizes
4241 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
4242 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
4243 checkGLcall("glTexImage2D");
4244 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
4246 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4247 checkGLcall("glTexParameteri");
4248 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4249 checkGLcall("glTexParameteri");
4251 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
4252 checkGLcall("glReadBuffer(GL_BACK)");
4254 if (texture_target != GL_TEXTURE_2D)
4256 gl_info->gl_ops.gl.p_glDisable(texture_target);
4257 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
4258 texture_target = GL_TEXTURE_2D;
4261 checkGLcall("glEnd and previous");
4263 left = src_rect->left;
4264 right = src_rect->right;
4266 if (!upsidedown)
4268 top = src_surface->resource.height - src_rect->top;
4269 bottom = src_surface->resource.height - src_rect->bottom;
4271 else
4273 top = src_surface->resource.height - src_rect->bottom;
4274 bottom = src_surface->resource.height - src_rect->top;
4277 if (src_surface->flags & SFLAG_NORMCOORD)
4279 left /= src_surface->pow2Width;
4280 right /= src_surface->pow2Width;
4281 top /= src_surface->pow2Height;
4282 bottom /= src_surface->pow2Height;
4285 /* draw the source texture stretched and upside down. The correct surface is bound already */
4286 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
4287 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
4289 context_set_draw_buffer(context, drawBuffer);
4290 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
4292 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
4293 /* bottom left */
4294 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
4295 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
4297 /* top left */
4298 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
4299 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
4301 /* top right */
4302 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
4303 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4305 /* bottom right */
4306 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
4307 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
4308 gl_info->gl_ops.gl.p_glEnd();
4309 checkGLcall("glEnd and previous");
4311 if (texture_target != dst_surface->texture_target)
4313 gl_info->gl_ops.gl.p_glDisable(texture_target);
4314 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
4315 texture_target = dst_surface->texture_target;
4318 /* Now read the stretched and upside down image into the destination texture */
4319 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
4320 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
4322 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
4323 0, 0, /* We blitted the image to the origin */
4324 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4325 checkGLcall("glCopyTexSubImage2D");
4327 if (drawBuffer == GL_BACK)
4329 /* Write the back buffer backup back. */
4330 if (backup)
4332 if (texture_target != GL_TEXTURE_2D)
4334 gl_info->gl_ops.gl.p_glDisable(texture_target);
4335 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
4336 texture_target = GL_TEXTURE_2D;
4338 context_bind_texture(context, GL_TEXTURE_2D, backup);
4340 else
4342 if (texture_target != src_surface->texture_target)
4344 gl_info->gl_ops.gl.p_glDisable(texture_target);
4345 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
4346 texture_target = src_surface->texture_target;
4348 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
4351 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
4352 /* top left */
4353 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
4354 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
4356 /* bottom left */
4357 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
4358 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
4360 /* bottom right */
4361 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
4362 (float)fbheight / (float)src_surface->pow2Height);
4363 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
4365 /* top right */
4366 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
4367 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
4368 gl_info->gl_ops.gl.p_glEnd();
4370 gl_info->gl_ops.gl.p_glDisable(texture_target);
4371 checkGLcall("glDisable(texture_target)");
4373 /* Cleanup */
4374 if (src != src_surface->container->texture_rgb.name && src != backup)
4376 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
4377 checkGLcall("glDeleteTextures(1, &src)");
4379 if (backup)
4381 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
4382 checkGLcall("glDeleteTextures(1, &backup)");
4385 if (wined3d_settings.strict_draw_ordering)
4386 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4388 context_release(context);
4390 /* The texture is now most up to date - If the surface is a render target
4391 * and has a drawable, this path is never entered. */
4392 surface_validate_location(dst_surface, SFLAG_INTEXTURE);
4393 surface_invalidate_location(dst_surface, ~SFLAG_INTEXTURE);
4396 /* Front buffer coordinates are always full screen coordinates, but our GL
4397 * drawable is limited to the window's client area. The sysmem and texture
4398 * copies do have the full screen size. Note that GL has a bottom-left
4399 * origin, while D3D has a top-left origin. */
4400 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
4402 UINT drawable_height;
4404 if (surface->swapchain && surface == surface->swapchain->front_buffer)
4406 POINT offset = {0, 0};
4407 RECT windowsize;
4409 ScreenToClient(window, &offset);
4410 OffsetRect(rect, offset.x, offset.y);
4412 GetClientRect(window, &windowsize);
4413 drawable_height = windowsize.bottom - windowsize.top;
4415 else
4417 drawable_height = surface->resource.height;
4420 rect->top = drawable_height - rect->top;
4421 rect->bottom = drawable_height - rect->bottom;
4424 static void surface_blt_to_drawable(const struct wined3d_device *device,
4425 enum wined3d_texture_filter_type filter, BOOL color_key,
4426 struct wined3d_surface *src_surface, const RECT *src_rect_in,
4427 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
4429 const struct wined3d_gl_info *gl_info;
4430 struct wined3d_context *context;
4431 RECT src_rect, dst_rect;
4433 src_rect = *src_rect_in;
4434 dst_rect = *dst_rect_in;
4436 context = context_acquire(device, dst_surface);
4437 gl_info = context->gl_info;
4439 /* Make sure the surface is up-to-date. This should probably use
4440 * surface_load_location() and worry about the destination surface too,
4441 * unless we're overwriting it completely. */
4442 wined3d_texture_load(src_surface->container, context, FALSE);
4444 /* Activate the destination context, set it up for blitting */
4445 context_apply_blit_state(context, device);
4447 if (!surface_is_offscreen(dst_surface))
4448 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
4450 device->blitter->set_shader(device->blit_priv, context, src_surface);
4452 if (color_key)
4454 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
4455 checkGLcall("glEnable(GL_ALPHA_TEST)");
4457 /* When the primary render target uses P8, the alpha component
4458 * contains the palette index. Which means that the colorkey is one of
4459 * the palette entries. In other cases pixels that should be masked
4460 * away have alpha set to 0. */
4461 if (swapchain_is_p8(context->swapchain))
4462 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
4463 (float)src_surface->src_blt_color_key.color_space_low_value / 256.0f);
4464 else
4465 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
4466 checkGLcall("glAlphaFunc");
4468 else
4470 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4471 checkGLcall("glDisable(GL_ALPHA_TEST)");
4474 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
4476 if (color_key)
4478 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4479 checkGLcall("glDisable(GL_ALPHA_TEST)");
4482 /* Leave the opengl state valid for blitting */
4483 device->blitter->unset_shader(context->gl_info);
4485 if (wined3d_settings.strict_draw_ordering
4486 || (dst_surface->swapchain && dst_surface->swapchain->front_buffer == dst_surface))
4487 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4489 context_release(context);
4492 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
4494 struct wined3d_device *device = s->resource.device;
4495 const struct blit_shader *blitter;
4497 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
4498 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
4499 if (!blitter)
4501 FIXME("No blitter is capable of performing the requested color fill operation.\n");
4502 return WINED3DERR_INVALIDCALL;
4505 return blitter->color_fill(device, s, rect, color);
4508 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4509 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
4510 enum wined3d_texture_filter_type filter)
4512 struct wined3d_device *device = dst_surface->resource.device;
4513 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4514 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
4516 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
4517 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4518 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
4520 /* Get the swapchain. One of the surfaces has to be a primary surface */
4521 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4523 WARN("Destination is in sysmem, rejecting gl blt\n");
4524 return WINED3DERR_INVALIDCALL;
4527 dst_swapchain = dst_surface->swapchain;
4529 if (src_surface)
4531 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4533 WARN("Src is in sysmem, rejecting gl blt\n");
4534 return WINED3DERR_INVALIDCALL;
4537 src_swapchain = src_surface->swapchain;
4539 else
4541 src_swapchain = NULL;
4544 /* Early sort out of cases where no render target is used */
4545 if (!dst_swapchain && !src_swapchain
4546 && src_surface != device->fb.render_targets[0]
4547 && dst_surface != device->fb.render_targets[0])
4549 TRACE("No surface is render target, not using hardware blit.\n");
4550 return WINED3DERR_INVALIDCALL;
4553 /* No destination color keying supported */
4554 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
4556 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
4557 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
4558 return WINED3DERR_INVALIDCALL;
4561 if (dst_swapchain && dst_swapchain == src_swapchain)
4563 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
4564 return WINED3DERR_INVALIDCALL;
4567 if (dst_swapchain && src_swapchain)
4569 FIXME("Implement hardware blit between two different swapchains\n");
4570 return WINED3DERR_INVALIDCALL;
4573 if (dst_swapchain)
4575 /* Handled with regular texture -> swapchain blit */
4576 if (src_surface == device->fb.render_targets[0])
4577 TRACE("Blit from active render target to a swapchain\n");
4579 else if (src_swapchain && dst_surface == device->fb.render_targets[0])
4581 FIXME("Implement blit from a swapchain to the active render target\n");
4582 return WINED3DERR_INVALIDCALL;
4585 if ((src_swapchain || src_surface == device->fb.render_targets[0]) && !dst_swapchain)
4587 /* Blit from render target to texture */
4588 BOOL stretchx;
4590 /* P8 read back is not implemented */
4591 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
4592 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
4594 TRACE("P8 read back not supported by frame buffer to texture blit\n");
4595 return WINED3DERR_INVALIDCALL;
4598 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
4600 TRACE("Color keying not supported by frame buffer to texture blit\n");
4601 return WINED3DERR_INVALIDCALL;
4602 /* Destination color key is checked above */
4605 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
4606 stretchx = TRUE;
4607 else
4608 stretchx = FALSE;
4610 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
4611 * flip the image nor scale it.
4613 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
4614 * -> If the app wants an image width an unscaled width, copy it line per line
4615 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
4616 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
4617 * back buffer. This is slower than reading line per line, thus not used for flipping
4618 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
4619 * pixel by pixel. */
4620 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
4621 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
4623 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
4624 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
4626 else
4628 TRACE("Using hardware stretching to flip / stretch the texture.\n");
4629 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
4632 surface_evict_sysmem(dst_surface);
4634 return WINED3D_OK;
4636 else if (src_surface)
4638 /* Blit from offscreen surface to render target */
4639 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
4640 DWORD oldCKeyFlags = src_surface->CKeyFlags;
4642 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4644 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4645 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
4646 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
4648 FIXME("Unsupported blit operation falling back to software\n");
4649 return WINED3DERR_INVALIDCALL;
4652 /* Color keying: Check if we have to do a color keyed blt,
4653 * and if not check if a color key is activated.
4655 * Just modify the color keying parameters in the surface and restore them afterwards
4656 * The surface keeps track of the color key last used to load the opengl surface.
4657 * PreLoad will catch the change to the flags and color key and reload if necessary.
4659 if (flags & WINEDDBLT_KEYSRC)
4661 /* Use color key from surface */
4663 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4665 /* Use color key from DDBltFx */
4666 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
4667 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
4669 else
4671 /* Do not use color key */
4672 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
4675 surface_blt_to_drawable(device, filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
4676 src_surface, src_rect, dst_surface, dst_rect);
4678 /* Restore the color key parameters */
4679 src_surface->CKeyFlags = oldCKeyFlags;
4680 src_surface->src_blt_color_key = old_blt_key;
4682 surface_validate_location(dst_surface, dst_surface->draw_binding);
4683 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
4685 return WINED3D_OK;
4688 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
4689 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
4690 return WINED3DERR_INVALIDCALL;
4693 /* Context activation is done by the caller. */
4694 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
4695 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
4697 struct wined3d_device *device = surface->resource.device;
4698 const struct wined3d_gl_info *gl_info = context->gl_info;
4699 GLint compare_mode = GL_NONE;
4700 struct blt_info info;
4701 GLint old_binding = 0;
4702 RECT rect;
4704 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4706 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
4707 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
4708 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4709 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
4710 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
4711 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
4712 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
4713 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
4714 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4715 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
4716 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
4718 SetRect(&rect, 0, h, w, 0);
4719 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
4720 context_active_texture(context, context->gl_info, 0);
4721 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
4722 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
4723 if (gl_info->supported[ARB_SHADOW])
4725 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
4726 if (compare_mode != GL_NONE)
4727 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
4730 device->shader_backend->shader_select_depth_blt(device->shader_priv,
4731 gl_info, info.tex_type, &surface->ds_current_size);
4733 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
4734 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
4735 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
4736 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
4737 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
4738 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
4739 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
4740 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
4741 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
4742 gl_info->gl_ops.gl.p_glEnd();
4744 if (compare_mode != GL_NONE)
4745 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
4746 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
4748 gl_info->gl_ops.gl.p_glPopAttrib();
4750 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
4753 void surface_modify_ds_location(struct wined3d_surface *surface,
4754 DWORD location, UINT w, UINT h)
4756 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
4758 if (location & ~(SFLAG_LOCATIONS | SFLAG_DISCARDED))
4759 FIXME("Invalid location (%#x) specified.\n", location);
4761 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
4762 || (!(surface->flags & SFLAG_INTEXTURE) && (location & SFLAG_INTEXTURE)))
4763 wined3d_texture_set_dirty(surface->container);
4765 surface->ds_current_size.cx = w;
4766 surface->ds_current_size.cy = h;
4767 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_DISCARDED);
4768 surface->flags |= location;
4771 /* Context activation is done by the caller. */
4772 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4774 const struct wined3d_gl_info *gl_info = context->gl_info;
4775 struct wined3d_device *device = surface->resource.device;
4776 GLsizei w, h;
4778 TRACE("surface %p, new location %#x.\n", surface, location);
4780 /* TODO: Make this work for modes other than FBO */
4781 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4783 if (!(surface->flags & location))
4785 w = surface->ds_current_size.cx;
4786 h = surface->ds_current_size.cy;
4787 surface->ds_current_size.cx = 0;
4788 surface->ds_current_size.cy = 0;
4790 else
4792 w = surface->resource.width;
4793 h = surface->resource.height;
4796 if (surface->ds_current_size.cx == surface->resource.width
4797 && surface->ds_current_size.cy == surface->resource.height)
4799 TRACE("Location (%#x) is already up to date.\n", location);
4800 return;
4803 if (surface->current_renderbuffer)
4805 FIXME("Not supported with fixed up depth stencil.\n");
4806 return;
4809 if (surface->flags & SFLAG_DISCARDED)
4811 TRACE("Surface was discarded, no need copy data.\n");
4812 switch (location)
4814 case SFLAG_INTEXTURE:
4815 surface_prepare_texture(surface, context, FALSE);
4816 break;
4817 case SFLAG_INRB_MULTISAMPLE:
4818 surface_prepare_rb(surface, gl_info, TRUE);
4819 break;
4820 case SFLAG_INDRAWABLE:
4821 /* Nothing to do */
4822 break;
4823 default:
4824 FIXME("Unhandled location %#x\n", location);
4826 surface->flags &= ~SFLAG_DISCARDED;
4827 surface->flags |= location;
4828 surface->ds_current_size.cx = surface->resource.width;
4829 surface->ds_current_size.cy = surface->resource.height;
4830 return;
4833 if (!(surface->flags & SFLAG_LOCATIONS))
4835 FIXME("No up to date depth stencil location.\n");
4836 surface->flags |= location;
4837 surface->ds_current_size.cx = surface->resource.width;
4838 surface->ds_current_size.cy = surface->resource.height;
4839 return;
4842 if (location == SFLAG_INTEXTURE)
4844 GLint old_binding = 0;
4845 GLenum bind_target;
4847 /* The render target is allowed to be smaller than the depth/stencil
4848 * buffer, so the onscreen depth/stencil buffer is potentially smaller
4849 * than the offscreen surface. Don't overwrite the offscreen surface
4850 * with undefined data. */
4851 w = min(w, context->swapchain->desc.backbuffer_width);
4852 h = min(h, context->swapchain->desc.backbuffer_height);
4854 TRACE("Copying onscreen depth buffer to depth texture.\n");
4856 if (!device->depth_blt_texture)
4857 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
4859 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4860 * directly on the FBO texture. That's because we need to flip. */
4861 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4862 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
4863 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4865 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4866 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4868 else
4870 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4871 bind_target = GL_TEXTURE_2D;
4873 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
4874 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
4875 * internal format, because the internal format might include stencil
4876 * data. In principle we should copy stencil data as well, but unless
4877 * the driver supports stencil export it's hard to do, and doesn't
4878 * seem to be needed in practice. If the hardware doesn't support
4879 * writing stencil data, the glCopyTexImage2D() call might trigger
4880 * software fallbacks. */
4881 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
4882 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4883 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4884 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4885 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4886 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4887 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4888 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
4890 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4891 NULL, surface, SFLAG_INTEXTURE);
4892 context_set_draw_buffer(context, GL_NONE);
4894 /* Do the actual blit */
4895 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
4896 checkGLcall("depth_blt");
4898 context_invalidate_state(context, STATE_FRAMEBUFFER);
4900 if (wined3d_settings.strict_draw_ordering)
4901 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4903 else if (location == SFLAG_INDRAWABLE)
4905 TRACE("Copying depth texture to onscreen depth buffer.\n");
4907 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4908 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
4909 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
4910 0, surface->pow2Height - h, w, h, surface->texture_target);
4911 checkGLcall("depth_blt");
4913 context_invalidate_state(context, STATE_FRAMEBUFFER);
4915 if (wined3d_settings.strict_draw_ordering)
4916 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4918 else
4920 ERR("Invalid location (%#x) specified.\n", location);
4923 surface->flags |= location;
4924 surface->ds_current_size.cx = surface->resource.width;
4925 surface->ds_current_size.cy = surface->resource.height;
4928 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
4930 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location & SFLAG_LOCATIONS));
4932 surface->flags |= (location & SFLAG_LOCATIONS);
4935 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
4937 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location & SFLAG_LOCATIONS));
4939 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
4940 wined3d_texture_set_dirty(surface->container);
4941 surface->flags &= ~(location & SFLAG_LOCATIONS);
4943 if (!(surface->flags & SFLAG_LOCATIONS))
4944 ERR("Surface %p does not have any up to date location.\n", surface);
4947 static DWORD resource_access_from_location(DWORD location)
4949 switch (location)
4951 case SFLAG_INSYSMEM:
4952 return WINED3D_RESOURCE_ACCESS_CPU;
4954 case SFLAG_INDRAWABLE:
4955 case SFLAG_INSRGBTEX:
4956 case SFLAG_INTEXTURE:
4957 case SFLAG_INRB_MULTISAMPLE:
4958 case SFLAG_INRB_RESOLVED:
4959 return WINED3D_RESOURCE_ACCESS_GPU;
4961 default:
4962 FIXME("Unhandled location %#x.\n", location);
4963 return 0;
4967 static void surface_load_sysmem(struct wined3d_surface *surface,
4968 const struct wined3d_gl_info *gl_info)
4970 surface_prepare_system_memory(surface);
4972 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
4973 surface_load_location(surface, SFLAG_INTEXTURE);
4975 /* Download the surface to system memory. */
4976 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
4978 struct wined3d_device *device = surface->resource.device;
4979 struct wined3d_context *context;
4981 /* TODO: Use already acquired context when possible. */
4982 context = context_acquire(device, NULL);
4984 wined3d_texture_bind_and_dirtify(surface->container, context, !(surface->flags & SFLAG_INTEXTURE));
4985 surface_download_data(surface, gl_info);
4987 context_release(context);
4989 return;
4992 if (surface->flags & SFLAG_INDRAWABLE)
4994 read_from_framebuffer(surface);
4995 return;
4998 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
4999 surface, surface->flags & SFLAG_LOCATIONS);
5002 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5003 const struct wined3d_gl_info *gl_info)
5005 RECT r;
5007 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5009 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5010 return WINED3DERR_INVALIDCALL;
5013 surface_get_rect(surface, NULL, &r);
5014 surface_load_location(surface, SFLAG_INTEXTURE);
5015 surface_blt_to_drawable(surface->resource.device,
5016 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
5018 return WINED3D_OK;
5021 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5022 const struct wined3d_gl_info *gl_info, BOOL srgb)
5024 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5025 struct wined3d_device *device = surface->resource.device;
5026 enum wined3d_conversion_type convert;
5027 struct wined3d_context *context;
5028 UINT width, src_pitch, dst_pitch;
5029 struct wined3d_bo_address data;
5030 struct wined3d_format format;
5031 POINT dst_point = {0, 0};
5032 BYTE *mem = NULL;
5034 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
5035 && surface_is_offscreen(surface)
5036 && (surface->flags & SFLAG_INDRAWABLE))
5038 surface_load_fb_texture(surface, srgb);
5040 return WINED3D_OK;
5043 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
5044 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
5045 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5046 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5047 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5049 if (srgb)
5050 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INTEXTURE,
5051 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
5052 else
5053 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INSRGBTEX,
5054 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
5056 return WINED3D_OK;
5059 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
5060 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
5061 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5062 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5063 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5065 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
5066 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
5067 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5069 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
5070 &rect, surface, dst_location, &rect);
5072 return WINED3D_OK;
5075 /* Upload from system memory */
5077 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
5078 TRUE /* We will use textures */, &format, &convert);
5080 if (srgb)
5082 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
5084 /* Performance warning... */
5085 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
5086 surface_load_location(surface, SFLAG_INSYSMEM);
5089 else
5091 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
5093 /* Performance warning... */
5094 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
5095 surface_load_location(surface, SFLAG_INSYSMEM);
5099 if (!(surface->flags & SFLAG_INSYSMEM))
5101 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
5102 /* Lets hope we get it from somewhere... */
5103 surface_load_location(surface, SFLAG_INSYSMEM);
5106 /* TODO: Use already acquired context when possible. */
5107 context = context_acquire(device, NULL);
5109 surface_prepare_texture(surface, context, srgb);
5110 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
5112 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
5114 surface->flags |= SFLAG_GLCKEY;
5115 surface->gl_color_key = surface->src_blt_color_key;
5117 else surface->flags &= ~SFLAG_GLCKEY;
5119 width = surface->resource.width;
5120 src_pitch = wined3d_surface_get_pitch(surface);
5122 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5123 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
5124 * called. */
5125 if ((convert != WINED3D_CT_NONE || format.convert) && (surface->flags & SFLAG_PBO))
5127 TRACE("Removing the pbo attached to surface %p.\n", surface);
5128 surface_remove_pbo(surface, gl_info);
5131 surface_get_memory(surface, &data);
5132 if (format.convert)
5134 /* This code is entered for texture formats which need a fixup. */
5135 UINT height = surface->resource.height;
5137 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
5138 dst_pitch = width * format.conv_byte_count;
5139 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5141 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5143 ERR("Out of memory (%u).\n", dst_pitch * height);
5144 context_release(context);
5145 return E_OUTOFMEMORY;
5147 format.convert(data.addr, mem, src_pitch, src_pitch * height,
5148 dst_pitch, dst_pitch * height, width, height, 1);
5149 format.byte_count = format.conv_byte_count;
5150 src_pitch = dst_pitch;
5151 data.addr = mem;
5153 else if (convert != WINED3D_CT_NONE)
5155 /* This code is only entered for color keying fixups */
5156 UINT height = surface->resource.height;
5158 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
5159 dst_pitch = width * format.conv_byte_count;
5160 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5162 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5164 ERR("Out of memory (%u).\n", dst_pitch * height);
5165 context_release(context);
5166 return E_OUTOFMEMORY;
5168 d3dfmt_convert_surface(data.addr, mem, src_pitch,
5169 width, height, dst_pitch, convert, surface);
5170 format.byte_count = format.conv_byte_count;
5171 src_pitch = dst_pitch;
5172 data.addr = mem;
5175 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
5177 context_release(context);
5179 HeapFree(GetProcessHeap(), 0, mem);
5181 return WINED3D_OK;
5184 static void surface_multisample_resolve(struct wined3d_surface *surface)
5186 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5188 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
5189 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
5191 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
5192 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
5195 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
5197 struct wined3d_device *device = surface->resource.device;
5198 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5199 HRESULT hr;
5201 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location));
5203 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5205 if (location == SFLAG_INTEXTURE && surface->flags & SFLAG_INDRAWABLE)
5207 struct wined3d_context *context = context_acquire(device, NULL);
5208 surface_load_ds_location(surface, context, location);
5209 context_release(context);
5210 return WINED3D_OK;
5212 else if (location & surface->flags && surface->draw_binding != SFLAG_INDRAWABLE)
5214 /* Already up to date, nothing to do. */
5215 return WINED3D_OK;
5217 else
5219 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
5220 debug_surflocation(surface->flags & SFLAG_LOCATIONS), debug_surflocation(location));
5221 return WINED3DERR_INVALIDCALL;
5225 if (surface->flags & location)
5227 TRACE("Location already up to date.\n");
5229 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
5230 && surface_need_pbo(surface, gl_info))
5231 surface_create_pbo(surface, gl_info);
5233 return WINED3D_OK;
5236 if (WARN_ON(d3d_surface))
5238 DWORD required_access = resource_access_from_location(location);
5239 if ((surface->resource.access_flags & required_access) != required_access)
5240 WARN("Operation requires %#x access, but surface only has %#x.\n",
5241 required_access, surface->resource.access_flags);
5244 if (!(surface->flags & SFLAG_LOCATIONS))
5246 ERR("Surface %p does not have any up to date location.\n", surface);
5247 surface->flags |= SFLAG_LOST;
5248 return WINED3DERR_DEVICELOST;
5251 switch (location)
5253 case SFLAG_INSYSMEM:
5254 surface_load_sysmem(surface, gl_info);
5255 break;
5257 case SFLAG_INDRAWABLE:
5258 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
5259 return hr;
5260 break;
5262 case SFLAG_INRB_RESOLVED:
5263 surface_multisample_resolve(surface);
5264 break;
5266 case SFLAG_INTEXTURE:
5267 case SFLAG_INSRGBTEX:
5268 if (FAILED(hr = surface_load_texture(surface, gl_info, location == SFLAG_INSRGBTEX)))
5269 return hr;
5270 break;
5272 default:
5273 ERR("Don't know how to handle location %#x.\n", location);
5274 break;
5277 surface->flags |= location;
5279 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
5280 surface_evict_sysmem(surface);
5282 return WINED3D_OK;
5285 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
5287 struct wined3d_swapchain *swapchain;
5289 /* Not on a swapchain - must be offscreen */
5290 if (!(swapchain = surface->swapchain))
5291 return TRUE;
5293 /* The front buffer is always onscreen */
5294 if (surface == swapchain->front_buffer) return FALSE;
5296 /* If the swapchain is rendered to an FBO, the backbuffer is
5297 * offscreen, otherwise onscreen */
5298 return swapchain->render_to_fbo;
5301 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
5302 /* Context activation is done by the caller. */
5303 static void ffp_blit_free(struct wined3d_device *device) { }
5305 /* Context activation is done by the caller. */
5306 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5308 const struct wined3d_gl_info *gl_info = context->gl_info;
5310 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
5311 checkGLcall("glEnable(target)");
5313 return WINED3D_OK;
5316 /* Context activation is done by the caller. */
5317 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
5319 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
5320 checkGLcall("glDisable(GL_TEXTURE_2D)");
5321 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
5323 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5324 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5326 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
5328 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
5329 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5333 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5334 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5335 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5337 switch (blit_op)
5339 case WINED3D_BLIT_OP_COLOR_BLIT:
5340 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
5341 return FALSE;
5343 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5345 TRACE("Checking support for fixup:\n");
5346 dump_color_fixup_desc(src_format->color_fixup);
5349 /* We only support identity conversions. */
5350 if (!is_identity_fixup(src_format->color_fixup)
5351 || !is_identity_fixup(dst_format->color_fixup))
5353 TRACE("Fixups are not supported.\n");
5354 return FALSE;
5357 return TRUE;
5359 case WINED3D_BLIT_OP_COLOR_FILL:
5360 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
5361 return FALSE;
5363 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5365 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
5366 return FALSE;
5368 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
5370 TRACE("Color fill not supported\n");
5371 return FALSE;
5374 /* FIXME: We should reject color fills on formats with fixups,
5375 * but this would break P8 color fills for example. */
5377 return TRUE;
5379 case WINED3D_BLIT_OP_DEPTH_FILL:
5380 return TRUE;
5382 default:
5383 TRACE("Unsupported blit_op=%d\n", blit_op);
5384 return FALSE;
5388 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5389 const RECT *dst_rect, const struct wined3d_color *color)
5391 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
5392 struct wined3d_fb_state fb = {&dst_surface, NULL};
5394 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
5396 return WINED3D_OK;
5399 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
5400 struct wined3d_surface *surface, const RECT *rect, float depth)
5402 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
5403 struct wined3d_fb_state fb = {NULL, surface};
5405 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
5407 return WINED3D_OK;
5410 const struct blit_shader ffp_blit = {
5411 ffp_blit_alloc,
5412 ffp_blit_free,
5413 ffp_blit_set,
5414 ffp_blit_unset,
5415 ffp_blit_supported,
5416 ffp_blit_color_fill,
5417 ffp_blit_depth_fill,
5420 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
5422 return WINED3D_OK;
5425 /* Context activation is done by the caller. */
5426 static void cpu_blit_free(struct wined3d_device *device)
5430 /* Context activation is done by the caller. */
5431 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5433 return WINED3D_OK;
5436 /* Context activation is done by the caller. */
5437 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
5441 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5442 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5443 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5445 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
5447 return TRUE;
5450 return FALSE;
5453 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
5454 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
5455 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
5457 UINT row_block_count;
5458 const BYTE *src_row;
5459 BYTE *dst_row;
5460 UINT x, y;
5462 src_row = src_data;
5463 dst_row = dst_data;
5465 row_block_count = (update_w + format->block_width - 1) / format->block_width;
5467 if (!flags)
5469 for (y = 0; y < update_h; y += format->block_height)
5471 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
5472 src_row += src_pitch;
5473 dst_row += dst_pitch;
5476 return WINED3D_OK;
5479 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
5481 src_row += (((update_h / format->block_height) - 1) * src_pitch);
5483 switch (format->id)
5485 case WINED3DFMT_DXT1:
5486 for (y = 0; y < update_h; y += format->block_height)
5488 struct block
5490 WORD color[2];
5491 BYTE control_row[4];
5494 const struct block *s = (const struct block *)src_row;
5495 struct block *d = (struct block *)dst_row;
5497 for (x = 0; x < row_block_count; ++x)
5499 d[x].color[0] = s[x].color[0];
5500 d[x].color[1] = s[x].color[1];
5501 d[x].control_row[0] = s[x].control_row[3];
5502 d[x].control_row[1] = s[x].control_row[2];
5503 d[x].control_row[2] = s[x].control_row[1];
5504 d[x].control_row[3] = s[x].control_row[0];
5506 src_row -= src_pitch;
5507 dst_row += dst_pitch;
5509 return WINED3D_OK;
5511 case WINED3DFMT_DXT3:
5512 for (y = 0; y < update_h; y += format->block_height)
5514 struct block
5516 WORD alpha_row[4];
5517 WORD color[2];
5518 BYTE control_row[4];
5521 const struct block *s = (const struct block *)src_row;
5522 struct block *d = (struct block *)dst_row;
5524 for (x = 0; x < row_block_count; ++x)
5526 d[x].alpha_row[0] = s[x].alpha_row[3];
5527 d[x].alpha_row[1] = s[x].alpha_row[2];
5528 d[x].alpha_row[2] = s[x].alpha_row[1];
5529 d[x].alpha_row[3] = s[x].alpha_row[0];
5530 d[x].color[0] = s[x].color[0];
5531 d[x].color[1] = s[x].color[1];
5532 d[x].control_row[0] = s[x].control_row[3];
5533 d[x].control_row[1] = s[x].control_row[2];
5534 d[x].control_row[2] = s[x].control_row[1];
5535 d[x].control_row[3] = s[x].control_row[0];
5537 src_row -= src_pitch;
5538 dst_row += dst_pitch;
5540 return WINED3D_OK;
5542 default:
5543 FIXME("Compressed flip not implemented for format %s.\n",
5544 debug_d3dformat(format->id));
5545 return E_NOTIMPL;
5549 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
5550 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
5552 return E_NOTIMPL;
5555 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5556 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
5557 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5559 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
5560 const struct wined3d_format *src_format, *dst_format;
5561 struct wined3d_texture *src_texture = NULL;
5562 struct wined3d_map_desc dst_map, src_map;
5563 const BYTE *sbase = NULL;
5564 HRESULT hr = WINED3D_OK;
5565 const BYTE *sbuf;
5566 BYTE *dbuf;
5567 int x, y;
5569 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5570 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5571 flags, fx, debug_d3dtexturefiltertype(filter));
5573 if (src_surface == dst_surface)
5575 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
5576 src_map = dst_map;
5577 src_format = dst_surface->resource.format;
5578 dst_format = src_format;
5580 else
5582 dst_format = dst_surface->resource.format;
5583 if (src_surface)
5585 if (dst_surface->resource.format->id != src_surface->resource.format->id)
5587 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
5589 /* The conv function writes a FIXME */
5590 WARN("Cannot convert source surface format to dest format.\n");
5591 goto release;
5593 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
5595 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
5596 src_format = src_surface->resource.format;
5598 else
5600 src_format = dst_format;
5603 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
5606 bpp = dst_surface->resource.format->byte_count;
5607 srcheight = src_rect->bottom - src_rect->top;
5608 srcwidth = src_rect->right - src_rect->left;
5609 dstheight = dst_rect->bottom - dst_rect->top;
5610 dstwidth = dst_rect->right - dst_rect->left;
5611 width = (dst_rect->right - dst_rect->left) * bpp;
5613 if (src_surface)
5614 sbase = (BYTE *)src_map.data
5615 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
5616 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
5617 if (src_surface != dst_surface)
5618 dbuf = dst_map.data;
5619 else
5620 dbuf = (BYTE *)dst_map.data
5621 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
5622 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
5624 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
5626 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
5628 if (src_surface == dst_surface)
5630 FIXME("Only plain blits supported on compressed surfaces.\n");
5631 hr = E_NOTIMPL;
5632 goto release;
5635 if (srcheight != dstheight || srcwidth != dstwidth)
5637 WARN("Stretching not supported on compressed surfaces.\n");
5638 hr = WINED3DERR_INVALIDCALL;
5639 goto release;
5642 if (!surface_check_block_align(src_surface, src_rect))
5644 WARN("Source rectangle not block-aligned.\n");
5645 hr = WINED3DERR_INVALIDCALL;
5646 goto release;
5649 if (!surface_check_block_align(dst_surface, dst_rect))
5651 WARN("Destination rectangle not block-aligned.\n");
5652 hr = WINED3DERR_INVALIDCALL;
5653 goto release;
5656 hr = surface_cpu_blt_compressed(sbase, dbuf,
5657 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
5658 src_format, flags, fx);
5659 goto release;
5662 /* First, all the 'source-less' blits */
5663 if (flags & WINEDDBLT_COLORFILL)
5665 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
5666 flags &= ~WINEDDBLT_COLORFILL;
5669 if (flags & WINEDDBLT_DEPTHFILL)
5671 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
5673 if (flags & WINEDDBLT_ROP)
5675 /* Catch some degenerate cases here. */
5676 switch (fx->dwROP)
5678 case BLACKNESS:
5679 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
5680 break;
5681 case 0xaa0029: /* No-op */
5682 break;
5683 case WHITENESS:
5684 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
5685 break;
5686 case SRCCOPY: /* Well, we do that below? */
5687 break;
5688 default:
5689 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
5690 goto error;
5692 flags &= ~WINEDDBLT_ROP;
5694 if (flags & WINEDDBLT_DDROPS)
5696 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
5698 /* Now the 'with source' blits. */
5699 if (src_surface)
5701 int sx, xinc, sy, yinc;
5703 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
5704 goto release;
5706 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
5707 && (srcwidth != dstwidth || srcheight != dstheight))
5709 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
5710 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
5713 xinc = (srcwidth << 16) / dstwidth;
5714 yinc = (srcheight << 16) / dstheight;
5716 if (!flags)
5718 /* No effects, we can cheat here. */
5719 if (dstwidth == srcwidth)
5721 if (dstheight == srcheight)
5723 /* No stretching in either direction. This needs to be as
5724 * fast as possible. */
5725 sbuf = sbase;
5727 /* Check for overlapping surfaces. */
5728 if (src_surface != dst_surface || dst_rect->top < src_rect->top
5729 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
5731 /* No overlap, or dst above src, so copy from top downwards. */
5732 for (y = 0; y < dstheight; ++y)
5734 memcpy(dbuf, sbuf, width);
5735 sbuf += src_map.row_pitch;
5736 dbuf += dst_map.row_pitch;
5739 else if (dst_rect->top > src_rect->top)
5741 /* Copy from bottom upwards. */
5742 sbuf += src_map.row_pitch * dstheight;
5743 dbuf += dst_map.row_pitch * dstheight;
5744 for (y = 0; y < dstheight; ++y)
5746 sbuf -= src_map.row_pitch;
5747 dbuf -= dst_map.row_pitch;
5748 memcpy(dbuf, sbuf, width);
5751 else
5753 /* Src and dst overlapping on the same line, use memmove. */
5754 for (y = 0; y < dstheight; ++y)
5756 memmove(dbuf, sbuf, width);
5757 sbuf += src_map.row_pitch;
5758 dbuf += dst_map.row_pitch;
5762 else
5764 /* Stretching in y direction only. */
5765 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5767 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5768 memcpy(dbuf, sbuf, width);
5769 dbuf += dst_map.row_pitch;
5773 else
5775 /* Stretching in X direction. */
5776 int last_sy = -1;
5777 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5779 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5781 if ((sy >> 16) == (last_sy >> 16))
5783 /* This source row is the same as last source row -
5784 * Copy the already stretched row. */
5785 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
5787 else
5789 #define STRETCH_ROW(type) \
5790 do { \
5791 const type *s = (const type *)sbuf; \
5792 type *d = (type *)dbuf; \
5793 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5794 d[x] = s[sx >> 16]; \
5795 } while(0)
5797 switch(bpp)
5799 case 1:
5800 STRETCH_ROW(BYTE);
5801 break;
5802 case 2:
5803 STRETCH_ROW(WORD);
5804 break;
5805 case 4:
5806 STRETCH_ROW(DWORD);
5807 break;
5808 case 3:
5810 const BYTE *s;
5811 BYTE *d = dbuf;
5812 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
5814 DWORD pixel;
5816 s = sbuf + 3 * (sx >> 16);
5817 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5818 d[0] = (pixel ) & 0xff;
5819 d[1] = (pixel >> 8) & 0xff;
5820 d[2] = (pixel >> 16) & 0xff;
5821 d += 3;
5823 break;
5825 default:
5826 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
5827 hr = WINED3DERR_NOTAVAILABLE;
5828 goto error;
5830 #undef STRETCH_ROW
5832 dbuf += dst_map.row_pitch;
5833 last_sy = sy;
5837 else
5839 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
5840 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
5841 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
5842 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
5844 /* The color keying flags are checked for correctness in ddraw */
5845 if (flags & WINEDDBLT_KEYSRC)
5847 keylow = src_surface->src_blt_color_key.color_space_low_value;
5848 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
5850 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5852 keylow = fx->ddckSrcColorkey.color_space_low_value;
5853 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
5856 if (flags & WINEDDBLT_KEYDEST)
5858 /* Destination color keys are taken from the source surface! */
5859 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
5860 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
5862 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
5864 destkeylow = fx->ddckDestColorkey.color_space_low_value;
5865 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
5868 if (bpp == 1)
5870 keymask = 0xff;
5872 else
5874 DWORD masks[3];
5875 get_color_masks(src_format, masks);
5876 keymask = masks[0]
5877 | masks[1]
5878 | masks[2];
5880 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
5883 if (flags & WINEDDBLT_DDFX)
5885 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
5886 LONG tmpxy;
5887 dTopLeft = dbuf;
5888 dTopRight = dbuf + ((dstwidth - 1) * bpp);
5889 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
5890 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
5892 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
5894 /* I don't think we need to do anything about this flag */
5895 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
5897 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
5899 tmp = dTopRight;
5900 dTopRight = dTopLeft;
5901 dTopLeft = tmp;
5902 tmp = dBottomRight;
5903 dBottomRight = dBottomLeft;
5904 dBottomLeft = tmp;
5905 dstxinc = dstxinc * -1;
5907 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
5909 tmp = dTopLeft;
5910 dTopLeft = dBottomLeft;
5911 dBottomLeft = tmp;
5912 tmp = dTopRight;
5913 dTopRight = dBottomRight;
5914 dBottomRight = tmp;
5915 dstyinc = dstyinc * -1;
5917 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
5919 /* I don't think we need to do anything about this flag */
5920 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
5922 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
5924 tmp = dBottomRight;
5925 dBottomRight = dTopLeft;
5926 dTopLeft = tmp;
5927 tmp = dBottomLeft;
5928 dBottomLeft = dTopRight;
5929 dTopRight = tmp;
5930 dstxinc = dstxinc * -1;
5931 dstyinc = dstyinc * -1;
5933 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
5935 tmp = dTopLeft;
5936 dTopLeft = dBottomLeft;
5937 dBottomLeft = dBottomRight;
5938 dBottomRight = dTopRight;
5939 dTopRight = tmp;
5940 tmpxy = dstxinc;
5941 dstxinc = dstyinc;
5942 dstyinc = tmpxy;
5943 dstxinc = dstxinc * -1;
5945 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
5947 tmp = dTopLeft;
5948 dTopLeft = dTopRight;
5949 dTopRight = dBottomRight;
5950 dBottomRight = dBottomLeft;
5951 dBottomLeft = tmp;
5952 tmpxy = dstxinc;
5953 dstxinc = dstyinc;
5954 dstyinc = tmpxy;
5955 dstyinc = dstyinc * -1;
5957 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
5959 /* I don't think we need to do anything about this flag */
5960 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
5962 dbuf = dTopLeft;
5963 flags &= ~(WINEDDBLT_DDFX);
5966 #define COPY_COLORKEY_FX(type) \
5967 do { \
5968 const type *s; \
5969 type *d = (type *)dbuf, *dx, tmp; \
5970 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
5972 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
5973 dx = d; \
5974 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5976 tmp = s[sx >> 16]; \
5977 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
5978 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
5980 dx[0] = tmp; \
5982 dx = (type *)(((BYTE *)dx) + dstxinc); \
5984 d = (type *)(((BYTE *)d) + dstyinc); \
5986 } while(0)
5988 switch (bpp)
5990 case 1:
5991 COPY_COLORKEY_FX(BYTE);
5992 break;
5993 case 2:
5994 COPY_COLORKEY_FX(WORD);
5995 break;
5996 case 4:
5997 COPY_COLORKEY_FX(DWORD);
5998 break;
5999 case 3:
6001 const BYTE *s;
6002 BYTE *d = dbuf, *dx;
6003 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6005 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6006 dx = d;
6007 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
6009 DWORD pixel, dpixel = 0;
6010 s = sbuf + 3 * (sx>>16);
6011 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6012 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
6013 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
6014 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
6016 dx[0] = (pixel ) & 0xff;
6017 dx[1] = (pixel >> 8) & 0xff;
6018 dx[2] = (pixel >> 16) & 0xff;
6020 dx += dstxinc;
6022 d += dstyinc;
6024 break;
6026 default:
6027 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
6028 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
6029 hr = WINED3DERR_NOTAVAILABLE;
6030 goto error;
6031 #undef COPY_COLORKEY_FX
6036 error:
6037 if (flags && FIXME_ON(d3d_surface))
6039 FIXME("\tUnsupported flags: %#x.\n", flags);
6042 release:
6043 wined3d_surface_unmap(dst_surface);
6044 if (src_surface && src_surface != dst_surface)
6045 wined3d_surface_unmap(src_surface);
6046 /* Release the converted surface, if any. */
6047 if (src_texture)
6048 wined3d_texture_decref(src_texture);
6050 return hr;
6053 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6054 const RECT *dst_rect, const struct wined3d_color *color)
6056 static const RECT src_rect;
6057 WINEDDBLTFX BltFx;
6059 memset(&BltFx, 0, sizeof(BltFx));
6060 BltFx.dwSize = sizeof(BltFx);
6061 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
6062 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
6063 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
6066 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
6067 struct wined3d_surface *surface, const RECT *rect, float depth)
6069 FIXME("Depth filling not implemented by cpu_blit.\n");
6070 return WINED3DERR_INVALIDCALL;
6073 const struct blit_shader cpu_blit = {
6074 cpu_blit_alloc,
6075 cpu_blit_free,
6076 cpu_blit_set,
6077 cpu_blit_unset,
6078 cpu_blit_supported,
6079 cpu_blit_color_fill,
6080 cpu_blit_depth_fill,
6083 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
6084 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
6085 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
6087 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
6088 struct wined3d_device *device = dst_surface->resource.device;
6089 DWORD src_ds_flags, dst_ds_flags;
6090 RECT src_rect, dst_rect;
6091 BOOL scale, convert;
6093 static const DWORD simple_blit = WINEDDBLT_ASYNC
6094 | WINEDDBLT_COLORFILL
6095 | WINEDDBLT_WAIT
6096 | WINEDDBLT_DEPTHFILL
6097 | WINEDDBLT_DONOTWAIT;
6099 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6100 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
6101 flags, fx, debug_d3dtexturefiltertype(filter));
6102 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
6104 if (fx)
6106 TRACE("dwSize %#x.\n", fx->dwSize);
6107 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
6108 TRACE("dwROP %#x.\n", fx->dwROP);
6109 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
6110 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
6111 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
6112 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
6113 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
6114 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
6115 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
6116 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
6117 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
6118 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
6119 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
6120 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
6121 TRACE("dwReserved %#x.\n", fx->dwReserved);
6122 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
6123 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
6124 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
6125 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
6126 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
6127 TRACE("ddckDestColorkey {%#x, %#x}.\n",
6128 fx->ddckDestColorkey.color_space_low_value,
6129 fx->ddckDestColorkey.color_space_high_value);
6130 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
6131 fx->ddckSrcColorkey.color_space_low_value,
6132 fx->ddckSrcColorkey.color_space_high_value);
6135 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
6137 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
6138 return WINEDDERR_SURFACEBUSY;
6141 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
6143 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
6144 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
6145 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
6146 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
6147 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
6149 WARN("The application gave us a bad destination rectangle.\n");
6150 return WINEDDERR_INVALIDRECT;
6153 if (src_surface)
6155 surface_get_rect(src_surface, src_rect_in, &src_rect);
6157 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
6158 || src_rect.left > src_surface->resource.width || src_rect.left < 0
6159 || src_rect.top > src_surface->resource.height || src_rect.top < 0
6160 || src_rect.right > src_surface->resource.width || src_rect.right < 0
6161 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
6163 WARN("Application gave us bad source rectangle for Blt.\n");
6164 return WINEDDERR_INVALIDRECT;
6167 else
6169 memset(&src_rect, 0, sizeof(src_rect));
6172 if (!fx || !(fx->dwDDFX))
6173 flags &= ~WINEDDBLT_DDFX;
6175 if (flags & WINEDDBLT_WAIT)
6176 flags &= ~WINEDDBLT_WAIT;
6178 if (flags & WINEDDBLT_ASYNC)
6180 static unsigned int once;
6182 if (!once++)
6183 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
6184 flags &= ~WINEDDBLT_ASYNC;
6187 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
6188 if (flags & WINEDDBLT_DONOTWAIT)
6190 static unsigned int once;
6192 if (!once++)
6193 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
6194 flags &= ~WINEDDBLT_DONOTWAIT;
6197 if (!device->d3d_initialized)
6199 WARN("D3D not initialized, using fallback.\n");
6200 goto cpu;
6203 /* We want to avoid invalidating the sysmem location for converted
6204 * surfaces, since otherwise we'd have to convert the data back when
6205 * locking them. */
6206 if (dst_surface->flags & SFLAG_CONVERTED)
6208 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
6209 goto cpu;
6212 if (flags & ~simple_blit)
6214 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
6215 goto fallback;
6218 if (src_surface)
6219 src_swapchain = src_surface->swapchain;
6220 else
6221 src_swapchain = NULL;
6223 dst_swapchain = dst_surface->swapchain;
6225 /* This isn't strictly needed. FBO blits for example could deal with
6226 * cross-swapchain blits by first downloading the source to a texture
6227 * before switching to the destination context. We just have this here to
6228 * not have to deal with the issue, since cross-swapchain blits should be
6229 * rare. */
6230 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
6232 FIXME("Using fallback for cross-swapchain blit.\n");
6233 goto fallback;
6236 scale = src_surface
6237 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
6238 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
6239 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
6241 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
6242 if (src_surface)
6243 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
6244 else
6245 src_ds_flags = 0;
6247 if (src_ds_flags || dst_ds_flags)
6249 if (flags & WINEDDBLT_DEPTHFILL)
6251 float depth;
6253 TRACE("Depth fill.\n");
6255 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
6256 return WINED3DERR_INVALIDCALL;
6258 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
6259 return WINED3D_OK;
6261 else
6263 if (src_ds_flags != dst_ds_flags)
6265 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
6266 return WINED3DERR_INVALIDCALL;
6269 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->draw_binding, &src_rect,
6270 dst_surface, dst_surface->draw_binding, &dst_rect)))
6271 return WINED3D_OK;
6274 else
6276 /* In principle this would apply to depth blits as well, but we don't
6277 * implement those in the CPU blitter at the moment. */
6278 if ((dst_surface->flags & SFLAG_INSYSMEM)
6279 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
6281 if (scale)
6282 TRACE("Not doing sysmem blit because of scaling.\n");
6283 else if (convert)
6284 TRACE("Not doing sysmem blit because of format conversion.\n");
6285 else
6286 goto cpu;
6289 if (flags & WINEDDBLT_COLORFILL)
6291 struct wined3d_color color;
6293 TRACE("Color fill.\n");
6295 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
6296 goto fallback;
6298 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
6299 return WINED3D_OK;
6301 else
6303 TRACE("Color blit.\n");
6305 /* Upload */
6306 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
6308 if (scale)
6309 TRACE("Not doing upload because of scaling.\n");
6310 else if (convert)
6311 TRACE("Not doing upload because of format conversion.\n");
6312 else
6314 POINT dst_point = {dst_rect.left, dst_rect.top};
6316 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
6318 if (!surface_is_offscreen(dst_surface))
6319 surface_load_location(dst_surface, dst_surface->draw_binding);
6320 return WINED3D_OK;
6325 /* Use present for back -> front blits. The idea behind this is
6326 * that present is potentially faster than a blit, in particular
6327 * when FBO blits aren't available. Some ddraw applications like
6328 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
6329 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
6330 * applications can't blit directly to the frontbuffer. */
6331 if (dst_swapchain && dst_swapchain->back_buffers
6332 && dst_surface == dst_swapchain->front_buffer
6333 && src_surface == dst_swapchain->back_buffers[0])
6335 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
6337 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
6339 /* Set the swap effect to COPY, we don't want the backbuffer
6340 * to become undefined. */
6341 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
6342 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
6343 dst_swapchain->desc.swap_effect = swap_effect;
6345 return WINED3D_OK;
6348 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6349 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6350 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6352 TRACE("Using FBO blit.\n");
6354 surface_blt_fbo(device, filter,
6355 src_surface, src_surface->draw_binding, &src_rect,
6356 dst_surface, dst_surface->draw_binding, &dst_rect);
6357 surface_validate_location(dst_surface, dst_surface->draw_binding);
6358 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
6360 return WINED3D_OK;
6363 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6364 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6365 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6367 TRACE("Using arbfp blit.\n");
6369 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
6370 return WINED3D_OK;
6375 fallback:
6376 /* Special cases for render targets. */
6377 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
6378 return WINED3D_OK;
6380 cpu:
6382 /* For the rest call the X11 surface implementation. For render targets
6383 * this should be implemented OpenGL accelerated in surface_blt_special(),
6384 * other blits are rather rare. */
6385 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
6388 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
6389 const struct wined3d_resource_desc *desc, DWORD flags)
6391 struct wined3d_device *device = container->resource.device;
6392 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6393 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
6394 UINT multisample_quality = desc->multisample_quality;
6395 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
6396 unsigned int resource_size;
6397 HRESULT hr;
6399 if (multisample_quality > 0)
6401 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
6402 multisample_quality = 0;
6405 /* Quick lockable sanity check.
6406 * TODO: remove this after surfaces, usage and lockability have been debugged properly
6407 * this function is too deep to need to care about things like this.
6408 * Levels need to be checked too, since they all affect what can be done. */
6409 switch (desc->pool)
6411 case WINED3D_POOL_MANAGED:
6412 if (desc->usage & WINED3DUSAGE_DYNAMIC)
6413 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
6414 break;
6416 case WINED3D_POOL_DEFAULT:
6417 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
6418 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
6419 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
6420 break;
6422 case WINED3D_POOL_SCRATCH:
6423 case WINED3D_POOL_SYSTEM_MEM:
6424 break;
6426 default:
6427 FIXME("Unknown pool %#x.\n", desc->pool);
6428 break;
6431 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
6432 FIXME("Trying to create a render target that isn't in the default pool.\n");
6434 /* FIXME: Check that the format is supported by the device. */
6436 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
6437 if (!resource_size)
6438 return WINED3DERR_INVALIDCALL;
6440 if (device->wined3d->flags & WINED3D_NO3D)
6441 surface->surface_ops = &gdi_surface_ops;
6442 else
6443 surface->surface_ops = &surface_ops;
6445 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
6446 desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height, 1,
6447 resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
6449 WARN("Failed to initialize resource, returning %#x.\n", hr);
6450 return hr;
6453 surface_set_container(surface, container);
6454 surface_validate_location(surface, SFLAG_INSYSMEM);
6455 list_init(&surface->renderbuffers);
6456 list_init(&surface->overlays);
6458 /* Flags */
6459 surface->flags |= SFLAG_NORMCOORD; /* Default to normalized coords. */
6460 if (flags & WINED3D_SURFACE_DISCARD)
6461 surface->flags |= SFLAG_DISCARD;
6462 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
6463 surface->flags |= SFLAG_PIN_SYSMEM;
6464 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
6465 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
6467 /* I'm not sure if this qualifies as a hack or as an optimization. It
6468 * seems reasonable to assume that lockable render targets will get
6469 * locked, so we might as well set SFLAG_DYNLOCK right at surface
6470 * creation. However, the other reason we want to do this is that several
6471 * ddraw applications access surface memory while the surface isn't
6472 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
6473 * future locks prevents these from crashing. */
6474 if (lockable && (desc->usage & WINED3DUSAGE_RENDERTARGET))
6475 surface->flags |= SFLAG_DYNLOCK;
6477 TRACE("surface %p, memory %p, size %u\n",
6478 surface, surface->resource.allocatedMemory, surface->resource.size);
6480 /* Call the private setup routine */
6481 hr = surface->surface_ops->surface_private_setup(surface);
6482 if (FAILED(hr))
6484 ERR("Private setup failed, returning %#x\n", hr);
6485 surface_set_container(surface, NULL);
6486 surface_cleanup(surface);
6487 return hr;
6490 /* Similar to lockable rendertargets above, creating the DIB section
6491 * during surface initialization prevents the sysmem pointer from changing
6492 * after a wined3d_surface_getdc() call. */
6493 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
6494 && SUCCEEDED(surface_create_dib_section(surface)))
6496 wined3d_resource_free_sysmem(&surface->resource);
6497 surface->resource.allocatedMemory = surface->dib.bitmap_data;
6500 return hr;
6503 HRESULT CDECL wined3d_surface_create(struct wined3d_texture *container,
6504 const struct wined3d_resource_desc *desc, DWORD flags, struct wined3d_surface **surface)
6506 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
6507 const struct wined3d_parent_ops *parent_ops;
6508 struct wined3d_surface *object;
6509 void *parent;
6510 HRESULT hr;
6512 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), "
6513 "pool %s, multisample_type %#x, multisample_quality %u, flags %#x, surface %p.\n",
6514 container, desc->width, desc->height, debug_d3dformat(desc->format),
6515 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
6516 desc->multisample_type, desc->multisample_quality, flags, surface);
6518 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
6519 return E_OUTOFMEMORY;
6521 if (FAILED(hr = surface_init(object, container, desc, flags)))
6523 WARN("Failed to initialize surface, returning %#x.\n", hr);
6524 HeapFree(GetProcessHeap(), 0, object);
6525 return hr;
6528 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
6529 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
6531 WARN("Failed to create surface parent, hr %#x.\n", hr);
6532 surface_set_container(object, NULL);
6533 wined3d_surface_decref(object);
6534 return hr;
6537 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
6539 object->resource.parent = parent;
6540 object->resource.parent_ops = parent_ops;
6541 *surface = object;
6543 return hr;