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 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
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d);
36 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
37 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
38 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter);
39 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
40 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *fx,
41 WINED3DTEXTUREFILTERTYPE filter);
43 static void surface_cleanup(struct wined3d_surface *surface)
45 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->texture_name || (surface->flags & SFLAG_PBO)
50 || surface->rb_multisample || surface->rb_resolved
51 || !list_empty(&surface->renderbuffers))
53 struct wined3d_renderbuffer_entry *entry, *entry2;
54 const struct wined3d_gl_info *gl_info;
55 struct wined3d_context *context;
57 context = context_acquire(surface->resource.device, NULL);
58 gl_info = context->gl_info;
62 if (surface->texture_name)
64 TRACE("Deleting texture %u.\n", surface->texture_name);
65 glDeleteTextures(1, &surface->texture_name);
68 if (surface->flags & SFLAG_PBO)
70 TRACE("Deleting PBO %u.\n", surface->pbo);
71 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
74 if (surface->rb_multisample)
76 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
77 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
80 if (surface->rb_resolved)
82 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
83 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
86 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
88 TRACE("Deleting renderbuffer %u.\n", entry->id);
89 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
90 HeapFree(GetProcessHeap(), 0, entry);
95 context_release(context);
98 if (surface->flags & SFLAG_DIBSECTION)
100 DeleteDC(surface->hDC);
101 DeleteObject(surface->dib.DIBsection);
102 surface->dib.bitmap_data = NULL;
103 surface->resource.allocatedMemory = NULL;
106 if (surface->flags & SFLAG_USERPTR)
107 wined3d_surface_set_mem(surface, NULL);
108 if (surface->overlay_dest)
109 list_remove(&surface->overlay_entry);
111 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
113 list_remove(&overlay->overlay_entry);
114 overlay->overlay_dest = NULL;
117 resource_cleanup(&surface->resource);
120 void surface_update_draw_binding(struct wined3d_surface *surface)
122 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
123 surface->draw_binding = SFLAG_INDRAWABLE;
124 else if (surface->resource.multisample_type)
125 surface->draw_binding = SFLAG_INRB_MULTISAMPLE;
127 surface->draw_binding = SFLAG_INTEXTURE;
130 void surface_set_container(struct wined3d_surface *surface, enum wined3d_container_type type, void *container)
132 TRACE("surface %p, container %p.\n", surface, container);
134 if (!container && type != WINED3D_CONTAINER_NONE)
135 ERR("Setting NULL container of type %#x.\n", type);
137 if (type == WINED3D_CONTAINER_SWAPCHAIN)
139 surface->get_drawable_size = get_drawable_size_swapchain;
143 switch (wined3d_settings.offscreen_rendering_mode)
146 surface->get_drawable_size = get_drawable_size_fbo;
150 surface->get_drawable_size = get_drawable_size_backbuffer;
154 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
159 surface->container.type = type;
160 surface->container.u.base = container;
161 surface_update_draw_binding(surface);
168 enum tex_types tex_type;
169 GLfloat coords[4][3];
180 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
182 f->l = ((r->left * 2.0f) / w) - 1.0f;
183 f->t = ((r->top * 2.0f) / h) - 1.0f;
184 f->r = ((r->right * 2.0f) / w) - 1.0f;
185 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
188 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
190 GLfloat (*coords)[3] = info->coords;
196 FIXME("Unsupported texture target %#x\n", target);
197 /* Fall back to GL_TEXTURE_2D */
199 info->binding = GL_TEXTURE_BINDING_2D;
200 info->bind_target = GL_TEXTURE_2D;
201 info->tex_type = tex_2d;
202 coords[0][0] = (float)rect->left / w;
203 coords[0][1] = (float)rect->top / h;
206 coords[1][0] = (float)rect->right / w;
207 coords[1][1] = (float)rect->top / h;
210 coords[2][0] = (float)rect->left / w;
211 coords[2][1] = (float)rect->bottom / h;
214 coords[3][0] = (float)rect->right / w;
215 coords[3][1] = (float)rect->bottom / h;
219 case GL_TEXTURE_RECTANGLE_ARB:
220 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
221 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
222 info->tex_type = tex_rect;
223 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
224 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
225 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
226 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
229 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
230 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
231 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
232 info->tex_type = tex_cube;
233 cube_coords_float(rect, w, h, &f);
235 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
236 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
237 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
238 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
241 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
242 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
243 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
244 info->tex_type = tex_cube;
245 cube_coords_float(rect, w, h, &f);
247 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
248 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
249 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
250 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
253 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
254 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
255 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
256 info->tex_type = tex_cube;
257 cube_coords_float(rect, w, h, &f);
259 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
260 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
261 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
262 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
265 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
266 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
267 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
268 info->tex_type = tex_cube;
269 cube_coords_float(rect, w, h, &f);
271 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
272 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
273 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
274 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
277 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
278 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
279 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
280 info->tex_type = tex_cube;
281 cube_coords_float(rect, w, h, &f);
283 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
284 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
285 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
286 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
289 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
290 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
291 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
292 info->tex_type = tex_cube;
293 cube_coords_float(rect, w, h, &f);
295 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
296 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
297 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
298 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
303 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
306 *rect_out = *rect_in;
311 rect_out->right = surface->resource.width;
312 rect_out->bottom = surface->resource.height;
316 /* GL locking and context activation is done by the caller */
317 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
318 const RECT *src_rect, const RECT *dst_rect, WINED3DTEXTUREFILTERTYPE Filter)
320 struct blt_info info;
322 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
324 glEnable(info.bind_target);
325 checkGLcall("glEnable(bind_target)");
327 context_bind_texture(context, info.bind_target, src_surface->texture_name);
329 /* Filtering for StretchRect */
330 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
331 wined3d_gl_mag_filter(magLookup, Filter));
332 checkGLcall("glTexParameteri");
333 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
334 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
335 checkGLcall("glTexParameteri");
336 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
337 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
338 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
339 glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
340 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
341 checkGLcall("glTexEnvi");
344 glBegin(GL_TRIANGLE_STRIP);
345 glTexCoord3fv(info.coords[0]);
346 glVertex2i(dst_rect->left, dst_rect->top);
348 glTexCoord3fv(info.coords[1]);
349 glVertex2i(dst_rect->right, dst_rect->top);
351 glTexCoord3fv(info.coords[2]);
352 glVertex2i(dst_rect->left, dst_rect->bottom);
354 glTexCoord3fv(info.coords[3]);
355 glVertex2i(dst_rect->right, dst_rect->bottom);
358 /* Unbind the texture */
359 context_bind_texture(context, info.bind_target, 0);
361 /* We changed the filtering settings on the texture. Inform the
362 * container about this to get the filters reset properly next draw. */
363 if (src_surface->container.type == WINED3D_CONTAINER_TEXTURE)
365 struct wined3d_texture *texture = src_surface->container.u.texture;
366 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
367 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
368 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
369 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
373 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
375 const struct wined3d_format *format = surface->resource.format;
383 TRACE("surface %p.\n", surface);
385 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
387 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
388 return WINED3DERR_INVALIDCALL;
391 switch (format->byte_count)
395 /* Allocate extra space to store the RGB bit masks. */
396 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
400 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
404 /* Allocate extra space for a palette. */
405 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
406 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
411 return E_OUTOFMEMORY;
413 /* Some applications access the surface in via DWORDs, and do not take
414 * the necessary care at the end of the surface. So we need at least
415 * 4 extra bytes at the end of the surface. Check against the page size,
416 * if the last page used for the surface has at least 4 spare bytes we're
417 * safe, otherwise add an extra line to the DIB section. */
418 GetSystemInfo(&sysInfo);
419 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
422 TRACE("Adding an extra line to the DIB section.\n");
425 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
426 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
427 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
428 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
429 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
430 * wined3d_surface_get_pitch(surface);
431 b_info->bmiHeader.biPlanes = 1;
432 b_info->bmiHeader.biBitCount = format->byte_count * 8;
434 b_info->bmiHeader.biXPelsPerMeter = 0;
435 b_info->bmiHeader.biYPelsPerMeter = 0;
436 b_info->bmiHeader.biClrUsed = 0;
437 b_info->bmiHeader.biClrImportant = 0;
439 /* Get the bit masks */
440 masks = (DWORD *)b_info->bmiColors;
441 switch (surface->resource.format->id)
443 case WINED3DFMT_B8G8R8_UNORM:
444 usage = DIB_RGB_COLORS;
445 b_info->bmiHeader.biCompression = BI_RGB;
448 case WINED3DFMT_B5G5R5X1_UNORM:
449 case WINED3DFMT_B5G5R5A1_UNORM:
450 case WINED3DFMT_B4G4R4A4_UNORM:
451 case WINED3DFMT_B4G4R4X4_UNORM:
452 case WINED3DFMT_B2G3R3_UNORM:
453 case WINED3DFMT_B2G3R3A8_UNORM:
454 case WINED3DFMT_R10G10B10A2_UNORM:
455 case WINED3DFMT_R8G8B8A8_UNORM:
456 case WINED3DFMT_R8G8B8X8_UNORM:
457 case WINED3DFMT_B10G10R10A2_UNORM:
458 case WINED3DFMT_B5G6R5_UNORM:
459 case WINED3DFMT_R16G16B16A16_UNORM:
461 b_info->bmiHeader.biCompression = BI_BITFIELDS;
462 masks[0] = format->red_mask;
463 masks[1] = format->green_mask;
464 masks[2] = format->blue_mask;
468 /* Don't know palette */
469 b_info->bmiHeader.biCompression = BI_RGB;
474 if (!(dc = GetDC(0)))
476 HeapFree(GetProcessHeap(), 0, b_info);
477 return HRESULT_FROM_WIN32(GetLastError());
480 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
481 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
482 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
483 surface->dib.DIBsection = CreateDIBSection(dc, b_info, usage, &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));
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 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
518 surface->resource.heapMemory = NULL;
523 static BOOL surface_need_pbo(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
525 if (surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
527 if (!(surface->flags & SFLAG_DYNLOCK))
529 if (surface->flags & (SFLAG_CONVERTED | SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM))
531 if (!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT])
537 static void surface_load_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
539 struct wined3d_context *context;
542 context = context_acquire(surface->resource.device, NULL);
545 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
546 error = glGetError();
547 if (!surface->pbo || error != GL_NO_ERROR)
548 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
550 TRACE("Binding PBO %u.\n", surface->pbo);
552 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
553 checkGLcall("glBindBufferARB");
555 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
556 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
557 checkGLcall("glBufferDataARB");
559 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
560 checkGLcall("glBindBufferARB");
562 /* We don't need the system memory anymore and we can't even use it for PBOs. */
563 if (!(surface->flags & SFLAG_CLIENT))
565 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
566 surface->resource.heapMemory = NULL;
568 surface->resource.allocatedMemory = NULL;
569 surface->flags |= SFLAG_PBO;
571 context_release(context);
574 static void surface_prepare_system_memory(struct wined3d_surface *surface)
576 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
578 TRACE("surface %p.\n", surface);
580 if (!(surface->flags & SFLAG_PBO) && surface_need_pbo(surface, gl_info))
581 surface_load_pbo(surface, gl_info);
582 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
584 /* Whatever surface we have, make sure that there is memory allocated
585 * for the downloaded copy, or a PBO to map. */
586 if (!surface->resource.heapMemory)
587 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
589 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
590 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
592 if (surface->flags & SFLAG_INSYSMEM)
593 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
597 static void surface_evict_sysmem(struct wined3d_surface *surface)
599 if (surface->flags & SFLAG_DONOTFREE)
602 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
603 surface->resource.allocatedMemory = NULL;
604 surface->resource.heapMemory = NULL;
605 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
608 /* Context activation is done by the caller. */
609 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
610 struct wined3d_context *context, BOOL srgb)
612 struct wined3d_device *device = surface->resource.device;
613 DWORD active_sampler;
615 /* We don't need a specific texture unit, but after binding the texture
616 * the current unit is dirty. Read the unit back instead of switching to
617 * 0, this avoids messing around with the state manager's GL states. The
618 * current texture unit should always be a valid one.
620 * To be more specific, this is tricky because we can implicitly be
621 * called from sampler() in state.c. This means we can't touch anything
622 * other than whatever happens to be the currently active texture, or we
623 * would risk marking already applied sampler states dirty again. */
624 active_sampler = device->rev_tex_unit_map[context->active_texture];
626 if (active_sampler != WINED3D_UNMAPPED_STAGE)
627 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
628 surface_bind(surface, context, srgb);
631 static void surface_force_reload(struct wined3d_surface *surface)
633 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
636 static void surface_release_client_storage(struct wined3d_surface *surface)
638 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
641 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
642 if (surface->texture_name)
644 surface_bind_and_dirtify(surface, context, FALSE);
645 glTexImage2D(surface->texture_target, surface->texture_level,
646 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
648 if (surface->texture_name_srgb)
650 surface_bind_and_dirtify(surface, context, TRUE);
651 glTexImage2D(surface->texture_target, surface->texture_level,
652 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
654 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
657 context_release(context);
659 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
660 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
661 surface_force_reload(surface);
664 static HRESULT surface_private_setup(struct wined3d_surface *surface)
666 /* TODO: Check against the maximum texture sizes supported by the video card. */
667 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
668 unsigned int pow2Width, pow2Height;
670 TRACE("surface %p.\n", surface);
672 surface->texture_name = 0;
673 surface->texture_target = GL_TEXTURE_2D;
675 /* Non-power2 support */
676 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
678 pow2Width = surface->resource.width;
679 pow2Height = surface->resource.height;
683 /* Find the nearest pow2 match */
684 pow2Width = pow2Height = 1;
685 while (pow2Width < surface->resource.width)
687 while (pow2Height < surface->resource.height)
690 surface->pow2Width = pow2Width;
691 surface->pow2Height = pow2Height;
693 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
695 /* TODO: Add support for non power two compressed textures. */
696 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
698 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
699 surface, surface->resource.width, surface->resource.height);
700 return WINED3DERR_NOTAVAILABLE;
704 if (pow2Width != surface->resource.width
705 || pow2Height != surface->resource.height)
707 surface->flags |= SFLAG_NONPOW2;
710 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
711 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
713 /* One of three options:
714 * 1: Do the same as we do with NPOT and scale the texture, (any
715 * texture ops would require the texture to be scaled which is
717 * 2: Set the texture to the maximum size (bad idea).
718 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
719 * 4: Create the surface, but allow it to be used only for DirectDraw
720 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
721 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
722 * the render target. */
723 if (surface->resource.pool == WINED3DPOOL_DEFAULT || surface->resource.pool == WINED3DPOOL_MANAGED)
725 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
726 return WINED3DERR_NOTAVAILABLE;
729 /* We should never use this surface in combination with OpenGL! */
730 TRACE("Creating an oversized surface: %ux%u.\n",
731 surface->pow2Width, surface->pow2Height);
735 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
736 * and EXT_PALETTED_TEXTURE is used in combination with texture
737 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
738 * EXT_PALETTED_TEXTURE doesn't work in combination with
739 * ARB_TEXTURE_RECTANGLE. */
740 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
741 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
742 && gl_info->supported[EXT_PALETTED_TEXTURE]
743 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
745 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
746 surface->pow2Width = surface->resource.width;
747 surface->pow2Height = surface->resource.height;
748 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
752 switch (wined3d_settings.offscreen_rendering_mode)
755 surface->get_drawable_size = get_drawable_size_fbo;
759 surface->get_drawable_size = get_drawable_size_backbuffer;
763 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
764 return WINED3DERR_INVALIDCALL;
767 surface->flags |= SFLAG_INSYSMEM;
772 static void surface_realize_palette(struct wined3d_surface *surface)
774 struct wined3d_palette *palette = surface->palette;
776 TRACE("surface %p.\n", surface);
778 if (!palette) return;
780 if (surface->resource.format->id == WINED3DFMT_P8_UINT
781 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
783 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
785 /* Make sure the texture is up to date. This call doesn't do
786 * anything if the texture is already up to date. */
787 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
789 /* We want to force a palette refresh, so mark the drawable as not being up to date */
790 if (!surface_is_offscreen(surface))
791 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
795 if (!(surface->flags & SFLAG_INSYSMEM))
797 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
798 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
800 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
804 if (surface->flags & SFLAG_DIBSECTION)
809 TRACE("Updating the DC's palette.\n");
811 for (i = 0; i < 256; ++i)
813 col[i].rgbRed = palette->palents[i].peRed;
814 col[i].rgbGreen = palette->palents[i].peGreen;
815 col[i].rgbBlue = palette->palents[i].peBlue;
816 col[i].rgbReserved = 0;
818 SetDIBColorTable(surface->hDC, 0, 256, col);
821 /* Propagate the changes to the drawable when we have a palette. */
822 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
823 surface_load_location(surface, surface->draw_binding, NULL);
826 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
830 /* If there's no destination surface there is nothing to do. */
831 if (!surface->overlay_dest)
834 /* Blt calls ModifyLocation on the dest surface, which in turn calls
835 * DrawOverlay to update the overlay. Prevent an endless recursion. */
836 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
839 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
840 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
841 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3DTEXF_LINEAR);
842 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
847 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
849 struct wined3d_device *device = surface->resource.device;
850 const RECT *pass_rect = rect;
852 TRACE("surface %p, rect %s, flags %#x.\n",
853 surface, wine_dbgstr_rect(rect), flags);
855 if (flags & WINED3DLOCK_DISCARD)
857 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
858 surface_prepare_system_memory(surface);
859 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
863 /* surface_load_location() does not check if the rectangle specifies
864 * the full surface. Most callers don't need that, so do it here. */
865 if (rect && !rect->top && !rect->left
866 && rect->right == surface->resource.width
867 && rect->bottom == surface->resource.height)
869 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
872 if (surface->flags & SFLAG_PBO)
874 const struct wined3d_gl_info *gl_info;
875 struct wined3d_context *context;
877 context = context_acquire(device, NULL);
878 gl_info = context->gl_info;
881 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
882 checkGLcall("glBindBufferARB");
884 /* This shouldn't happen but could occur if some other function
885 * didn't handle the PBO properly. */
886 if (surface->resource.allocatedMemory)
887 ERR("The surface already has PBO memory allocated.\n");
889 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
890 checkGLcall("glMapBufferARB");
892 /* Make sure the PBO isn't set anymore in order not to break non-PBO
894 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
895 checkGLcall("glBindBufferARB");
898 context_release(context);
901 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
904 surface_add_dirty_rect(surface, NULL);
907 struct wined3d_box b;
911 b.right = rect->right;
912 b.bottom = rect->bottom;
915 surface_add_dirty_rect(surface, &b);
920 static void surface_unmap(struct wined3d_surface *surface)
922 struct wined3d_device *device = surface->resource.device;
925 TRACE("surface %p.\n", surface);
927 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
929 if (surface->flags & SFLAG_PBO)
931 const struct wined3d_gl_info *gl_info;
932 struct wined3d_context *context;
934 TRACE("Freeing PBO memory.\n");
936 context = context_acquire(device, NULL);
937 gl_info = context->gl_info;
940 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
941 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
942 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
943 checkGLcall("glUnmapBufferARB");
945 context_release(context);
947 surface->resource.allocatedMemory = NULL;
950 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
952 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
954 TRACE("Not dirtified, nothing to do.\n");
958 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
959 || (device->fb.render_targets && surface == device->fb.render_targets[0]))
961 if (!surface->dirtyRect.left && !surface->dirtyRect.top
962 && surface->dirtyRect.right == surface->resource.width
963 && surface->dirtyRect.bottom == surface->resource.height)
969 /* TODO: Proper partial rectangle tracking. */
971 surface->flags |= SFLAG_INSYSMEM;
974 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
976 /* Partial rectangle tracking is not commonly implemented, it is only
977 * done for render targets. INSYSMEM was set before to tell
978 * surface_load_location() where to read the rectangle from.
979 * Indrawable is set because all modifications from the partial
980 * sysmem copy are written back to the drawable, thus the surface is
981 * merged again in the drawable. The sysmem copy is not fully up to
982 * date because only a subrectangle was read in Map(). */
985 surface_modify_location(surface, surface->draw_binding, TRUE);
986 surface_evict_sysmem(surface);
989 surface->dirtyRect.left = surface->resource.width;
990 surface->dirtyRect.top = surface->resource.height;
991 surface->dirtyRect.right = 0;
992 surface->dirtyRect.bottom = 0;
994 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
996 FIXME("Depth / stencil buffer locking is not implemented.\n");
1000 /* Overlays have to be redrawn manually after changes with the GL implementation */
1001 if (surface->overlay_dest)
1002 surface_draw_overlay(surface);
1005 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1007 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1009 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1014 static void wined3d_surface_depth_blt_fbo(const struct wined3d_device *device, struct wined3d_surface *src_surface,
1015 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1017 const struct wined3d_gl_info *gl_info;
1018 struct wined3d_context *context;
1019 DWORD src_mask, dst_mask;
1022 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1023 device, src_surface, wine_dbgstr_rect(src_rect),
1024 dst_surface, wine_dbgstr_rect(dst_rect));
1026 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1027 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1029 if (src_mask != dst_mask)
1031 ERR("Incompatible formats %s and %s.\n",
1032 debug_d3dformat(src_surface->resource.format->id),
1033 debug_d3dformat(dst_surface->resource.format->id));
1039 ERR("Not a depth / stencil format: %s.\n",
1040 debug_d3dformat(src_surface->resource.format->id));
1045 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1046 gl_mask |= GL_DEPTH_BUFFER_BIT;
1047 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1048 gl_mask |= GL_STENCIL_BUFFER_BIT;
1050 /* Make sure the locations are up-to-date. Loading the destination
1051 * surface isn't required if the entire surface is overwritten. */
1052 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1053 if (!surface_is_full_rect(dst_surface, dst_rect))
1054 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1056 context = context_acquire(device, NULL);
1057 if (!context->valid)
1059 context_release(context);
1060 WARN("Invalid context, skipping blit.\n");
1064 gl_info = context->gl_info;
1068 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1069 glReadBuffer(GL_NONE);
1070 checkGLcall("glReadBuffer()");
1071 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1073 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1074 context_set_draw_buffer(context, GL_NONE);
1075 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1077 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1079 glDepthMask(GL_TRUE);
1080 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
1082 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1084 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1086 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1087 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
1090 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
1093 glDisable(GL_SCISSOR_TEST);
1094 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1096 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1097 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1098 checkGLcall("glBlitFramebuffer()");
1102 if (wined3d_settings.strict_draw_ordering)
1103 wglFlush(); /* Flush to ensure ordering across contexts. */
1105 context_release(context);
1108 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1109 * Depth / stencil is not supported. */
1110 static void surface_blt_fbo(const struct wined3d_device *device, const WINED3DTEXTUREFILTERTYPE filter,
1111 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1112 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1114 const struct wined3d_gl_info *gl_info;
1115 struct wined3d_context *context;
1116 RECT src_rect, dst_rect;
1120 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1121 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1122 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1123 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1124 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1126 src_rect = *src_rect_in;
1127 dst_rect = *dst_rect_in;
1131 case WINED3DTEXF_LINEAR:
1132 gl_filter = GL_LINEAR;
1136 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1137 case WINED3DTEXF_NONE:
1138 case WINED3DTEXF_POINT:
1139 gl_filter = GL_NEAREST;
1143 /* Resolve the source surface first if needed. */
1144 if (src_location == SFLAG_INRB_MULTISAMPLE
1145 && (src_surface->resource.format->id != dst_surface->resource.format->id
1146 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1147 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1148 src_location = SFLAG_INRB_RESOLVED;
1150 /* Make sure the locations are up-to-date. Loading the destination
1151 * surface isn't required if the entire surface is overwritten. (And is
1152 * in fact harmful if we're being called by surface_load_location() with
1153 * the purpose of loading the destination surface.) */
1154 surface_load_location(src_surface, src_location, NULL);
1155 if (!surface_is_full_rect(dst_surface, &dst_rect))
1156 surface_load_location(dst_surface, dst_location, NULL);
1158 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1159 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1160 else context = context_acquire(device, NULL);
1162 if (!context->valid)
1164 context_release(context);
1165 WARN("Invalid context, skipping blit.\n");
1169 gl_info = context->gl_info;
1171 if (src_location == SFLAG_INDRAWABLE)
1173 TRACE("Source surface %p is onscreen.\n", src_surface);
1174 buffer = surface_get_gl_buffer(src_surface);
1175 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1179 TRACE("Source surface %p is offscreen.\n", src_surface);
1180 buffer = GL_COLOR_ATTACHMENT0;
1184 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1185 glReadBuffer(buffer);
1186 checkGLcall("glReadBuffer()");
1187 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1190 if (dst_location == SFLAG_INDRAWABLE)
1192 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1193 buffer = surface_get_gl_buffer(dst_surface);
1194 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1198 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1199 buffer = GL_COLOR_ATTACHMENT0;
1203 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1204 context_set_draw_buffer(context, buffer);
1205 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1206 context_invalidate_state(context, STATE_FRAMEBUFFER);
1208 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1209 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
1210 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
1211 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
1212 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
1214 glDisable(GL_SCISSOR_TEST);
1215 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1217 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1218 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1219 checkGLcall("glBlitFramebuffer()");
1223 if (wined3d_settings.strict_draw_ordering
1224 || (dst_location == SFLAG_INDRAWABLE
1225 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1228 context_release(context);
1231 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1232 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
1233 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
1235 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1238 /* Source and/or destination need to be on the GL side */
1239 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
1244 case WINED3D_BLIT_OP_COLOR_BLIT:
1245 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1247 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1251 case WINED3D_BLIT_OP_DEPTH_BLIT:
1252 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1254 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1262 if (!(src_format->id == dst_format->id
1263 || (is_identity_fixup(src_format->color_fixup)
1264 && is_identity_fixup(dst_format->color_fixup))))
1270 /* This function checks if the primary render target uses the 8bit paletted format. */
1271 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1273 if (device->fb.render_targets && device->fb.render_targets[0])
1275 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1276 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1277 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1283 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1284 DWORD color, struct wined3d_color *float_color)
1286 const struct wined3d_format *format = surface->resource.format;
1287 const struct wined3d_device *device = surface->resource.device;
1291 case WINED3DFMT_P8_UINT:
1292 if (surface->palette)
1294 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1295 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1296 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1300 float_color->r = 0.0f;
1301 float_color->g = 0.0f;
1302 float_color->b = 0.0f;
1304 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1307 case WINED3DFMT_B5G6R5_UNORM:
1308 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1309 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1310 float_color->b = (color & 0x1f) / 31.0f;
1311 float_color->a = 1.0f;
1314 case WINED3DFMT_B8G8R8_UNORM:
1315 case WINED3DFMT_B8G8R8X8_UNORM:
1316 float_color->r = D3DCOLOR_R(color);
1317 float_color->g = D3DCOLOR_G(color);
1318 float_color->b = D3DCOLOR_B(color);
1319 float_color->a = 1.0f;
1322 case WINED3DFMT_B8G8R8A8_UNORM:
1323 float_color->r = D3DCOLOR_R(color);
1324 float_color->g = D3DCOLOR_G(color);
1325 float_color->b = D3DCOLOR_B(color);
1326 float_color->a = D3DCOLOR_A(color);
1330 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1337 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1339 const struct wined3d_format *format = surface->resource.format;
1343 case WINED3DFMT_S1_UINT_D15_UNORM:
1344 *float_depth = depth / (float)0x00007fff;
1347 case WINED3DFMT_D16_UNORM:
1348 *float_depth = depth / (float)0x0000ffff;
1351 case WINED3DFMT_D24_UNORM_S8_UINT:
1352 case WINED3DFMT_X8D24_UNORM:
1353 *float_depth = depth / (float)0x00ffffff;
1356 case WINED3DFMT_D32_UNORM:
1357 *float_depth = depth / (float)0xffffffff;
1361 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1368 /* Do not call while under the GL lock. */
1369 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1371 const struct wined3d_resource *resource = &surface->resource;
1372 struct wined3d_device *device = resource->device;
1373 const struct blit_shader *blitter;
1375 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1376 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1379 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1380 return WINED3DERR_INVALIDCALL;
1383 return blitter->depth_fill(device, surface, rect, depth);
1386 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1387 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1389 struct wined3d_device *device = src_surface->resource.device;
1391 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1392 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1393 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1394 return WINED3DERR_INVALIDCALL;
1396 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1398 surface_modify_ds_location(dst_surface, SFLAG_INTEXTURE,
1399 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1404 /* Do not call while under the GL lock. */
1405 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1406 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1407 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1409 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1410 struct wined3d_device *device = dst_surface->resource.device;
1411 DWORD src_ds_flags, dst_ds_flags;
1412 RECT src_rect, dst_rect;
1413 BOOL scale, convert;
1415 static const DWORD simple_blit = WINEDDBLT_ASYNC
1416 | WINEDDBLT_COLORFILL
1418 | WINEDDBLT_DEPTHFILL
1419 | WINEDDBLT_DONOTWAIT;
1421 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1422 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1423 flags, fx, debug_d3dtexturefiltertype(filter));
1424 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1428 TRACE("dwSize %#x.\n", fx->dwSize);
1429 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
1430 TRACE("dwROP %#x.\n", fx->dwROP);
1431 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
1432 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
1433 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
1434 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
1435 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
1436 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
1437 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
1438 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
1439 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
1440 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
1441 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
1442 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
1443 TRACE("dwReserved %#x.\n", fx->dwReserved);
1444 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
1445 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
1446 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
1447 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
1448 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
1449 TRACE("ddckDestColorkey {%#x, %#x}.\n",
1450 fx->ddckDestColorkey.color_space_low_value,
1451 fx->ddckDestColorkey.color_space_high_value);
1452 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
1453 fx->ddckSrcColorkey.color_space_low_value,
1454 fx->ddckSrcColorkey.color_space_high_value);
1457 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1459 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1460 return WINEDDERR_SURFACEBUSY;
1463 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1465 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1466 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1467 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1468 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1469 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1471 WARN("The application gave us a bad destination rectangle.\n");
1472 return WINEDDERR_INVALIDRECT;
1477 surface_get_rect(src_surface, src_rect_in, &src_rect);
1479 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1480 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1481 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1482 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1483 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1485 WARN("Application gave us bad source rectangle for Blt.\n");
1486 return WINEDDERR_INVALIDRECT;
1491 memset(&src_rect, 0, sizeof(src_rect));
1494 if (!fx || !(fx->dwDDFX))
1495 flags &= ~WINEDDBLT_DDFX;
1497 if (flags & WINEDDBLT_WAIT)
1498 flags &= ~WINEDDBLT_WAIT;
1500 if (flags & WINEDDBLT_ASYNC)
1502 static unsigned int once;
1505 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1506 flags &= ~WINEDDBLT_ASYNC;
1509 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1510 if (flags & WINEDDBLT_DONOTWAIT)
1512 static unsigned int once;
1515 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1516 flags &= ~WINEDDBLT_DONOTWAIT;
1519 if (!device->d3d_initialized)
1521 WARN("D3D not initialized, using fallback.\n");
1525 /* We want to avoid invalidating the sysmem location for converted
1526 * surfaces, since otherwise we'd have to convert the data back when
1528 if (dst_surface->flags & SFLAG_CONVERTED)
1530 WARN("Converted surface, using CPU blit.\n");
1531 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1534 if (flags & ~simple_blit)
1536 WARN("Using fallback for complex blit (%#x).\n", flags);
1540 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1541 src_swapchain = src_surface->container.u.swapchain;
1543 src_swapchain = NULL;
1545 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1546 dst_swapchain = dst_surface->container.u.swapchain;
1548 dst_swapchain = NULL;
1550 /* This isn't strictly needed. FBO blits for example could deal with
1551 * cross-swapchain blits by first downloading the source to a texture
1552 * before switching to the destination context. We just have this here to
1553 * not have to deal with the issue, since cross-swapchain blits should be
1555 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1557 FIXME("Using fallback for cross-swapchain blit.\n");
1562 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
1563 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
1564 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
1566 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1568 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1572 if (src_ds_flags || dst_ds_flags)
1574 if (flags & WINEDDBLT_DEPTHFILL)
1578 TRACE("Depth fill.\n");
1580 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1581 return WINED3DERR_INVALIDCALL;
1583 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1588 /* Accessing depth / stencil surfaces is supposed to fail while in
1589 * a scene, except for fills, which seem to work. */
1590 if (device->inScene)
1592 WARN("Rejecting depth / stencil access while in scene.\n");
1593 return WINED3DERR_INVALIDCALL;
1596 if (src_ds_flags != dst_ds_flags)
1598 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1599 return WINED3DERR_INVALIDCALL;
1602 if (src_rect.top || src_rect.left
1603 || src_rect.bottom != src_surface->resource.height
1604 || src_rect.right != src_surface->resource.width)
1606 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1607 wine_dbgstr_rect(&src_rect));
1608 return WINED3DERR_INVALIDCALL;
1611 if (dst_rect.top || dst_rect.left
1612 || dst_rect.bottom != dst_surface->resource.height
1613 || dst_rect.right != dst_surface->resource.width)
1615 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1616 wine_dbgstr_rect(&src_rect));
1617 return WINED3DERR_INVALIDCALL;
1622 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1623 return WINED3DERR_INVALIDCALL;
1626 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1632 /* In principle this would apply to depth blits as well, but we don't
1633 * implement those in the CPU blitter at the moment. */
1634 if ((dst_surface->flags & SFLAG_INSYSMEM)
1635 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
1638 TRACE("Not doing sysmem blit because of scaling.\n");
1640 TRACE("Not doing sysmem blit because of format conversion.\n");
1642 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1645 if (flags & WINEDDBLT_COLORFILL)
1647 struct wined3d_color color;
1649 TRACE("Color fill.\n");
1651 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1654 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1659 TRACE("Color blit.\n");
1662 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
1665 TRACE("Not doing upload because of scaling.\n");
1667 TRACE("Not doing upload because of format conversion.\n");
1670 POINT dst_point = {dst_rect.left, dst_rect.top};
1672 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
1674 if (!surface_is_offscreen(dst_surface))
1675 surface_load_location(dst_surface, dst_surface->draw_binding, NULL);
1681 /* Use present for back -> front blits. The idea behind this is
1682 * that present is potentially faster than a blit, in particular
1683 * when FBO blits aren't available. Some ddraw applications like
1684 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
1685 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
1686 * applications can't blit directly to the frontbuffer. */
1687 if (dst_swapchain && dst_swapchain->back_buffers
1688 && dst_surface == dst_swapchain->front_buffer
1689 && src_surface == dst_swapchain->back_buffers[0])
1691 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
1693 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1695 /* Set the swap effect to COPY, we don't want the backbuffer
1696 * to become undefined. */
1697 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
1698 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
1699 dst_swapchain->desc.swap_effect = swap_effect;
1704 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1705 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1706 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1708 TRACE("Using FBO blit.\n");
1710 surface_blt_fbo(device, filter,
1711 src_surface, src_surface->draw_binding, &src_rect,
1712 dst_surface, dst_surface->draw_binding, &dst_rect);
1713 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1717 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1718 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1719 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1721 TRACE("Using arbfp blit.\n");
1723 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1731 /* Special cases for render targets. */
1732 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1733 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1735 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1736 src_surface, &src_rect, flags, fx, filter)))
1742 /* For the rest call the X11 surface implementation. For render targets
1743 * this should be implemented OpenGL accelerated in BltOverride, other
1744 * blits are rather rare. */
1745 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1748 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1749 struct wined3d_surface *render_target)
1751 TRACE("surface %p, render_target %p.\n", surface, render_target);
1753 /* TODO: Check surface sizes, pools, etc. */
1755 if (render_target->resource.multisample_type)
1756 return WINED3DERR_INVALIDCALL;
1758 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3DTEXF_POINT);
1761 /* Context activation is done by the caller. */
1762 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1764 if (surface->flags & SFLAG_DIBSECTION)
1766 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1770 if (!surface->resource.heapMemory)
1771 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1772 else if (!(surface->flags & SFLAG_CLIENT))
1773 ERR("Surface %p has heapMemory %p and flags %#x.\n",
1774 surface, surface->resource.heapMemory, surface->flags);
1776 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1777 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1781 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1782 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1783 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1784 surface->resource.size, surface->resource.allocatedMemory));
1785 checkGLcall("glGetBufferSubDataARB");
1786 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1787 checkGLcall("glDeleteBuffersARB");
1791 surface->flags &= ~SFLAG_PBO;
1794 /* Do not call while under the GL lock. */
1795 static void surface_unload(struct wined3d_resource *resource)
1797 struct wined3d_surface *surface = surface_from_resource(resource);
1798 struct wined3d_renderbuffer_entry *entry, *entry2;
1799 struct wined3d_device *device = resource->device;
1800 const struct wined3d_gl_info *gl_info;
1801 struct wined3d_context *context;
1803 TRACE("surface %p.\n", surface);
1805 if (resource->pool == WINED3DPOOL_DEFAULT)
1807 /* Default pool resources are supposed to be destroyed before Reset is called.
1808 * Implicit resources stay however. So this means we have an implicit render target
1809 * or depth stencil. The content may be destroyed, but we still have to tear down
1810 * opengl resources, so we cannot leave early.
1812 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1813 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1814 * or the depth stencil into an FBO the texture or render buffer will be removed
1815 * and all flags get lost
1817 if (!(surface->flags & SFLAG_PBO))
1818 surface_init_sysmem(surface);
1819 /* We also get here when the ddraw swapchain is destroyed, for example
1820 * for a mode switch. In this case this surface won't necessarily be
1821 * an implicit surface. We have to mark it lost so that the
1822 * application can restore it after the mode switch. */
1823 surface->flags |= SFLAG_LOST;
1827 /* Load the surface into system memory */
1828 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1829 surface_modify_location(surface, surface->draw_binding, FALSE);
1831 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1832 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1833 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1835 context = context_acquire(device, NULL);
1836 gl_info = context->gl_info;
1838 /* Destroy PBOs, but load them into real sysmem before */
1839 if (surface->flags & SFLAG_PBO)
1840 surface_remove_pbo(surface, gl_info);
1842 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1843 * all application-created targets the application has to release the surface
1844 * before calling _Reset
1846 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1849 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1851 list_remove(&entry->entry);
1852 HeapFree(GetProcessHeap(), 0, entry);
1854 list_init(&surface->renderbuffers);
1855 surface->current_renderbuffer = NULL;
1859 /* If we're in a texture, the texture name belongs to the texture.
1860 * Otherwise, destroy it. */
1861 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1863 glDeleteTextures(1, &surface->texture_name);
1864 surface->texture_name = 0;
1865 glDeleteTextures(1, &surface->texture_name_srgb);
1866 surface->texture_name_srgb = 0;
1868 if (surface->rb_multisample)
1870 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1871 surface->rb_multisample = 0;
1873 if (surface->rb_resolved)
1875 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1876 surface->rb_resolved = 0;
1881 context_release(context);
1883 resource_unload(resource);
1886 static const struct wined3d_resource_ops surface_resource_ops =
1891 static const struct wined3d_surface_ops surface_ops =
1893 surface_private_setup,
1894 surface_realize_palette,
1899 /*****************************************************************************
1900 * Initializes the GDI surface, aka creates the DIB section we render to
1901 * The DIB section creation is done by calling GetDC, which will create the
1902 * section and releasing the dc to allow the app to use it. The dib section
1903 * will stay until the surface is released
1905 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1906 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1907 * avoid confusion in the shared surface code.
1910 * WINED3D_OK on success
1911 * The return values of called methods on failure
1913 *****************************************************************************/
1914 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1918 TRACE("surface %p.\n", surface);
1920 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1922 ERR("Overlays not yet supported by GDI surfaces.\n");
1923 return WINED3DERR_INVALIDCALL;
1926 /* Sysmem textures have memory already allocated - release it,
1927 * this avoids an unnecessary memcpy. */
1928 hr = surface_create_dib_section(surface);
1931 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1932 surface->resource.heapMemory = NULL;
1933 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1936 /* We don't mind the nonpow2 stuff in GDI. */
1937 surface->pow2Width = surface->resource.width;
1938 surface->pow2Height = surface->resource.height;
1943 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1945 struct wined3d_palette *palette = surface->palette;
1947 TRACE("surface %p.\n", surface);
1949 if (!palette) return;
1951 if (surface->flags & SFLAG_DIBSECTION)
1956 TRACE("Updating the DC's palette.\n");
1958 for (i = 0; i < 256; ++i)
1960 col[i].rgbRed = palette->palents[i].peRed;
1961 col[i].rgbGreen = palette->palents[i].peGreen;
1962 col[i].rgbBlue = palette->palents[i].peBlue;
1963 col[i].rgbReserved = 0;
1965 SetDIBColorTable(surface->hDC, 0, 256, col);
1968 /* Update the image because of the palette change. Some games like e.g.
1969 * Red Alert call SetEntries a lot to implement fading. */
1970 /* Tell the swapchain to update the screen. */
1971 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1973 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1974 if (surface == swapchain->front_buffer)
1976 x11_copy_to_screen(swapchain, NULL);
1981 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
1983 TRACE("surface %p, rect %s, flags %#x.\n",
1984 surface, wine_dbgstr_rect(rect), flags);
1986 if (!(surface->flags & SFLAG_DIBSECTION))
1988 /* This happens on gdi surfaces if the application set a user pointer
1989 * and resets it. Recreate the DIB section. */
1990 surface_create_dib_section(surface);
1991 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1995 static void gdi_surface_unmap(struct wined3d_surface *surface)
1997 TRACE("surface %p.\n", surface);
1999 /* Tell the swapchain to update the screen. */
2000 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2002 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2003 if (surface == swapchain->front_buffer)
2005 x11_copy_to_screen(swapchain, &surface->lockedRect);
2009 memset(&surface->lockedRect, 0, sizeof(RECT));
2012 static const struct wined3d_surface_ops gdi_surface_ops =
2014 gdi_surface_private_setup,
2015 gdi_surface_realize_palette,
2020 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2025 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2029 name = &surface->texture_name_srgb;
2030 flag = SFLAG_INSRGBTEX;
2034 name = &surface->texture_name;
2035 flag = SFLAG_INTEXTURE;
2038 if (!*name && new_name)
2040 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2041 * surface has no texture name yet. See if we can get rid of this. */
2042 if (surface->flags & flag)
2044 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2045 surface_modify_location(surface, flag, FALSE);
2050 surface_force_reload(surface);
2053 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2055 TRACE("surface %p, target %#x.\n", surface, target);
2057 if (surface->texture_target != target)
2059 if (target == GL_TEXTURE_RECTANGLE_ARB)
2061 surface->flags &= ~SFLAG_NORMCOORD;
2063 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2065 surface->flags |= SFLAG_NORMCOORD;
2068 surface->texture_target = target;
2069 surface_force_reload(surface);
2072 /* Context activation is done by the caller. */
2073 void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
2075 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
2077 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2079 struct wined3d_texture *texture = surface->container.u.texture;
2081 TRACE("Passing to container (%p).\n", texture);
2082 texture->texture_ops->texture_bind(texture, context, srgb);
2086 if (surface->texture_level)
2088 ERR("Standalone surface %p is non-zero texture level %u.\n",
2089 surface, surface->texture_level);
2093 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2097 if (!surface->texture_name)
2099 glGenTextures(1, &surface->texture_name);
2100 checkGLcall("glGenTextures");
2102 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2104 context_bind_texture(context, surface->texture_target, surface->texture_name);
2105 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2106 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2107 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2108 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2109 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2110 checkGLcall("glTexParameteri");
2114 context_bind_texture(context, surface->texture_target, surface->texture_name);
2121 /* This call just downloads data, the caller is responsible for binding the
2122 * correct texture. */
2123 /* Context activation is done by the caller. */
2124 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2126 const struct wined3d_format *format = surface->resource.format;
2128 /* Only support read back of converted P8 surfaces. */
2129 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2131 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
2137 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2139 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2140 surface, surface->texture_level, format->glFormat, format->glType,
2141 surface->resource.allocatedMemory);
2143 if (surface->flags & SFLAG_PBO)
2145 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2146 checkGLcall("glBindBufferARB");
2147 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2148 checkGLcall("glGetCompressedTexImageARB");
2149 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2150 checkGLcall("glBindBufferARB");
2154 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2155 surface->texture_level, surface->resource.allocatedMemory));
2156 checkGLcall("glGetCompressedTexImageARB");
2164 GLenum gl_format = format->glFormat;
2165 GLenum gl_type = format->glType;
2169 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2170 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2172 gl_format = GL_ALPHA;
2173 gl_type = GL_UNSIGNED_BYTE;
2176 if (surface->flags & SFLAG_NONPOW2)
2178 unsigned char alignment = surface->resource.device->surface_alignment;
2179 src_pitch = format->byte_count * surface->pow2Width;
2180 dst_pitch = wined3d_surface_get_pitch(surface);
2181 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2182 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2186 mem = surface->resource.allocatedMemory;
2189 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2190 surface, surface->texture_level, gl_format, gl_type, mem);
2192 if (surface->flags & SFLAG_PBO)
2194 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2195 checkGLcall("glBindBufferARB");
2197 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2198 checkGLcall("glGetTexImage");
2200 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2201 checkGLcall("glBindBufferARB");
2205 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2206 checkGLcall("glGetTexImage");
2210 if (surface->flags & SFLAG_NONPOW2)
2212 const BYTE *src_data;
2216 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2217 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2218 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2220 * We're doing this...
2222 * instead of boxing the texture :
2223 * |<-texture width ->| -->pow2width| /\
2224 * |111111111111111111| | |
2225 * |222 Texture 222222| boxed empty | texture height
2226 * |3333 Data 33333333| | |
2227 * |444444444444444444| | \/
2228 * ----------------------------------- |
2229 * | boxed empty | boxed empty | pow2height
2231 * -----------------------------------
2234 * we're repacking the data to the expected texture width
2236 * |<-texture width ->| -->pow2width| /\
2237 * |111111111111111111222222222222222| |
2238 * |222333333333333333333444444444444| texture height
2242 * | empty | pow2height
2244 * -----------------------------------
2248 * |<-texture width ->| /\
2249 * |111111111111111111|
2250 * |222222222222222222|texture height
2251 * |333333333333333333|
2252 * |444444444444444444| \/
2253 * --------------------
2255 * this also means that any references to allocatedMemory should work with the data as if were a
2256 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2258 * internally the texture is still stored in a boxed format so any references to textureName will
2259 * get a boxed texture with width pow2width and not a texture of width resource.width.
2261 * Performance should not be an issue, because applications normally do not lock the surfaces when
2262 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2263 * and doesn't have to be re-read. */
2265 dst_data = surface->resource.allocatedMemory;
2266 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2267 for (y = 1; y < surface->resource.height; ++y)
2269 /* skip the first row */
2270 src_data += src_pitch;
2271 dst_data += dst_pitch;
2272 memcpy(dst_data, src_data, dst_pitch);
2275 HeapFree(GetProcessHeap(), 0, mem);
2279 /* Surface has now been downloaded */
2280 surface->flags |= SFLAG_INSYSMEM;
2283 /* This call just uploads data, the caller is responsible for binding the
2284 * correct texture. */
2285 /* Context activation is done by the caller. */
2286 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2287 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
2288 BOOL srgb, const struct wined3d_bo_address *data)
2290 UINT update_w = src_rect->right - src_rect->left;
2291 UINT update_h = src_rect->bottom - src_rect->top;
2293 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
2294 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
2295 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2297 if (surface->flags & SFLAG_LOCKED)
2299 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
2300 surface->flags |= SFLAG_PIN_SYSMEM;
2303 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2304 update_h *= format->heightscale;
2308 if (data->buffer_object)
2310 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2311 checkGLcall("glBindBufferARB");
2314 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2316 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2317 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2318 const BYTE *addr = data->addr;
2321 addr += (src_rect->top / format->block_height) * src_pitch;
2322 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2325 internal = format->glGammaInternal;
2326 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2327 internal = format->rtInternal;
2329 internal = format->glInternal;
2331 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2332 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2333 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2335 if (row_length == src_pitch)
2337 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2338 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2344 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2345 * can't use the unpack row length like below. */
2346 for (row = 0, y = dst_point->y; row < row_count; ++row)
2348 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2349 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2350 y += format->block_height;
2354 checkGLcall("glCompressedTexSubImage2DARB");
2358 const BYTE *addr = data->addr;
2360 addr += src_rect->top * src_pitch;
2361 addr += src_rect->left * format->byte_count;
2363 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2364 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2365 update_w, update_h, format->glFormat, format->glType, addr);
2367 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
2368 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2369 update_w, update_h, format->glFormat, format->glType, addr);
2370 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2371 checkGLcall("glTexSubImage2D");
2374 if (data->buffer_object)
2376 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2377 checkGLcall("glBindBufferARB");
2382 if (wined3d_settings.strict_draw_ordering)
2385 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2387 struct wined3d_device *device = surface->resource.device;
2390 for (i = 0; i < device->context_count; ++i)
2392 context_surface_update(device->contexts[i], surface);
2397 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
2398 struct wined3d_surface *src_surface, const RECT *src_rect)
2400 const struct wined3d_format *src_format;
2401 const struct wined3d_format *dst_format;
2402 const struct wined3d_gl_info *gl_info;
2403 struct wined3d_context *context;
2404 struct wined3d_bo_address data;
2405 struct wined3d_format format;
2406 UINT update_w, update_h;
2407 CONVERT_TYPES convert;
2414 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
2415 dst_surface, wine_dbgstr_point(dst_point),
2416 src_surface, wine_dbgstr_rect(src_rect));
2418 src_format = src_surface->resource.format;
2419 dst_format = dst_surface->resource.format;
2421 if (src_format->id != dst_format->id)
2423 WARN("Source and destination surfaces should have the same format.\n");
2424 return WINED3DERR_INVALIDCALL;
2433 else if (dst_point->x < 0 || dst_point->y < 0)
2435 WARN("Invalid destination point.\n");
2436 return WINED3DERR_INVALIDCALL;
2443 r.right = src_surface->resource.width;
2444 r.bottom = src_surface->resource.height;
2447 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
2448 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
2450 WARN("Invalid source rectangle.\n");
2451 return WINED3DERR_INVALIDCALL;
2454 src_w = src_surface->resource.width;
2455 src_h = src_surface->resource.height;
2457 dst_w = dst_surface->resource.width;
2458 dst_h = dst_surface->resource.height;
2460 update_w = src_rect->right - src_rect->left;
2461 update_h = src_rect->bottom - src_rect->top;
2463 if (update_w > dst_w || dst_point->x > dst_w - update_w
2464 || update_h > dst_h || dst_point->y > dst_h - update_h)
2466 WARN("Destination out of bounds.\n");
2467 return WINED3DERR_INVALIDCALL;
2470 /* NPOT block sizes would be silly. */
2471 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS)
2472 && ((update_w & (src_format->block_width - 1) || update_h & (src_format->block_height - 1))
2473 && (src_w != update_w || dst_w != update_w || src_h != update_h || dst_h != update_h)))
2475 WARN("Update rect not block-aligned.\n");
2476 return WINED3DERR_INVALIDCALL;
2479 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
2480 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
2481 if (convert != NO_CONVERSION || format.convert)
2483 RECT dst_rect = {dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h};
2484 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3DTEXF_POINT);
2487 context = context_acquire(dst_surface->resource.device, NULL);
2488 gl_info = context->gl_info;
2490 /* Only load the surface for partial updates. For newly allocated texture
2491 * the texture wouldn't be the current location, and we'd upload zeroes
2492 * just to overwrite them again. */
2493 if (update_w == dst_w && update_h == dst_h)
2494 surface_prepare_texture(dst_surface, context, FALSE);
2496 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
2497 surface_bind(dst_surface, context, FALSE);
2499 data.buffer_object = src_surface->pbo;
2500 data.addr = src_surface->resource.allocatedMemory;
2501 src_pitch = wined3d_surface_get_pitch(src_surface);
2503 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
2505 invalidate_active_texture(dst_surface->resource.device, context);
2507 context_release(context);
2509 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
2513 /* This call just allocates the texture, the caller is responsible for binding
2514 * the correct texture. */
2515 /* Context activation is done by the caller. */
2516 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2517 const struct wined3d_format *format, BOOL srgb)
2519 BOOL enable_client_storage = FALSE;
2520 GLsizei width = surface->pow2Width;
2521 GLsizei height = surface->pow2Height;
2522 const BYTE *mem = NULL;
2527 internal = format->glGammaInternal;
2529 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2531 internal = format->rtInternal;
2535 internal = format->glInternal;
2538 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2540 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",
2541 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2542 internal, width, height, format->glFormat, format->glType);
2546 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2548 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2549 || !surface->resource.allocatedMemory)
2551 /* In some cases we want to disable client storage.
2552 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2553 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2554 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2555 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2557 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2558 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2559 surface->flags &= ~SFLAG_CLIENT;
2560 enable_client_storage = TRUE;
2564 surface->flags |= SFLAG_CLIENT;
2566 /* Point OpenGL to our allocated texture memory. Do not use
2567 * resource.allocatedMemory here because it might point into a
2568 * PBO. Instead use heapMemory, but get the alignment right. */
2569 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2570 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2574 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2576 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2577 internal, width, height, 0, surface->resource.size, mem));
2578 checkGLcall("glCompressedTexImage2DARB");
2582 glTexImage2D(surface->texture_target, surface->texture_level,
2583 internal, width, height, 0, format->glFormat, format->glType, mem);
2584 checkGLcall("glTexImage2D");
2587 if(enable_client_storage) {
2588 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2589 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2594 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2595 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2596 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2597 /* GL locking is done by the caller */
2598 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2600 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2601 struct wined3d_renderbuffer_entry *entry;
2602 GLuint renderbuffer = 0;
2603 unsigned int src_width, src_height;
2604 unsigned int width, height;
2606 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2608 width = rt->pow2Width;
2609 height = rt->pow2Height;
2613 width = surface->pow2Width;
2614 height = surface->pow2Height;
2617 src_width = surface->pow2Width;
2618 src_height = surface->pow2Height;
2620 /* A depth stencil smaller than the render target is not valid */
2621 if (width > src_width || height > src_height) return;
2623 /* Remove any renderbuffer set if the sizes match */
2624 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2625 || (width == src_width && height == src_height))
2627 surface->current_renderbuffer = NULL;
2631 /* Look if we've already got a renderbuffer of the correct dimensions */
2632 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2634 if (entry->width == width && entry->height == height)
2636 renderbuffer = entry->id;
2637 surface->current_renderbuffer = entry;
2644 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2645 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2646 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2647 surface->resource.format->glInternal, width, height);
2649 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2650 entry->width = width;
2651 entry->height = height;
2652 entry->id = renderbuffer;
2653 list_add_head(&surface->renderbuffers, &entry->entry);
2655 surface->current_renderbuffer = entry;
2658 checkGLcall("set_compatible_renderbuffer");
2661 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2663 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2665 TRACE("surface %p.\n", surface);
2667 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2669 ERR("Surface %p is not on a swapchain.\n", surface);
2673 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2675 if (swapchain->render_to_fbo)
2677 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2678 return GL_COLOR_ATTACHMENT0;
2680 TRACE("Returning GL_BACK\n");
2683 else if (surface == swapchain->front_buffer)
2685 TRACE("Returning GL_FRONT\n");
2689 FIXME("Higher back buffer, returning GL_BACK\n");
2693 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2694 void surface_add_dirty_rect(struct wined3d_surface *surface, const struct wined3d_box *dirty_rect)
2696 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2698 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2699 /* No partial locking for textures yet. */
2700 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2702 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2705 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->left);
2706 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->top);
2707 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->right);
2708 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->bottom);
2712 surface->dirtyRect.left = 0;
2713 surface->dirtyRect.top = 0;
2714 surface->dirtyRect.right = surface->resource.width;
2715 surface->dirtyRect.bottom = surface->resource.height;
2718 /* if the container is a texture then mark it dirty. */
2719 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2721 TRACE("Passing to container.\n");
2722 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2726 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2728 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2731 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2733 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2735 ERR("Not supported on scratch surfaces.\n");
2736 return WINED3DERR_INVALIDCALL;
2739 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2741 /* Reload if either the texture and sysmem have different ideas about the
2742 * color key, or the actual key values changed. */
2743 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2744 && (surface->gl_color_key.color_space_low_value != surface->src_blt_color_key.color_space_low_value
2745 || surface->gl_color_key.color_space_high_value != surface->src_blt_color_key.color_space_high_value)))
2747 TRACE("Reloading because of color keying\n");
2748 /* To perform the color key conversion we need a sysmem copy of
2749 * the surface. Make sure we have it. */
2751 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2752 /* Make sure the texture is reloaded because of the color key change,
2753 * this kills performance though :( */
2754 /* TODO: This is not necessarily needed with hw palettized texture support. */
2755 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2756 /* Switching color keying on / off may change the internal format. */
2758 surface_force_reload(surface);
2760 else if (!(surface->flags & flag))
2762 TRACE("Reloading because surface is dirty.\n");
2766 TRACE("surface is already in texture\n");
2770 /* No partial locking for textures yet. */
2771 surface_load_location(surface, flag, NULL);
2772 surface_evict_sysmem(surface);
2777 /* See also float_16_to_32() in wined3d_private.h */
2778 static inline unsigned short float_32_to_16(const float *in)
2781 float tmp = fabsf(*in);
2782 unsigned int mantissa;
2785 /* Deal with special numbers */
2791 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2793 if (tmp < powf(2, 10))
2799 } while (tmp < powf(2, 10));
2801 else if (tmp >= powf(2, 11))
2807 } while (tmp >= powf(2, 11));
2810 mantissa = (unsigned int)tmp;
2811 if (tmp - mantissa >= 0.5f)
2812 ++mantissa; /* Round to nearest, away from zero. */
2814 exp += 10; /* Normalize the mantissa. */
2815 exp += 15; /* Exponent is encoded with excess 15. */
2817 if (exp > 30) /* too big */
2819 ret = 0x7c00; /* INF */
2823 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2826 mantissa = mantissa >> 1;
2829 ret = mantissa & 0x3ff;
2833 ret = (exp << 10) | (mantissa & 0x3ff);
2836 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2840 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2844 TRACE("Surface %p, container %p of type %#x.\n",
2845 surface, surface->container.u.base, surface->container.type);
2847 switch (surface->container.type)
2849 case WINED3D_CONTAINER_TEXTURE:
2850 return wined3d_texture_incref(surface->container.u.texture);
2852 case WINED3D_CONTAINER_SWAPCHAIN:
2853 return wined3d_swapchain_incref(surface->container.u.swapchain);
2856 ERR("Unhandled container type %#x.\n", surface->container.type);
2857 case WINED3D_CONTAINER_NONE:
2861 refcount = InterlockedIncrement(&surface->resource.ref);
2862 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2867 /* Do not call while under the GL lock. */
2868 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2872 TRACE("Surface %p, container %p of type %#x.\n",
2873 surface, surface->container.u.base, surface->container.type);
2875 switch (surface->container.type)
2877 case WINED3D_CONTAINER_TEXTURE:
2878 return wined3d_texture_decref(surface->container.u.texture);
2880 case WINED3D_CONTAINER_SWAPCHAIN:
2881 return wined3d_swapchain_decref(surface->container.u.swapchain);
2884 ERR("Unhandled container type %#x.\n", surface->container.type);
2885 case WINED3D_CONTAINER_NONE:
2889 refcount = InterlockedDecrement(&surface->resource.ref);
2890 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2894 surface_cleanup(surface);
2895 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2897 TRACE("Destroyed surface %p.\n", surface);
2898 HeapFree(GetProcessHeap(), 0, surface);
2904 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2906 return resource_set_priority(&surface->resource, priority);
2909 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2911 return resource_get_priority(&surface->resource);
2914 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2916 TRACE("surface %p.\n", surface);
2918 if (!surface->resource.device->d3d_initialized)
2920 ERR("D3D not initialized.\n");
2924 surface_internal_preload(surface, SRGB_ANY);
2927 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2929 TRACE("surface %p.\n", surface);
2931 return surface->resource.parent;
2934 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2936 TRACE("surface %p.\n", surface);
2938 return &surface->resource;
2941 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2943 TRACE("surface %p, flags %#x.\n", surface, flags);
2947 case WINEDDGBS_CANBLT:
2948 case WINEDDGBS_ISBLTDONE:
2952 return WINED3DERR_INVALIDCALL;
2956 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2958 TRACE("surface %p, flags %#x.\n", surface, flags);
2960 /* XXX: DDERR_INVALIDSURFACETYPE */
2964 case WINEDDGFS_CANFLIP:
2965 case WINEDDGFS_ISFLIPDONE:
2969 return WINED3DERR_INVALIDCALL;
2973 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2975 TRACE("surface %p.\n", surface);
2977 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2978 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2981 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2983 TRACE("surface %p.\n", surface);
2985 surface->flags &= ~SFLAG_LOST;
2989 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2991 TRACE("surface %p, palette %p.\n", surface, palette);
2993 if (surface->palette == palette)
2995 TRACE("Nop palette change.\n");
2999 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
3000 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3002 surface->palette = palette;
3006 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3007 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
3009 surface->surface_ops->surface_realize_palette(surface);
3015 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
3016 DWORD flags, const struct wined3d_color_key *color_key)
3018 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3020 if (flags & WINEDDCKEY_COLORSPACE)
3022 FIXME(" colorkey value not supported (%08x) !\n", flags);
3023 return WINED3DERR_INVALIDCALL;
3026 /* Dirtify the surface, but only if a key was changed. */
3029 switch (flags & ~WINEDDCKEY_COLORSPACE)
3031 case WINEDDCKEY_DESTBLT:
3032 surface->dst_blt_color_key = *color_key;
3033 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3036 case WINEDDCKEY_DESTOVERLAY:
3037 surface->dst_overlay_color_key = *color_key;
3038 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3041 case WINEDDCKEY_SRCOVERLAY:
3042 surface->src_overlay_color_key = *color_key;
3043 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3046 case WINEDDCKEY_SRCBLT:
3047 surface->src_blt_color_key = *color_key;
3048 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3054 switch (flags & ~WINEDDCKEY_COLORSPACE)
3056 case WINEDDCKEY_DESTBLT:
3057 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3060 case WINEDDCKEY_DESTOVERLAY:
3061 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3064 case WINEDDCKEY_SRCOVERLAY:
3065 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3068 case WINEDDCKEY_SRCBLT:
3069 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3077 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3079 TRACE("surface %p.\n", surface);
3081 return surface->palette;
3084 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3086 const struct wined3d_format *format = surface->resource.format;
3089 TRACE("surface %p.\n", surface);
3091 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
3093 /* Since compressed formats are block based, pitch means the amount of
3094 * bytes to the next row of block rather than the next row of pixels. */
3095 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3096 pitch = row_block_count * format->block_byte_count;
3100 unsigned char alignment = surface->resource.device->surface_alignment;
3101 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3102 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3105 TRACE("Returning %u.\n", pitch);
3110 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3112 TRACE("surface %p, mem %p.\n", surface, mem);
3114 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3116 WARN("Surface is locked or the DC is in use.\n");
3117 return WINED3DERR_INVALIDCALL;
3120 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
3121 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3123 ERR("Not supported on render targets.\n");
3124 return WINED3DERR_INVALIDCALL;
3127 if (mem && mem != surface->resource.allocatedMemory)
3129 void *release = NULL;
3131 /* Do I have to copy the old surface content? */
3132 if (surface->flags & SFLAG_DIBSECTION)
3134 DeleteDC(surface->hDC);
3135 DeleteObject(surface->dib.DIBsection);
3136 surface->dib.bitmap_data = NULL;
3137 surface->resource.allocatedMemory = NULL;
3138 surface->hDC = NULL;
3139 surface->flags &= ~SFLAG_DIBSECTION;
3141 else if (!(surface->flags & SFLAG_USERPTR))
3143 release = surface->resource.heapMemory;
3144 surface->resource.heapMemory = NULL;
3146 surface->resource.allocatedMemory = mem;
3147 surface->flags |= SFLAG_USERPTR;
3149 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
3150 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3152 /* For client textures OpenGL has to be notified. */
3153 if (surface->flags & SFLAG_CLIENT)
3154 surface_release_client_storage(surface);
3156 /* Now free the old memory if any. */
3157 HeapFree(GetProcessHeap(), 0, release);
3159 else if (surface->flags & SFLAG_USERPTR)
3161 /* HeapMemory should be NULL already. */
3162 if (surface->resource.heapMemory)
3163 ERR("User pointer surface has heap memory allocated.\n");
3167 surface->resource.allocatedMemory = NULL;
3168 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
3170 if (surface->flags & SFLAG_CLIENT)
3171 surface_release_client_storage(surface);
3173 surface_prepare_system_memory(surface);
3176 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3182 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3186 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3188 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3190 WARN("Not an overlay surface.\n");
3191 return WINEDDERR_NOTAOVERLAYSURFACE;
3194 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3195 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3196 surface->overlay_destrect.left = x;
3197 surface->overlay_destrect.top = y;
3198 surface->overlay_destrect.right = x + w;
3199 surface->overlay_destrect.bottom = y + h;
3201 surface_draw_overlay(surface);
3206 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3208 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3210 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3212 TRACE("Not an overlay surface.\n");
3213 return WINEDDERR_NOTAOVERLAYSURFACE;
3216 if (!surface->overlay_dest)
3218 TRACE("Overlay not visible.\n");
3221 return WINEDDERR_OVERLAYNOTVISIBLE;
3224 *x = surface->overlay_destrect.left;
3225 *y = surface->overlay_destrect.top;
3227 TRACE("Returning position %d, %d.\n", *x, *y);
3232 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3233 DWORD flags, struct wined3d_surface *ref)
3235 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3237 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3239 TRACE("Not an overlay surface.\n");
3240 return WINEDDERR_NOTAOVERLAYSURFACE;
3246 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3247 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3249 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3250 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3252 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3254 WARN("Not an overlay surface.\n");
3255 return WINEDDERR_NOTAOVERLAYSURFACE;
3257 else if (!dst_surface)
3259 WARN("Dest surface is NULL.\n");
3260 return WINED3DERR_INVALIDCALL;
3265 surface->overlay_srcrect = *src_rect;
3269 surface->overlay_srcrect.left = 0;
3270 surface->overlay_srcrect.top = 0;
3271 surface->overlay_srcrect.right = surface->resource.width;
3272 surface->overlay_srcrect.bottom = surface->resource.height;
3277 surface->overlay_destrect = *dst_rect;
3281 surface->overlay_destrect.left = 0;
3282 surface->overlay_destrect.top = 0;
3283 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3284 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3287 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3289 surface->overlay_dest = NULL;
3290 list_remove(&surface->overlay_entry);
3293 if (flags & WINEDDOVER_SHOW)
3295 if (surface->overlay_dest != dst_surface)
3297 surface->overlay_dest = dst_surface;
3298 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3301 else if (flags & WINEDDOVER_HIDE)
3303 /* tests show that the rectangles are erased on hide */
3304 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3305 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3306 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3307 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3308 surface->overlay_dest = NULL;
3311 surface_draw_overlay(surface);
3316 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3318 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3320 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3322 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3324 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3325 return WINED3DERR_INVALIDCALL;
3328 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3329 surface->pow2Width, surface->pow2Height);
3330 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3331 surface->resource.format = format;
3333 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3334 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3335 format->glFormat, format->glInternal, format->glType);
3340 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3341 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3343 unsigned short *dst_s;
3347 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3349 for (y = 0; y < h; ++y)
3351 src_f = (const float *)(src + y * pitch_in);
3352 dst_s = (unsigned short *) (dst + y * pitch_out);
3353 for (x = 0; x < w; ++x)
3355 dst_s[x] = float_32_to_16(src_f + x);
3360 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3361 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3363 static const unsigned char convert_5to8[] =
3365 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3366 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3367 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3368 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3370 static const unsigned char convert_6to8[] =
3372 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3373 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3374 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3375 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3376 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3377 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3378 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3379 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3383 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3385 for (y = 0; y < h; ++y)
3387 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3388 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3389 for (x = 0; x < w; ++x)
3391 WORD pixel = src_line[x];
3392 dst_line[x] = 0xff000000
3393 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3394 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3395 | convert_5to8[(pixel & 0x001f)];
3400 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3401 * in both cases we're just setting the X / Alpha channel to 0xff. */
3402 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3403 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3407 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3409 for (y = 0; y < h; ++y)
3411 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3412 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3414 for (x = 0; x < w; ++x)
3416 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3421 static inline BYTE cliptobyte(int x)
3423 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3426 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3427 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3429 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3432 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3434 for (y = 0; y < h; ++y)
3436 const BYTE *src_line = src + y * pitch_in;
3437 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3438 for (x = 0; x < w; ++x)
3440 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3441 * C = Y - 16; D = U - 128; E = V - 128;
3442 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3443 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3444 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3445 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3446 * U and V are shared between the pixels. */
3447 if (!(x & 1)) /* For every even pixel, read new U and V. */
3449 d = (int) src_line[1] - 128;
3450 e = (int) src_line[3] - 128;
3452 g2 = - 100 * d - 208 * e + 128;
3455 c2 = 298 * ((int) src_line[0] - 16);
3456 dst_line[x] = 0xff000000
3457 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3458 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3459 | cliptobyte((c2 + b2) >> 8); /* blue */
3460 /* Scale RGB values to 0..255 range,
3461 * then clip them if still not in range (may be negative),
3462 * then shift them within DWORD if necessary. */
3468 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3469 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3472 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3474 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3476 for (y = 0; y < h; ++y)
3478 const BYTE *src_line = src + y * pitch_in;
3479 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3480 for (x = 0; x < w; ++x)
3482 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3483 * C = Y - 16; D = U - 128; E = V - 128;
3484 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3485 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3486 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3487 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3488 * U and V are shared between the pixels. */
3489 if (!(x & 1)) /* For every even pixel, read new U and V. */
3491 d = (int) src_line[1] - 128;
3492 e = (int) src_line[3] - 128;
3494 g2 = - 100 * d - 208 * e + 128;
3497 c2 = 298 * ((int) src_line[0] - 16);
3498 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3499 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3500 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3501 /* Scale RGB values to 0..255 range,
3502 * then clip them if still not in range (may be negative),
3503 * then shift them within DWORD if necessary. */
3509 struct d3dfmt_convertor_desc
3511 enum wined3d_format_id from, to;
3512 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3515 static const struct d3dfmt_convertor_desc convertors[] =
3517 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3518 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3519 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3520 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3521 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3522 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3525 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3526 enum wined3d_format_id to)
3530 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3532 if (convertors[i].from == from && convertors[i].to == to)
3533 return &convertors[i];
3539 /*****************************************************************************
3540 * surface_convert_format
3542 * Creates a duplicate of a surface in a different format. Is used by Blt to
3543 * blit between surfaces with different formats.
3546 * source: Source surface
3547 * fmt: Requested destination format
3549 *****************************************************************************/
3550 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3552 struct wined3d_mapped_rect src_map, dst_map;
3553 const struct d3dfmt_convertor_desc *conv;
3554 struct wined3d_surface *ret = NULL;
3557 conv = find_convertor(source->resource.format->id, to_fmt);
3560 FIXME("Cannot find a conversion function from format %s to %s.\n",
3561 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3565 wined3d_surface_create(source->resource.device, source->resource.width,
3566 source->resource.height, to_fmt, 0 /* level */, 0 /* usage */, WINED3DPOOL_SCRATCH,
3567 WINED3D_MULTISAMPLE_NONE /* TODO: Multisampled conversion */, 0 /* MultiSampleQuality */,
3568 source->surface_type, WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD,
3569 NULL /* parent */, &wined3d_null_parent_ops, &ret);
3572 ERR("Failed to create a destination surface for conversion.\n");
3576 memset(&src_map, 0, sizeof(src_map));
3577 memset(&dst_map, 0, sizeof(dst_map));
3579 hr = wined3d_surface_map(source, &src_map, NULL, WINED3DLOCK_READONLY);
3582 ERR("Failed to lock the source surface.\n");
3583 wined3d_surface_decref(ret);
3586 hr = wined3d_surface_map(ret, &dst_map, NULL, WINED3DLOCK_READONLY);
3589 ERR("Failed to lock the destination surface.\n");
3590 wined3d_surface_unmap(source);
3591 wined3d_surface_decref(ret);
3595 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
3596 source->resource.width, source->resource.height);
3598 wined3d_surface_unmap(ret);
3599 wined3d_surface_unmap(source);
3604 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3605 unsigned int bpp, UINT pitch, DWORD color)
3612 #define COLORFILL_ROW(type) \
3614 type *d = (type *)buf; \
3615 for (x = 0; x < width; ++x) \
3616 d[x] = (type)color; \
3622 COLORFILL_ROW(BYTE);
3626 COLORFILL_ROW(WORD);
3632 for (x = 0; x < width; ++x, d += 3)
3634 d[0] = (color ) & 0xFF;
3635 d[1] = (color >> 8) & 0xFF;
3636 d[2] = (color >> 16) & 0xFF;
3641 COLORFILL_ROW(DWORD);
3645 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3646 return WINED3DERR_NOTAVAILABLE;
3649 #undef COLORFILL_ROW
3651 /* Now copy first row. */
3653 for (y = 1; y < height; ++y)
3656 memcpy(buf, first, width * bpp);
3662 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3664 TRACE("surface %p.\n", surface);
3666 if (!(surface->flags & SFLAG_LOCKED))
3668 WARN("Trying to unmap unmapped surface.\n");
3669 return WINEDDERR_NOTLOCKED;
3671 surface->flags &= ~SFLAG_LOCKED;
3673 surface->surface_ops->surface_unmap(surface);
3678 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3679 struct wined3d_mapped_rect *mapped_rect, const RECT *rect, DWORD flags)
3681 const struct wined3d_format *format = surface->resource.format;
3683 TRACE("surface %p, mapped_rect %p, rect %s, flags %#x.\n",
3684 surface, mapped_rect, wine_dbgstr_rect(rect), flags);
3686 if (surface->flags & SFLAG_LOCKED)
3688 WARN("Surface is already mapped.\n");
3689 return WINED3DERR_INVALIDCALL;
3691 if ((format->flags & WINED3DFMT_FLAG_BLOCKS)
3692 && rect && (rect->left || rect->top
3693 || rect->right != surface->resource.width
3694 || rect->bottom != surface->resource.height))
3696 UINT width_mask = format->block_width - 1;
3697 UINT height_mask = format->block_height - 1;
3699 if ((rect->left & width_mask) || (rect->right & width_mask)
3700 || (rect->top & height_mask) || (rect->bottom & height_mask))
3702 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3703 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3705 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3706 return WINED3DERR_INVALIDCALL;
3710 surface->flags |= SFLAG_LOCKED;
3712 if (!(surface->flags & SFLAG_LOCKABLE))
3713 WARN("Trying to lock unlockable surface.\n");
3715 /* Performance optimization: Count how often a surface is mapped, if it is
3716 * mapped regularly do not throw away the system memory copy. This avoids
3717 * the need to download the surface from OpenGL all the time. The surface
3718 * is still downloaded if the OpenGL texture is changed. */
3719 if (!(surface->flags & SFLAG_DYNLOCK))
3721 if (++surface->lockCount > MAXLOCKCOUNT)
3723 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3724 surface->flags |= SFLAG_DYNLOCK;
3728 surface->surface_ops->surface_map(surface, rect, flags);
3730 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3731 mapped_rect->row_pitch = surface->resource.width * format->byte_count;
3733 mapped_rect->row_pitch = wined3d_surface_get_pitch(surface);
3737 mapped_rect->data = surface->resource.allocatedMemory;
3738 surface->lockedRect.left = 0;
3739 surface->lockedRect.top = 0;
3740 surface->lockedRect.right = surface->resource.width;
3741 surface->lockedRect.bottom = surface->resource.height;
3745 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3747 /* Compressed textures are block based, so calculate the offset of
3748 * the block that contains the top-left pixel of the locked rectangle. */
3749 mapped_rect->data = surface->resource.allocatedMemory
3750 + ((rect->top / format->block_height) * mapped_rect->row_pitch)
3751 + ((rect->left / format->block_width) * format->block_byte_count);
3755 mapped_rect->data = surface->resource.allocatedMemory
3756 + (mapped_rect->row_pitch * rect->top)
3757 + (rect->left * format->byte_count);
3759 surface->lockedRect.left = rect->left;
3760 surface->lockedRect.top = rect->top;
3761 surface->lockedRect.right = rect->right;
3762 surface->lockedRect.bottom = rect->bottom;
3765 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3766 TRACE("Returning memory %p, pitch %u.\n", mapped_rect->data, mapped_rect->row_pitch);
3771 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3773 struct wined3d_mapped_rect map;
3776 TRACE("surface %p, dc %p.\n", surface, dc);
3778 if (surface->flags & SFLAG_USERPTR)
3780 ERR("Not supported on surfaces with application-provided memory.\n");
3781 return WINEDDERR_NODC;
3784 /* Give more detailed info for ddraw. */
3785 if (surface->flags & SFLAG_DCINUSE)
3786 return WINEDDERR_DCALREADYCREATED;
3788 /* Can't GetDC if the surface is locked. */
3789 if (surface->flags & SFLAG_LOCKED)
3790 return WINED3DERR_INVALIDCALL;
3792 /* Create a DIB section if there isn't a dc yet. */
3795 if (surface->flags & SFLAG_CLIENT)
3797 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3798 surface_release_client_storage(surface);
3800 hr = surface_create_dib_section(surface);
3802 return WINED3DERR_INVALIDCALL;
3804 /* Use the DIB section from now on if we are not using a PBO. */
3805 if (!(surface->flags & SFLAG_PBO))
3806 surface->resource.allocatedMemory = surface->dib.bitmap_data;
3809 /* Map the surface. */
3810 hr = wined3d_surface_map(surface, &map, NULL, 0);
3813 ERR("Map failed, hr %#x.\n", hr);
3817 /* Sync the DIB with the PBO. This can't be done earlier because Map()
3818 * activates the allocatedMemory. */
3819 if (surface->flags & SFLAG_PBO)
3820 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
3822 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3823 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3825 /* GetDC on palettized formats is unsupported in D3D9, and the method
3826 * is missing in D3D8, so this should only be used for DX <=7
3827 * surfaces (with non-device palettes). */
3828 const PALETTEENTRY *pal = NULL;
3830 if (surface->palette)
3832 pal = surface->palette->palents;
3836 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3837 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3839 if (dds_primary && dds_primary->palette)
3840 pal = dds_primary->palette->palents;
3848 for (i = 0; i < 256; ++i)
3850 col[i].rgbRed = pal[i].peRed;
3851 col[i].rgbGreen = pal[i].peGreen;
3852 col[i].rgbBlue = pal[i].peBlue;
3853 col[i].rgbReserved = 0;
3855 SetDIBColorTable(surface->hDC, 0, 256, col);
3859 surface->flags |= SFLAG_DCINUSE;
3862 TRACE("Returning dc %p.\n", *dc);
3867 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3869 TRACE("surface %p, dc %p.\n", surface, dc);
3871 if (!(surface->flags & SFLAG_DCINUSE))
3872 return WINEDDERR_NODC;
3874 if (surface->hDC != dc)
3876 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3878 return WINEDDERR_NODC;
3881 /* Copy the contents of the DIB over to the PBO. */
3882 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3883 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3885 /* We locked first, so unlock now. */
3886 wined3d_surface_unmap(surface);
3888 surface->flags &= ~SFLAG_DCINUSE;
3893 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3895 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3901 FIXME("Ignoring flags %#x.\n", flags);
3903 WARN("Ignoring flags %#x.\n", flags);
3906 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
3908 ERR("Not supported on swapchain surfaces.\n");
3909 return WINEDDERR_NOTFLIPPABLE;
3912 /* Flipping is only supported on render targets and overlays. */
3913 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
3915 WARN("Tried to flip a non-render target, non-overlay surface.\n");
3916 return WINEDDERR_NOTFLIPPABLE;
3919 flip_surface(surface, override);
3921 /* Update overlays if they're visible. */
3922 if ((surface->resource.usage & WINED3DUSAGE_OVERLAY) && surface->overlay_dest)
3923 return surface_draw_overlay(surface);
3928 /* Do not call while under the GL lock. */
3929 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3931 struct wined3d_device *device = surface->resource.device;
3933 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3935 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3937 struct wined3d_texture *texture = surface->container.u.texture;
3939 TRACE("Passing to container (%p).\n", texture);
3940 texture->texture_ops->texture_preload(texture, srgb);
3944 struct wined3d_context *context;
3946 TRACE("(%p) : About to load surface\n", surface);
3948 /* TODO: Use already acquired context when possible. */
3949 context = context_acquire(device, NULL);
3951 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3953 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3955 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3959 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3963 context_release(context);
3967 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3969 if (!surface->resource.allocatedMemory)
3971 if (!surface->resource.heapMemory)
3973 if (!(surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3974 surface->resource.size + RESOURCE_ALIGNMENT)))
3976 ERR("Failed to allocate memory.\n");
3980 else if (!(surface->flags & SFLAG_CLIENT))
3982 ERR("Surface %p has heapMemory %p and flags %#x.\n",
3983 surface, surface->resource.heapMemory, surface->flags);
3986 surface->resource.allocatedMemory =
3987 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
3991 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
3994 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3999 /* Read the framebuffer back into the surface */
4000 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
4002 struct wined3d_device *device = surface->resource.device;
4003 const struct wined3d_gl_info *gl_info;
4004 struct wined3d_context *context;
4008 BYTE *row, *top, *bottom;
4012 BOOL srcIsUpsideDown;
4017 context = context_acquire(device, surface);
4018 context_apply_blit_state(context, device);
4019 gl_info = context->gl_info;
4023 /* Select the correct read buffer, and give some debug output.
4024 * There is no need to keep track of the current read buffer or reset it, every part of the code
4025 * that reads sets the read buffer as desired.
4027 if (surface_is_offscreen(surface))
4029 /* Mapping the primary render target which is not on a swapchain.
4030 * Read from the back buffer. */
4031 TRACE("Mapping offscreen render target.\n");
4032 glReadBuffer(device->offscreenBuffer);
4033 srcIsUpsideDown = TRUE;
4037 /* Onscreen surfaces are always part of a swapchain */
4038 GLenum buffer = surface_get_gl_buffer(surface);
4039 TRACE("Mapping %#x buffer.\n", buffer);
4040 glReadBuffer(buffer);
4041 checkGLcall("glReadBuffer");
4042 srcIsUpsideDown = FALSE;
4045 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
4048 local_rect.left = 0;
4050 local_rect.right = surface->resource.width;
4051 local_rect.bottom = surface->resource.height;
4057 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
4059 switch (surface->resource.format->id)
4061 case WINED3DFMT_P8_UINT:
4063 if (primary_render_target_is_p8(device))
4065 /* In case of P8 render targets the index is stored in the alpha component */
4067 type = GL_UNSIGNED_BYTE;
4069 bpp = surface->resource.format->byte_count;
4073 /* GL can't return palettized data, so read ARGB pixels into a
4074 * separate block of memory and convert them into palettized format
4075 * in software. Slow, but if the app means to use palettized render
4076 * targets and locks it...
4078 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4079 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4080 * for the color channels when palettizing the colors.
4083 type = GL_UNSIGNED_BYTE;
4085 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4088 ERR("Out of memory\n");
4092 bpp = surface->resource.format->byte_count * 3;
4099 fmt = surface->resource.format->glFormat;
4100 type = surface->resource.format->glType;
4101 bpp = surface->resource.format->byte_count;
4104 if (surface->flags & SFLAG_PBO)
4106 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4107 checkGLcall("glBindBufferARB");
4110 ERR("mem not null for pbo -- unexpected\n");
4115 /* Save old pixel store pack state */
4116 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4117 checkGLcall("glGetIntegerv");
4118 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4119 checkGLcall("glGetIntegerv");
4120 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4121 checkGLcall("glGetIntegerv");
4123 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4124 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4125 checkGLcall("glPixelStorei");
4126 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4127 checkGLcall("glPixelStorei");
4128 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4129 checkGLcall("glPixelStorei");
4131 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4132 local_rect.right - local_rect.left,
4133 local_rect.bottom - local_rect.top,
4135 checkGLcall("glReadPixels");
4137 /* Reset previous pixel store pack state */
4138 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4139 checkGLcall("glPixelStorei");
4140 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4141 checkGLcall("glPixelStorei");
4142 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4143 checkGLcall("glPixelStorei");
4145 if (surface->flags & SFLAG_PBO)
4147 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4148 checkGLcall("glBindBufferARB");
4150 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4151 * to get a pointer to it and perform the flipping in software. This is a lot
4152 * faster than calling glReadPixels for each line. In case we want more speed
4153 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4154 if (!srcIsUpsideDown)
4156 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4157 checkGLcall("glBindBufferARB");
4159 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4160 checkGLcall("glMapBufferARB");
4164 /* TODO: Merge this with the palettization loop below for P8 targets */
4165 if(!srcIsUpsideDown) {
4167 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4168 Flip the lines in software */
4169 len = (local_rect.right - local_rect.left) * bpp;
4170 off = local_rect.left * bpp;
4172 row = HeapAlloc(GetProcessHeap(), 0, len);
4174 ERR("Out of memory\n");
4175 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4176 HeapFree(GetProcessHeap(), 0, mem);
4181 top = mem + pitch * local_rect.top;
4182 bottom = mem + pitch * (local_rect.bottom - 1);
4183 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4184 memcpy(row, top + off, len);
4185 memcpy(top + off, bottom + off, len);
4186 memcpy(bottom + off, row, len);
4190 HeapFree(GetProcessHeap(), 0, row);
4192 /* Unmap the temp PBO buffer */
4193 if (surface->flags & SFLAG_PBO)
4195 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4196 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4201 context_release(context);
4203 /* For P8 textures we need to perform an inverse palette lookup. This is
4204 * done by searching for a palette index which matches the RGB value.
4205 * Note this isn't guaranteed to work when there are multiple entries for
4206 * the same color but we have no choice. In case of P8 render targets,
4207 * the index is stored in the alpha component so no conversion is needed. */
4208 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4210 const PALETTEENTRY *pal = NULL;
4211 DWORD width = pitch / 3;
4214 if (surface->palette)
4216 pal = surface->palette->palents;
4220 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4221 HeapFree(GetProcessHeap(), 0, mem);
4225 for(y = local_rect.top; y < local_rect.bottom; y++) {
4226 for(x = local_rect.left; x < local_rect.right; x++) {
4227 /* start lines pixels */
4228 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4229 const BYTE *green = blue + 1;
4230 const BYTE *red = green + 1;
4232 for(c = 0; c < 256; c++) {
4233 if(*red == pal[c].peRed &&
4234 *green == pal[c].peGreen &&
4235 *blue == pal[c].peBlue)
4237 *((BYTE *) dest + y * width + x) = c;
4243 HeapFree(GetProcessHeap(), 0, mem);
4247 /* Read the framebuffer contents into a texture. Note that this function
4248 * doesn't do any kind of flipping. Using this on an onscreen surface will
4249 * result in a flipped D3D texture. */
4250 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
4252 struct wined3d_device *device = surface->resource.device;
4253 struct wined3d_context *context;
4255 context = context_acquire(device, surface);
4256 device_invalidate_state(device, STATE_FRAMEBUFFER);
4258 surface_prepare_texture(surface, context, srgb);
4259 surface_bind_and_dirtify(surface, context, srgb);
4261 TRACE("Reading back offscreen render target %p.\n", surface);
4265 if (surface_is_offscreen(surface))
4266 glReadBuffer(device->offscreenBuffer);
4268 glReadBuffer(surface_get_gl_buffer(surface));
4269 checkGLcall("glReadBuffer");
4271 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4272 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4273 checkGLcall("glCopyTexSubImage2D");
4277 context_release(context);
4280 /* Context activation is done by the caller. */
4281 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4282 struct wined3d_context *context, BOOL srgb)
4284 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4285 CONVERT_TYPES convert;
4286 struct wined3d_format format;
4288 if (surface->flags & alloc_flag) return;
4290 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4291 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4292 else surface->flags &= ~SFLAG_CONVERTED;
4294 surface_bind_and_dirtify(surface, context, srgb);
4295 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4296 surface->flags |= alloc_flag;
4299 /* Context activation is done by the caller. */
4300 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4302 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4304 struct wined3d_texture *texture = surface->container.u.texture;
4305 UINT sub_count = texture->level_count * texture->layer_count;
4308 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4310 for (i = 0; i < sub_count; ++i)
4312 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4313 surface_prepare_texture_internal(s, context, srgb);
4319 surface_prepare_texture_internal(surface, context, srgb);
4322 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4326 if (surface->rb_multisample)
4329 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4330 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4331 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4332 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4333 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4337 if (surface->rb_resolved)
4340 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4341 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4342 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4343 surface->pow2Width, surface->pow2Height);
4344 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4348 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4349 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4351 struct wined3d_device *device = surface->resource.device;
4352 UINT pitch = wined3d_surface_get_pitch(surface);
4353 const struct wined3d_gl_info *gl_info;
4354 struct wined3d_context *context;
4358 surface_get_rect(surface, rect, &local_rect);
4360 mem += local_rect.top * pitch + local_rect.left * bpp;
4361 w = local_rect.right - local_rect.left;
4362 h = local_rect.bottom - local_rect.top;
4364 /* Activate the correct context for the render target */
4365 context = context_acquire(device, surface);
4366 context_apply_blit_state(context, device);
4367 gl_info = context->gl_info;
4371 if (!surface_is_offscreen(surface))
4373 GLenum buffer = surface_get_gl_buffer(surface);
4374 TRACE("Unlocking %#x buffer.\n", buffer);
4375 context_set_draw_buffer(context, buffer);
4377 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4378 glPixelZoom(1.0f, -1.0f);
4382 /* Primary offscreen render target */
4383 TRACE("Offscreen render target.\n");
4384 context_set_draw_buffer(context, device->offscreenBuffer);
4386 glPixelZoom(1.0f, 1.0f);
4389 glRasterPos3i(local_rect.left, local_rect.top, 1);
4390 checkGLcall("glRasterPos3i");
4392 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4393 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4395 if (surface->flags & SFLAG_PBO)
4397 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4398 checkGLcall("glBindBufferARB");
4401 glDrawPixels(w, h, fmt, type, mem);
4402 checkGLcall("glDrawPixels");
4404 if (surface->flags & SFLAG_PBO)
4406 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4407 checkGLcall("glBindBufferARB");
4410 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4411 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4415 if (wined3d_settings.strict_draw_ordering
4416 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4417 && surface->container.u.swapchain->front_buffer == surface))
4420 context_release(context);
4423 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4424 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4426 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4427 const struct wined3d_device *device = surface->resource.device;
4428 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4429 BOOL blit_supported = FALSE;
4431 /* Copy the default values from the surface. Below we might perform fixups */
4432 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4433 *format = *surface->resource.format;
4434 *convert = NO_CONVERSION;
4436 /* Ok, now look if we have to do any conversion */
4437 switch (surface->resource.format->id)
4439 case WINED3DFMT_P8_UINT:
4440 /* Below the call to blit_supported is disabled for Wine 1.2
4441 * because the function isn't operating correctly yet. At the
4442 * moment 8-bit blits are handled in software and if certain GL
4443 * extensions are around, surface conversion is performed at
4444 * upload time. The blit_supported call recognizes it as a
4445 * destination fixup. This type of upload 'fixup' and 8-bit to
4446 * 8-bit blits need to be handled by the blit_shader.
4447 * TODO: get rid of this #if 0. */
4449 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4450 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4451 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4453 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4455 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4456 * texturing. Further also use conversion in case of color keying.
4457 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4458 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4459 * conflicts with this.
4461 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4462 || colorkey_active || !use_texturing)
4464 format->glFormat = GL_RGBA;
4465 format->glInternal = GL_RGBA;
4466 format->glType = GL_UNSIGNED_BYTE;
4467 format->conv_byte_count = 4;
4468 if (colorkey_active)
4469 *convert = CONVERT_PALETTED_CK;
4471 *convert = CONVERT_PALETTED;
4475 case WINED3DFMT_B2G3R3_UNORM:
4476 /* **********************
4477 GL_UNSIGNED_BYTE_3_3_2
4478 ********************** */
4479 if (colorkey_active) {
4480 /* This texture format will never be used.. So do not care about color keying
4481 up until the point in time it will be needed :-) */
4482 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4486 case WINED3DFMT_B5G6R5_UNORM:
4487 if (colorkey_active)
4489 *convert = CONVERT_CK_565;
4490 format->glFormat = GL_RGBA;
4491 format->glInternal = GL_RGB5_A1;
4492 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4493 format->conv_byte_count = 2;
4497 case WINED3DFMT_B5G5R5X1_UNORM:
4498 if (colorkey_active)
4500 *convert = CONVERT_CK_5551;
4501 format->glFormat = GL_BGRA;
4502 format->glInternal = GL_RGB5_A1;
4503 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4504 format->conv_byte_count = 2;
4508 case WINED3DFMT_B8G8R8_UNORM:
4509 if (colorkey_active)
4511 *convert = CONVERT_CK_RGB24;
4512 format->glFormat = GL_RGBA;
4513 format->glInternal = GL_RGBA8;
4514 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4515 format->conv_byte_count = 4;
4519 case WINED3DFMT_B8G8R8X8_UNORM:
4520 if (colorkey_active)
4522 *convert = CONVERT_RGB32_888;
4523 format->glFormat = GL_RGBA;
4524 format->glInternal = GL_RGBA8;
4525 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4526 format->conv_byte_count = 4;
4537 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
4539 /* FIXME: Is this really how color keys are supposed to work? I think it
4540 * makes more sense to compare the individual channels. */
4541 return color >= color_key->color_space_low_value
4542 && color <= color_key->color_space_high_value;
4545 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4547 const struct wined3d_device *device = surface->resource.device;
4548 const struct wined3d_palette *pal = surface->palette;
4549 BOOL index_in_alpha = FALSE;
4552 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4553 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4554 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4555 * duplicate entries. Store the color key in the unused alpha component to speed the
4556 * download up and to make conversion unneeded. */
4557 index_in_alpha = primary_render_target_is_p8(device);
4561 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4564 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4565 * there's no palette at this time. */
4566 for (i = 0; i < 256; i++) table[i][3] = i;
4571 TRACE("Using surface palette %p\n", pal);
4572 /* Get the surface's palette */
4573 for (i = 0; i < 256; ++i)
4575 table[i][0] = pal->palents[i].peRed;
4576 table[i][1] = pal->palents[i].peGreen;
4577 table[i][2] = pal->palents[i].peBlue;
4579 /* When index_in_alpha is set the palette index is stored in the
4580 * alpha component. In case of a readback we can then read
4581 * GL_ALPHA. Color keying is handled in BltOverride using a
4582 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4583 * color key itself is passed to glAlphaFunc in other cases the
4584 * alpha component of pixels that should be masked away is set to 0. */
4587 else if (colorkey && color_in_range(&surface->src_blt_color_key, i))
4589 else if (pal->flags & WINEDDPCAPS_ALPHA)
4590 table[i][3] = pal->palents[i].peFlags;
4597 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4598 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4602 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4607 memcpy(dst, src, pitch * height);
4610 case CONVERT_PALETTED:
4611 case CONVERT_PALETTED_CK:
4616 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4618 for (y = 0; y < height; y++)
4620 source = src + pitch * y;
4621 dest = dst + outpitch * y;
4622 /* This is an 1 bpp format, using the width here is fine */
4623 for (x = 0; x < width; x++) {
4624 BYTE color = *source++;
4625 *dest++ = table[color][0];
4626 *dest++ = table[color][1];
4627 *dest++ = table[color][2];
4628 *dest++ = table[color][3];
4634 case CONVERT_CK_565:
4636 /* Converting the 565 format in 5551 packed to emulate color-keying.
4638 Note : in all these conversion, it would be best to average the averaging
4639 pixels to get the color of the pixel that will be color-keyed to
4640 prevent 'color bleeding'. This will be done later on if ever it is
4643 Note2: Nvidia documents say that their driver does not support alpha + color keying
4644 on the same surface and disables color keying in such a case
4650 TRACE("Color keyed 565\n");
4652 for (y = 0; y < height; y++) {
4653 Source = (const WORD *)(src + y * pitch);
4654 Dest = (WORD *) (dst + y * outpitch);
4655 for (x = 0; x < width; x++ ) {
4656 WORD color = *Source++;
4657 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4658 if (!color_in_range(&surface->src_blt_color_key, color))
4666 case CONVERT_CK_5551:
4668 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4672 TRACE("Color keyed 5551\n");
4673 for (y = 0; y < height; y++) {
4674 Source = (const WORD *)(src + y * pitch);
4675 Dest = (WORD *) (dst + y * outpitch);
4676 for (x = 0; x < width; x++ ) {
4677 WORD color = *Source++;
4679 if (!color_in_range(&surface->src_blt_color_key, color))
4682 *Dest &= ~(1 << 15);
4689 case CONVERT_CK_RGB24:
4691 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4693 for (y = 0; y < height; y++)
4695 source = src + pitch * y;
4696 dest = dst + outpitch * y;
4697 for (x = 0; x < width; x++) {
4698 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4699 DWORD dstcolor = color << 8;
4700 if (!color_in_range(&surface->src_blt_color_key, color))
4702 *(DWORD*)dest = dstcolor;
4710 case CONVERT_RGB32_888:
4712 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4714 for (y = 0; y < height; y++)
4716 source = src + pitch * y;
4717 dest = dst + outpitch * y;
4718 for (x = 0; x < width; x++) {
4719 DWORD color = 0xffffff & *(const DWORD*)source;
4720 DWORD dstcolor = color << 8;
4721 if (!color_in_range(&surface->src_blt_color_key, color))
4723 *(DWORD*)dest = dstcolor;
4732 ERR("Unsupported conversion type %#x.\n", convert);
4737 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4739 /* Flip the surface contents */
4744 front->hDC = back->hDC;
4748 /* Flip the DIBsection */
4750 HBITMAP tmp = front->dib.DIBsection;
4751 front->dib.DIBsection = back->dib.DIBsection;
4752 back->dib.DIBsection = tmp;
4755 /* Flip the surface data */
4759 tmp = front->dib.bitmap_data;
4760 front->dib.bitmap_data = back->dib.bitmap_data;
4761 back->dib.bitmap_data = tmp;
4763 tmp = front->resource.allocatedMemory;
4764 front->resource.allocatedMemory = back->resource.allocatedMemory;
4765 back->resource.allocatedMemory = tmp;
4767 tmp = front->resource.heapMemory;
4768 front->resource.heapMemory = back->resource.heapMemory;
4769 back->resource.heapMemory = tmp;
4774 GLuint tmp_pbo = front->pbo;
4775 front->pbo = back->pbo;
4776 back->pbo = tmp_pbo;
4779 /* Flip the opengl texture */
4783 tmp = back->texture_name;
4784 back->texture_name = front->texture_name;
4785 front->texture_name = tmp;
4787 tmp = back->texture_name_srgb;
4788 back->texture_name_srgb = front->texture_name_srgb;
4789 front->texture_name_srgb = tmp;
4791 tmp = back->rb_multisample;
4792 back->rb_multisample = front->rb_multisample;
4793 front->rb_multisample = tmp;
4795 tmp = back->rb_resolved;
4796 back->rb_resolved = front->rb_resolved;
4797 front->rb_resolved = tmp;
4799 resource_unload(&back->resource);
4800 resource_unload(&front->resource);
4804 DWORD tmp_flags = back->flags;
4805 back->flags = front->flags;
4806 front->flags = tmp_flags;
4810 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4811 * pixel copy calls. */
4812 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4813 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4815 struct wined3d_device *device = dst_surface->resource.device;
4818 struct wined3d_context *context;
4819 BOOL upsidedown = FALSE;
4820 RECT dst_rect = *dst_rect_in;
4822 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4823 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4825 if(dst_rect.top > dst_rect.bottom) {
4826 UINT tmp = dst_rect.bottom;
4827 dst_rect.bottom = dst_rect.top;
4832 context = context_acquire(device, src_surface);
4833 context_apply_blit_state(context, device);
4834 surface_internal_preload(dst_surface, SRGB_RGB);
4837 /* Bind the target texture */
4838 context_bind_texture(context, dst_surface->texture_target, dst_surface->texture_name);
4839 if (surface_is_offscreen(src_surface))
4841 TRACE("Reading from an offscreen target\n");
4842 upsidedown = !upsidedown;
4843 glReadBuffer(device->offscreenBuffer);
4847 glReadBuffer(surface_get_gl_buffer(src_surface));
4849 checkGLcall("glReadBuffer");
4851 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4852 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4854 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4856 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4858 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4859 ERR("Texture filtering not supported in direct blit\n");
4862 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4863 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4865 ERR("Texture filtering not supported in direct blit\n");
4869 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4870 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4872 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4874 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4875 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4876 src_rect->left, src_surface->resource.height - src_rect->bottom,
4877 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4881 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4882 /* I have to process this row by row to swap the image,
4883 * otherwise it would be upside down, so stretching in y direction
4884 * doesn't cost extra time
4886 * However, stretching in x direction can be avoided if not necessary
4888 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4889 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4891 /* Well, that stuff works, but it's very slow.
4892 * find a better way instead
4896 for (col = dst_rect.left; col < dst_rect.right; ++col)
4898 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4899 dst_rect.left + col /* x offset */, row /* y offset */,
4900 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4905 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4906 dst_rect.left /* x offset */, row /* y offset */,
4907 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4911 checkGLcall("glCopyTexSubImage2D");
4914 context_release(context);
4916 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4917 * path is never entered
4919 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4922 /* Uses the hardware to stretch and flip the image */
4923 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4924 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4926 struct wined3d_device *device = dst_surface->resource.device;
4927 struct wined3d_swapchain *src_swapchain = NULL;
4928 GLuint src, backup = 0;
4929 float left, right, top, bottom; /* Texture coordinates */
4930 UINT fbwidth = src_surface->resource.width;
4931 UINT fbheight = src_surface->resource.height;
4932 struct wined3d_context *context;
4933 GLenum drawBuffer = GL_BACK;
4934 GLenum texture_target;
4935 BOOL noBackBufferBackup;
4937 BOOL upsidedown = FALSE;
4938 RECT dst_rect = *dst_rect_in;
4940 TRACE("Using hwstretch blit\n");
4941 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4942 context = context_acquire(device, src_surface);
4943 context_apply_blit_state(context, device);
4944 surface_internal_preload(dst_surface, SRGB_RGB);
4946 src_offscreen = surface_is_offscreen(src_surface);
4947 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4948 if (!noBackBufferBackup && !src_surface->texture_name)
4950 /* Get it a description */
4951 surface_internal_preload(src_surface, SRGB_RGB);
4955 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4956 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4958 if (context->aux_buffers >= 2)
4960 /* Got more than one aux buffer? Use the 2nd aux buffer */
4961 drawBuffer = GL_AUX1;
4963 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4965 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4966 drawBuffer = GL_AUX0;
4969 if(noBackBufferBackup) {
4970 glGenTextures(1, &backup);
4971 checkGLcall("glGenTextures");
4972 context_bind_texture(context, GL_TEXTURE_2D, backup);
4973 texture_target = GL_TEXTURE_2D;
4975 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4976 * we are reading from the back buffer, the backup can be used as source texture
4978 texture_target = src_surface->texture_target;
4979 context_bind_texture(context, texture_target, src_surface->texture_name);
4980 glEnable(texture_target);
4981 checkGLcall("glEnable(texture_target)");
4983 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4984 src_surface->flags &= ~SFLAG_INTEXTURE;
4987 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4988 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4990 if(dst_rect.top > dst_rect.bottom) {
4991 UINT tmp = dst_rect.bottom;
4992 dst_rect.bottom = dst_rect.top;
4999 TRACE("Reading from an offscreen target\n");
5000 upsidedown = !upsidedown;
5001 glReadBuffer(device->offscreenBuffer);
5005 glReadBuffer(surface_get_gl_buffer(src_surface));
5008 /* TODO: Only back up the part that will be overwritten */
5009 glCopyTexSubImage2D(texture_target, 0,
5010 0, 0 /* read offsets */,
5015 checkGLcall("glCopyTexSubImage2D");
5017 /* No issue with overriding these - the sampler is dirty due to blit usage */
5018 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
5019 wined3d_gl_mag_filter(magLookup, Filter));
5020 checkGLcall("glTexParameteri");
5021 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5022 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
5023 checkGLcall("glTexParameteri");
5025 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5026 src_swapchain = src_surface->container.u.swapchain;
5027 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5029 src = backup ? backup : src_surface->texture_name;
5033 glReadBuffer(GL_FRONT);
5034 checkGLcall("glReadBuffer(GL_FRONT)");
5036 glGenTextures(1, &src);
5037 checkGLcall("glGenTextures(1, &src)");
5038 context_bind_texture(context, GL_TEXTURE_2D, src);
5040 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5041 * out for power of 2 sizes
5043 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5044 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5045 checkGLcall("glTexImage2D");
5046 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5047 0, 0 /* read offsets */,
5052 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5053 checkGLcall("glTexParameteri");
5054 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5055 checkGLcall("glTexParameteri");
5057 glReadBuffer(GL_BACK);
5058 checkGLcall("glReadBuffer(GL_BACK)");
5060 if(texture_target != GL_TEXTURE_2D) {
5061 glDisable(texture_target);
5062 glEnable(GL_TEXTURE_2D);
5063 texture_target = GL_TEXTURE_2D;
5066 checkGLcall("glEnd and previous");
5068 left = src_rect->left;
5069 right = src_rect->right;
5073 top = src_surface->resource.height - src_rect->top;
5074 bottom = src_surface->resource.height - src_rect->bottom;
5078 top = src_surface->resource.height - src_rect->bottom;
5079 bottom = src_surface->resource.height - src_rect->top;
5082 if (src_surface->flags & SFLAG_NORMCOORD)
5084 left /= src_surface->pow2Width;
5085 right /= src_surface->pow2Width;
5086 top /= src_surface->pow2Height;
5087 bottom /= src_surface->pow2Height;
5090 /* draw the source texture stretched and upside down. The correct surface is bound already */
5091 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5092 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5094 context_set_draw_buffer(context, drawBuffer);
5095 glReadBuffer(drawBuffer);
5099 glTexCoord2f(left, bottom);
5103 glTexCoord2f(left, top);
5104 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5107 glTexCoord2f(right, top);
5108 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5111 glTexCoord2f(right, bottom);
5112 glVertex2i(dst_rect.right - dst_rect.left, 0);
5114 checkGLcall("glEnd and previous");
5116 if (texture_target != dst_surface->texture_target)
5118 glDisable(texture_target);
5119 glEnable(dst_surface->texture_target);
5120 texture_target = dst_surface->texture_target;
5123 /* Now read the stretched and upside down image into the destination texture */
5124 context_bind_texture(context, texture_target, dst_surface->texture_name);
5125 glCopyTexSubImage2D(texture_target,
5127 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5128 0, 0, /* We blitted the image to the origin */
5129 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5130 checkGLcall("glCopyTexSubImage2D");
5132 if(drawBuffer == GL_BACK) {
5133 /* Write the back buffer backup back */
5135 if(texture_target != GL_TEXTURE_2D) {
5136 glDisable(texture_target);
5137 glEnable(GL_TEXTURE_2D);
5138 texture_target = GL_TEXTURE_2D;
5140 context_bind_texture(context, GL_TEXTURE_2D, backup);
5144 if (texture_target != src_surface->texture_target)
5146 glDisable(texture_target);
5147 glEnable(src_surface->texture_target);
5148 texture_target = src_surface->texture_target;
5150 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5155 glTexCoord2f(0.0f, 0.0f);
5156 glVertex2i(0, fbheight);
5159 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5163 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5164 (float)fbheight / (float)src_surface->pow2Height);
5165 glVertex2i(fbwidth, 0);
5168 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5169 glVertex2i(fbwidth, fbheight);
5172 glDisable(texture_target);
5173 checkGLcall("glDisable(texture_target)");
5176 if (src != src_surface->texture_name && src != backup)
5178 glDeleteTextures(1, &src);
5179 checkGLcall("glDeleteTextures(1, &src)");
5182 glDeleteTextures(1, &backup);
5183 checkGLcall("glDeleteTextures(1, &backup)");
5188 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5190 context_release(context);
5192 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5193 * path is never entered
5195 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5198 /* Front buffer coordinates are always full screen coordinates, but our GL
5199 * drawable is limited to the window's client area. The sysmem and texture
5200 * copies do have the full screen size. Note that GL has a bottom-left
5201 * origin, while D3D has a top-left origin. */
5202 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5204 UINT drawable_height;
5206 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5207 && surface == surface->container.u.swapchain->front_buffer)
5209 POINT offset = {0, 0};
5212 ScreenToClient(window, &offset);
5213 OffsetRect(rect, offset.x, offset.y);
5215 GetClientRect(window, &windowsize);
5216 drawable_height = windowsize.bottom - windowsize.top;
5220 drawable_height = surface->resource.height;
5223 rect->top = drawable_height - rect->top;
5224 rect->bottom = drawable_height - rect->bottom;
5227 static void surface_blt_to_drawable(const struct wined3d_device *device,
5228 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5229 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5230 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5232 struct wined3d_context *context;
5233 RECT src_rect, dst_rect;
5235 src_rect = *src_rect_in;
5236 dst_rect = *dst_rect_in;
5238 /* Make sure the surface is up-to-date. This should probably use
5239 * surface_load_location() and worry about the destination surface too,
5240 * unless we're overwriting it completely. */
5241 surface_internal_preload(src_surface, SRGB_RGB);
5243 /* Activate the destination context, set it up for blitting */
5244 context = context_acquire(device, dst_surface);
5245 context_apply_blit_state(context, device);
5247 if (!surface_is_offscreen(dst_surface))
5248 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5250 device->blitter->set_shader(device->blit_priv, context, src_surface);
5256 glEnable(GL_ALPHA_TEST);
5257 checkGLcall("glEnable(GL_ALPHA_TEST)");
5259 /* When the primary render target uses P8, the alpha component
5260 * contains the palette index. Which means that the colorkey is one of
5261 * the palette entries. In other cases pixels that should be masked
5262 * away have alpha set to 0. */
5263 if (primary_render_target_is_p8(device))
5264 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->src_blt_color_key.color_space_low_value / 256.0f);
5266 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5267 checkGLcall("glAlphaFunc");
5271 glDisable(GL_ALPHA_TEST);
5272 checkGLcall("glDisable(GL_ALPHA_TEST)");
5275 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5279 glDisable(GL_ALPHA_TEST);
5280 checkGLcall("glDisable(GL_ALPHA_TEST)");
5285 /* Leave the opengl state valid for blitting */
5286 device->blitter->unset_shader(context->gl_info);
5288 if (wined3d_settings.strict_draw_ordering
5289 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5290 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5291 wglFlush(); /* Flush to ensure ordering across contexts. */
5293 context_release(context);
5296 /* Do not call while under the GL lock. */
5297 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
5299 struct wined3d_device *device = s->resource.device;
5300 const struct blit_shader *blitter;
5302 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5303 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5306 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5307 return WINED3DERR_INVALIDCALL;
5310 return blitter->color_fill(device, s, rect, color);
5313 /* Do not call while under the GL lock. */
5314 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5315 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5316 WINED3DTEXTUREFILTERTYPE Filter)
5318 struct wined3d_device *device = dst_surface->resource.device;
5319 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5320 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5322 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5323 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5324 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5326 /* Get the swapchain. One of the surfaces has to be a primary surface */
5327 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5329 WARN("Destination is in sysmem, rejecting gl blt\n");
5330 return WINED3DERR_INVALIDCALL;
5333 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5334 dstSwapchain = dst_surface->container.u.swapchain;
5338 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5340 WARN("Src is in sysmem, rejecting gl blt\n");
5341 return WINED3DERR_INVALIDCALL;
5344 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5345 srcSwapchain = src_surface->container.u.swapchain;
5348 /* Early sort out of cases where no render target is used */
5349 if (!dstSwapchain && !srcSwapchain
5350 && src_surface != device->fb.render_targets[0]
5351 && dst_surface != device->fb.render_targets[0])
5353 TRACE("No surface is render target, not using hardware blit.\n");
5354 return WINED3DERR_INVALIDCALL;
5357 /* No destination color keying supported */
5358 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5360 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5361 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5362 return WINED3DERR_INVALIDCALL;
5365 if (dstSwapchain && dstSwapchain == srcSwapchain)
5367 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5368 return WINED3DERR_INVALIDCALL;
5371 if (dstSwapchain && srcSwapchain)
5373 FIXME("Implement hardware blit between two different swapchains\n");
5374 return WINED3DERR_INVALIDCALL;
5379 /* Handled with regular texture -> swapchain blit */
5380 if (src_surface == device->fb.render_targets[0])
5381 TRACE("Blit from active render target to a swapchain\n");
5383 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5385 FIXME("Implement blit from a swapchain to the active render target\n");
5386 return WINED3DERR_INVALIDCALL;
5389 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5391 /* Blit from render target to texture */
5394 /* P8 read back is not implemented */
5395 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5396 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5398 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5399 return WINED3DERR_INVALIDCALL;
5402 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5404 TRACE("Color keying not supported by frame buffer to texture blit\n");
5405 return WINED3DERR_INVALIDCALL;
5406 /* Destination color key is checked above */
5409 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5414 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5415 * flip the image nor scale it.
5417 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5418 * -> If the app wants a image width an unscaled width, copy it line per line
5419 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5420 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5421 * back buffer. This is slower than reading line per line, thus not used for flipping
5422 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5423 * pixel by pixel. */
5424 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5425 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5427 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5428 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, Filter);
5430 TRACE("Using hardware stretching to flip / stretch the texture\n");
5431 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5434 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5436 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5437 dst_surface->resource.allocatedMemory = NULL;
5438 dst_surface->resource.heapMemory = NULL;
5442 dst_surface->flags &= ~SFLAG_INSYSMEM;
5447 else if (src_surface)
5449 /* Blit from offscreen surface to render target */
5450 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
5451 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5453 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5455 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5456 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5457 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5459 FIXME("Unsupported blit operation falling back to software\n");
5460 return WINED3DERR_INVALIDCALL;
5463 /* Color keying: Check if we have to do a color keyed blt,
5464 * and if not check if a color key is activated.
5466 * Just modify the color keying parameters in the surface and restore them afterwards
5467 * The surface keeps track of the color key last used to load the opengl surface.
5468 * PreLoad will catch the change to the flags and color key and reload if necessary.
5470 if (flags & WINEDDBLT_KEYSRC)
5472 /* Use color key from surface */
5474 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5476 /* Use color key from DDBltFx */
5477 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5478 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
5482 /* Do not use color key */
5483 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5486 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5487 src_surface, src_rect, dst_surface, dst_rect);
5489 /* Restore the color key parameters */
5490 src_surface->CKeyFlags = oldCKeyFlags;
5491 src_surface->src_blt_color_key = old_blt_key;
5493 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5498 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5499 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5500 return WINED3DERR_INVALIDCALL;
5503 /* GL locking is done by the caller */
5504 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5505 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5507 struct wined3d_device *device = surface->resource.device;
5508 const struct wined3d_gl_info *gl_info = context->gl_info;
5509 GLint compare_mode = GL_NONE;
5510 struct blt_info info;
5511 GLint old_binding = 0;
5514 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5516 glDisable(GL_CULL_FACE);
5517 glDisable(GL_BLEND);
5518 glDisable(GL_ALPHA_TEST);
5519 glDisable(GL_SCISSOR_TEST);
5520 glDisable(GL_STENCIL_TEST);
5521 glEnable(GL_DEPTH_TEST);
5522 glDepthFunc(GL_ALWAYS);
5523 glDepthMask(GL_TRUE);
5524 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5525 glViewport(x, y, w, h);
5527 SetRect(&rect, 0, h, w, 0);
5528 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5529 context_active_texture(context, context->gl_info, 0);
5530 glGetIntegerv(info.binding, &old_binding);
5531 glBindTexture(info.bind_target, texture);
5532 if (gl_info->supported[ARB_SHADOW])
5534 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5535 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5538 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5539 gl_info, info.tex_type, &surface->ds_current_size);
5541 glBegin(GL_TRIANGLE_STRIP);
5542 glTexCoord3fv(info.coords[0]);
5543 glVertex2f(-1.0f, -1.0f);
5544 glTexCoord3fv(info.coords[1]);
5545 glVertex2f(1.0f, -1.0f);
5546 glTexCoord3fv(info.coords[2]);
5547 glVertex2f(-1.0f, 1.0f);
5548 glTexCoord3fv(info.coords[3]);
5549 glVertex2f(1.0f, 1.0f);
5552 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5553 glBindTexture(info.bind_target, old_binding);
5557 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5560 void surface_modify_ds_location(struct wined3d_surface *surface,
5561 DWORD location, UINT w, UINT h)
5563 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5565 if (location & ~SFLAG_LOCATIONS)
5566 FIXME("Invalid location (%#x) specified.\n", location);
5568 if (!(surface->flags & SFLAG_ALLOCATED))
5569 location &= ~SFLAG_INTEXTURE;
5570 if (!(surface->rb_resolved))
5571 location &= ~SFLAG_INRB_RESOLVED;
5572 if (!(surface->rb_multisample))
5573 location &= ~SFLAG_INRB_MULTISAMPLE;
5575 surface->ds_current_size.cx = w;
5576 surface->ds_current_size.cy = h;
5577 surface->flags &= ~SFLAG_LOCATIONS;
5578 surface->flags |= location;
5581 /* Context activation is done by the caller. */
5582 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5584 struct wined3d_device *device = surface->resource.device;
5587 TRACE("surface %p, new location %#x.\n", surface, location);
5589 /* TODO: Make this work for modes other than FBO */
5590 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5592 if (!(surface->flags & location))
5594 w = surface->ds_current_size.cx;
5595 h = surface->ds_current_size.cy;
5596 surface->ds_current_size.cx = 0;
5597 surface->ds_current_size.cy = 0;
5601 w = surface->resource.width;
5602 h = surface->resource.height;
5605 if (surface->ds_current_size.cx == surface->resource.width
5606 && surface->ds_current_size.cy == surface->resource.height)
5608 TRACE("Location (%#x) is already up to date.\n", location);
5612 if (surface->current_renderbuffer)
5614 FIXME("Not supported with fixed up depth stencil.\n");
5618 if (!(surface->flags & SFLAG_LOCATIONS))
5620 /* This mostly happens when a depth / stencil is used without being
5621 * cleared first. In principle we could upload from sysmem, or
5622 * explicitly clear before first usage. For the moment there don't
5623 * appear to be a lot of applications depending on this, so a FIXME
5625 FIXME("No up to date depth stencil location.\n");
5626 surface->flags |= location;
5627 surface->ds_current_size.cx = surface->resource.width;
5628 surface->ds_current_size.cy = surface->resource.height;
5632 if (location == SFLAG_INTEXTURE)
5634 GLint old_binding = 0;
5637 /* The render target is allowed to be smaller than the depth/stencil
5638 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5639 * than the offscreen surface. Don't overwrite the offscreen surface
5640 * with undefined data. */
5641 w = min(w, context->swapchain->desc.backbuffer_width);
5642 h = min(h, context->swapchain->desc.backbuffer_height);
5644 TRACE("Copying onscreen depth buffer to depth texture.\n");
5648 if (!device->depth_blt_texture)
5650 glGenTextures(1, &device->depth_blt_texture);
5653 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5654 * directly on the FBO texture. That's because we need to flip. */
5655 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5656 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5657 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5659 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5660 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5664 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5665 bind_target = GL_TEXTURE_2D;
5667 glBindTexture(bind_target, device->depth_blt_texture);
5668 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5669 * internal format, because the internal format might include stencil
5670 * data. In principle we should copy stencil data as well, but unless
5671 * the driver supports stencil export it's hard to do, and doesn't
5672 * seem to be needed in practice. If the hardware doesn't support
5673 * writing stencil data, the glCopyTexImage2D() call might trigger
5674 * software fallbacks. */
5675 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5676 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5677 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5678 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5679 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5680 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5681 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5682 glBindTexture(bind_target, old_binding);
5684 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5685 NULL, surface, SFLAG_INTEXTURE);
5686 context_set_draw_buffer(context, GL_NONE);
5687 glReadBuffer(GL_NONE);
5689 /* Do the actual blit */
5690 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5691 checkGLcall("depth_blt");
5693 context_invalidate_state(context, STATE_FRAMEBUFFER);
5697 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5699 else if (location == SFLAG_INDRAWABLE)
5701 TRACE("Copying depth texture to onscreen depth buffer.\n");
5705 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5706 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5707 surface_depth_blt(surface, context, surface->texture_name,
5708 0, surface->pow2Height - h, w, h, surface->texture_target);
5709 checkGLcall("depth_blt");
5711 context_invalidate_state(context, STATE_FRAMEBUFFER);
5715 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5719 ERR("Invalid location (%#x) specified.\n", location);
5722 surface->flags |= location;
5723 surface->ds_current_size.cx = surface->resource.width;
5724 surface->ds_current_size.cy = surface->resource.height;
5727 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5729 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5730 struct wined3d_surface *overlay;
5732 TRACE("surface %p, location %s, persistent %#x.\n",
5733 surface, debug_surflocation(location), persistent);
5735 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5736 && !(surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5737 && (location & SFLAG_INDRAWABLE))
5738 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5740 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5741 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5742 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5746 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5747 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5749 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5751 TRACE("Passing to container.\n");
5752 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5755 surface->flags &= ~SFLAG_LOCATIONS;
5756 surface->flags |= location;
5758 /* Redraw emulated overlays, if any */
5759 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5761 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5763 surface_draw_overlay(overlay);
5769 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5771 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5773 TRACE("Passing to container\n");
5774 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5777 surface->flags &= ~location;
5780 if (!(surface->flags & SFLAG_LOCATIONS))
5782 ERR("Surface %p does not have any up to date location.\n", surface);
5786 static DWORD resource_access_from_location(DWORD location)
5790 case SFLAG_INSYSMEM:
5791 return WINED3D_RESOURCE_ACCESS_CPU;
5793 case SFLAG_INDRAWABLE:
5794 case SFLAG_INSRGBTEX:
5795 case SFLAG_INTEXTURE:
5796 case SFLAG_INRB_MULTISAMPLE:
5797 case SFLAG_INRB_RESOLVED:
5798 return WINED3D_RESOURCE_ACCESS_GPU;
5801 FIXME("Unhandled location %#x.\n", location);
5806 static void surface_load_sysmem(struct wined3d_surface *surface,
5807 const struct wined3d_gl_info *gl_info, const RECT *rect)
5809 surface_prepare_system_memory(surface);
5811 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5812 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5814 /* Download the surface to system memory. */
5815 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5817 struct wined3d_device *device = surface->resource.device;
5818 struct wined3d_context *context;
5820 /* TODO: Use already acquired context when possible. */
5821 context = context_acquire(device, NULL);
5823 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5824 surface_download_data(surface, gl_info);
5826 context_release(context);
5831 if (surface->flags & SFLAG_INDRAWABLE)
5833 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5834 wined3d_surface_get_pitch(surface));
5838 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5839 surface, surface->flags & SFLAG_LOCATIONS);
5842 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5843 const struct wined3d_gl_info *gl_info, const RECT *rect)
5845 struct wined3d_device *device = surface->resource.device;
5846 struct wined3d_format format;
5847 CONVERT_TYPES convert;
5851 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5853 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5854 return WINED3DERR_INVALIDCALL;
5857 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5858 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5860 if (surface->flags & SFLAG_INTEXTURE)
5864 surface_get_rect(surface, rect, &r);
5865 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5870 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5872 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5873 * path through sysmem. */
5874 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5877 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5879 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5880 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5882 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5884 struct wined3d_context *context;
5886 TRACE("Removing the pbo attached to surface %p.\n", surface);
5888 /* TODO: Use already acquired context when possible. */
5889 context = context_acquire(device, NULL);
5891 surface_remove_pbo(surface, gl_info);
5893 context_release(context);
5896 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5898 UINT height = surface->resource.height;
5899 UINT width = surface->resource.width;
5900 UINT src_pitch, dst_pitch;
5902 byte_count = format.conv_byte_count;
5903 src_pitch = wined3d_surface_get_pitch(surface);
5905 /* Stick to the alignment for the converted surface too, makes it
5906 * easier to load the surface. */
5907 dst_pitch = width * byte_count;
5908 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5910 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5912 ERR("Out of memory (%u).\n", dst_pitch * height);
5913 return E_OUTOFMEMORY;
5916 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5917 src_pitch, width, height, dst_pitch, convert, surface);
5919 surface->flags |= SFLAG_CONVERTED;
5923 surface->flags &= ~SFLAG_CONVERTED;
5924 mem = surface->resource.allocatedMemory;
5925 byte_count = format.byte_count;
5928 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5930 /* Don't delete PBO memory. */
5931 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5932 HeapFree(GetProcessHeap(), 0, mem);
5937 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5938 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5940 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5941 struct wined3d_device *device = surface->resource.device;
5942 struct wined3d_context *context;
5943 UINT width, src_pitch, dst_pitch;
5944 struct wined3d_bo_address data;
5945 struct wined3d_format format;
5946 POINT dst_point = {0, 0};
5947 CONVERT_TYPES convert;
5950 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
5951 && surface_is_offscreen(surface)
5952 && (surface->flags & SFLAG_INDRAWABLE))
5954 surface_load_fb_texture(surface, srgb);
5959 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
5960 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
5961 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5962 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5963 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5966 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
5967 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
5969 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
5970 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
5975 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
5976 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
5977 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5978 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5979 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5981 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
5982 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
5983 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5985 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, src_location,
5986 &rect, surface, dst_location, &rect);
5991 /* Upload from system memory */
5993 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
5994 TRUE /* We will use textures */, &format, &convert);
5998 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6000 /* Performance warning... */
6001 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6002 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6007 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6009 /* Performance warning... */
6010 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6011 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6015 if (!(surface->flags & SFLAG_INSYSMEM))
6017 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6018 /* Lets hope we get it from somewhere... */
6019 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6022 /* TODO: Use already acquired context when possible. */
6023 context = context_acquire(device, NULL);
6025 surface_prepare_texture(surface, context, srgb);
6026 surface_bind_and_dirtify(surface, context, srgb);
6028 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6030 surface->flags |= SFLAG_GLCKEY;
6031 surface->gl_color_key = surface->src_blt_color_key;
6033 else surface->flags &= ~SFLAG_GLCKEY;
6035 width = surface->resource.width;
6036 src_pitch = wined3d_surface_get_pitch(surface);
6038 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6039 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6041 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6043 TRACE("Removing the pbo attached to surface %p.\n", surface);
6044 surface_remove_pbo(surface, gl_info);
6049 /* This code is entered for texture formats which need a fixup. */
6050 UINT height = surface->resource.height;
6052 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6053 dst_pitch = width * format.conv_byte_count;
6054 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6056 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6058 ERR("Out of memory (%u).\n", dst_pitch * height);
6059 context_release(context);
6060 return E_OUTOFMEMORY;
6062 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6063 format.byte_count = format.conv_byte_count;
6064 src_pitch = dst_pitch;
6066 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6068 /* This code is only entered for color keying fixups */
6069 UINT height = surface->resource.height;
6071 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6072 dst_pitch = width * format.conv_byte_count;
6073 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6075 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6077 ERR("Out of memory (%u).\n", dst_pitch * height);
6078 context_release(context);
6079 return E_OUTOFMEMORY;
6081 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6082 width, height, dst_pitch, convert, surface);
6083 format.byte_count = format.conv_byte_count;
6084 src_pitch = dst_pitch;
6088 mem = surface->resource.allocatedMemory;
6091 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6093 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
6095 context_release(context);
6097 /* Don't delete PBO memory. */
6098 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6099 HeapFree(GetProcessHeap(), 0, mem);
6104 static void surface_multisample_resolve(struct wined3d_surface *surface)
6106 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6108 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6109 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6111 surface_blt_fbo(surface->resource.device, WINED3DTEXF_POINT,
6112 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6115 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6117 struct wined3d_device *device = surface->resource.device;
6118 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6121 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6123 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6125 if (location == SFLAG_INTEXTURE)
6127 struct wined3d_context *context = context_acquire(device, NULL);
6128 surface_load_ds_location(surface, context, location);
6129 context_release(context);
6134 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6135 return WINED3DERR_INVALIDCALL;
6139 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6140 location = SFLAG_INTEXTURE;
6142 if (surface->flags & location)
6144 TRACE("Location already up to date.\n");
6146 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
6147 && surface_need_pbo(surface, gl_info))
6148 surface_load_pbo(surface, gl_info);
6153 if (WARN_ON(d3d_surface))
6155 DWORD required_access = resource_access_from_location(location);
6156 if ((surface->resource.access_flags & required_access) != required_access)
6157 WARN("Operation requires %#x access, but surface only has %#x.\n",
6158 required_access, surface->resource.access_flags);
6161 if (!(surface->flags & SFLAG_LOCATIONS))
6163 ERR("Surface %p does not have any up to date location.\n", surface);
6164 surface->flags |= SFLAG_LOST;
6165 return WINED3DERR_DEVICELOST;
6170 case SFLAG_INSYSMEM:
6171 surface_load_sysmem(surface, gl_info, rect);
6174 case SFLAG_INDRAWABLE:
6175 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6179 case SFLAG_INRB_RESOLVED:
6180 surface_multisample_resolve(surface);
6183 case SFLAG_INTEXTURE:
6184 case SFLAG_INSRGBTEX:
6185 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6190 ERR("Don't know how to handle location %#x.\n", location);
6196 surface->flags |= location;
6198 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6199 surface_evict_sysmem(surface);
6202 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6203 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6205 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6211 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6213 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6215 /* Not on a swapchain - must be offscreen */
6216 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6218 /* The front buffer is always onscreen */
6219 if (surface == swapchain->front_buffer) return FALSE;
6221 /* If the swapchain is rendered to an FBO, the backbuffer is
6222 * offscreen, otherwise onscreen */
6223 return swapchain->render_to_fbo;
6226 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6227 /* Context activation is done by the caller. */
6228 static void ffp_blit_free(struct wined3d_device *device) { }
6230 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6231 /* Context activation is done by the caller. */
6232 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6235 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6237 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6239 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6241 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6245 /* Context activation is done by the caller. */
6246 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6248 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6250 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6251 * else the surface is converted in software at upload time in LoadLocation.
6253 if (!(surface->flags & SFLAG_CONVERTED) && fixup == COMPLEX_FIXUP_P8
6254 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6255 ffp_blit_p8_upload_palette(surface, context->gl_info);
6258 glEnable(surface->texture_target);
6259 checkGLcall("glEnable(surface->texture_target)");
6264 /* Context activation is done by the caller. */
6265 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6268 glDisable(GL_TEXTURE_2D);
6269 checkGLcall("glDisable(GL_TEXTURE_2D)");
6270 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6272 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6273 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6275 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6277 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6278 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6283 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6284 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6285 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6287 enum complex_fixup src_fixup;
6291 case WINED3D_BLIT_OP_COLOR_BLIT:
6292 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
6295 src_fixup = get_complex_fixup(src_format->color_fixup);
6296 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6298 TRACE("Checking support for fixup:\n");
6299 dump_color_fixup_desc(src_format->color_fixup);
6302 if (!is_identity_fixup(dst_format->color_fixup))
6304 TRACE("Destination fixups are not supported\n");
6308 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6310 TRACE("P8 fixup supported\n");
6314 /* We only support identity conversions. */
6315 if (is_identity_fixup(src_format->color_fixup))
6321 TRACE("[FAILED]\n");
6324 case WINED3D_BLIT_OP_COLOR_FILL:
6325 if (dst_pool == WINED3DPOOL_SYSTEMMEM)
6328 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6330 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6333 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6335 TRACE("Color fill not supported\n");
6339 /* FIXME: We should reject color fills on formats with fixups,
6340 * but this would break P8 color fills for example. */
6344 case WINED3D_BLIT_OP_DEPTH_FILL:
6348 TRACE("Unsupported blit_op=%d\n", blit_op);
6353 /* Do not call while under the GL lock. */
6354 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6355 const RECT *dst_rect, const struct wined3d_color *color)
6357 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6358 struct wined3d_fb_state fb = {&dst_surface, NULL};
6360 return device_clear_render_targets(device, 1, &fb,
6361 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6364 /* Do not call while under the GL lock. */
6365 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6366 struct wined3d_surface *surface, const RECT *rect, float depth)
6368 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6369 struct wined3d_fb_state fb = {NULL, surface};
6371 return device_clear_render_targets(device, 0, &fb,
6372 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6375 const struct blit_shader ffp_blit = {
6381 ffp_blit_color_fill,
6382 ffp_blit_depth_fill,
6385 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6390 /* Context activation is done by the caller. */
6391 static void cpu_blit_free(struct wined3d_device *device)
6395 /* Context activation is done by the caller. */
6396 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6401 /* Context activation is done by the caller. */
6402 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6406 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6407 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6408 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6410 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6418 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
6419 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
6420 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
6422 UINT row_block_count;
6423 const BYTE *src_row;
6430 row_block_count = (update_w + format->block_width - 1) / format->block_width;
6434 for (y = 0; y < update_h; y += format->block_height)
6436 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
6437 src_row += src_pitch;
6438 dst_row += dst_pitch;
6444 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
6446 src_row += (((update_h / format->block_height) - 1) * src_pitch);
6450 case WINED3DFMT_DXT1:
6451 for (y = 0; y < update_h; y += format->block_height)
6456 BYTE control_row[4];
6459 const struct block *s = (const struct block *)src_row;
6460 struct block *d = (struct block *)dst_row;
6462 for (x = 0; x < row_block_count; ++x)
6464 d[x].color[0] = s[x].color[0];
6465 d[x].color[1] = s[x].color[1];
6466 d[x].control_row[0] = s[x].control_row[3];
6467 d[x].control_row[1] = s[x].control_row[2];
6468 d[x].control_row[2] = s[x].control_row[1];
6469 d[x].control_row[3] = s[x].control_row[0];
6471 src_row -= src_pitch;
6472 dst_row += dst_pitch;
6476 case WINED3DFMT_DXT3:
6477 for (y = 0; y < update_h; y += format->block_height)
6483 BYTE control_row[4];
6486 const struct block *s = (const struct block *)src_row;
6487 struct block *d = (struct block *)dst_row;
6489 for (x = 0; x < row_block_count; ++x)
6491 d[x].alpha_row[0] = s[x].alpha_row[3];
6492 d[x].alpha_row[1] = s[x].alpha_row[2];
6493 d[x].alpha_row[2] = s[x].alpha_row[1];
6494 d[x].alpha_row[3] = s[x].alpha_row[0];
6495 d[x].color[0] = s[x].color[0];
6496 d[x].color[1] = s[x].color[1];
6497 d[x].control_row[0] = s[x].control_row[3];
6498 d[x].control_row[1] = s[x].control_row[2];
6499 d[x].control_row[2] = s[x].control_row[1];
6500 d[x].control_row[3] = s[x].control_row[0];
6502 src_row -= src_pitch;
6503 dst_row += dst_pitch;
6508 FIXME("Compressed flip not implemented for format %s.\n",
6509 debug_d3dformat(format->id));
6514 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
6515 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
6520 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6521 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6522 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6524 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6525 const struct wined3d_format *src_format, *dst_format;
6526 struct wined3d_surface *orig_src = src_surface;
6527 struct wined3d_mapped_rect dst_map, src_map;
6528 HRESULT hr = WINED3D_OK;
6534 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6535 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6536 flags, fx, debug_d3dtexturefiltertype(filter));
6546 full_rect.right = dst_surface->resource.width;
6547 full_rect.bottom = dst_surface->resource.height;
6548 IntersectRect(&xdst, &full_rect, dst_rect);
6552 BOOL clip_horiz, clip_vert;
6555 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6556 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6558 if (clip_vert || clip_horiz)
6560 /* Now check if this is a special case or not... */
6561 if ((flags & WINEDDBLT_DDFX)
6562 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6563 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6565 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6573 xsrc.left -= xdst.left;
6576 if (xdst.right > dst_surface->resource.width)
6578 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6579 xdst.right = (int)dst_surface->resource.width;
6587 xsrc.top -= xdst.top;
6590 if (xdst.bottom > dst_surface->resource.height)
6592 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6593 xdst.bottom = (int)dst_surface->resource.height;
6597 /* And check if after clipping something is still to be done... */
6598 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6599 || (xdst.left >= (int)dst_surface->resource.width)
6600 || (xdst.top >= (int)dst_surface->resource.height)
6601 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6602 || (xsrc.left >= (int)src_surface->resource.width)
6603 || (xsrc.top >= (int)src_surface->resource.height))
6605 TRACE("Nothing to be done after clipping.\n");
6611 if (src_surface == dst_surface)
6613 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6615 src_format = dst_surface->resource.format;
6616 dst_format = src_format;
6620 dst_format = dst_surface->resource.format;
6623 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6625 src_surface = surface_convert_format(src_surface, dst_format->id);
6628 /* The conv function writes a FIXME */
6629 WARN("Cannot convert source surface format to dest format.\n");
6633 wined3d_surface_map(src_surface, &src_map, NULL, WINED3DLOCK_READONLY);
6634 src_format = src_surface->resource.format;
6638 src_format = dst_format;
6641 wined3d_surface_map(dst_surface, &dst_map, &xdst, 0);
6643 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6646 bpp = dst_surface->resource.format->byte_count;
6647 srcheight = xsrc.bottom - xsrc.top;
6648 srcwidth = xsrc.right - xsrc.left;
6649 dstheight = xdst.bottom - xdst.top;
6650 dstwidth = xdst.right - xdst.left;
6651 width = (xdst.right - xdst.left) * bpp;
6653 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
6655 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6657 if (src_surface == dst_surface)
6659 FIXME("Only plain blits supported on compressed surfaces.\n");
6664 if (srcheight != dstheight || srcwidth != dstwidth)
6666 WARN("Stretching not supported on compressed surfaces.\n");
6667 hr = WINED3DERR_INVALIDCALL;
6671 if (srcwidth & (src_format->block_width - 1) || srcheight & (src_format->block_height - 1))
6673 WARN("Rectangle not block-aligned.\n");
6674 hr = WINED3DERR_INVALIDCALL;
6678 hr = surface_cpu_blt_compressed(src_map.data, dst_map.data,
6679 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
6680 src_format, flags, fx);
6684 if (dst_rect && src_surface != dst_surface)
6685 dbuf = dst_map.data;
6687 dbuf = (BYTE *)dst_map.data + (xdst.top * dst_map.row_pitch) + (xdst.left * bpp);
6689 /* First, all the 'source-less' blits */
6690 if (flags & WINEDDBLT_COLORFILL)
6692 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
6693 flags &= ~WINEDDBLT_COLORFILL;
6696 if (flags & WINEDDBLT_DEPTHFILL)
6698 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6700 if (flags & WINEDDBLT_ROP)
6702 /* Catch some degenerate cases here. */
6706 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
6708 case 0xAA0029: /* No-op */
6711 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
6713 case SRCCOPY: /* Well, we do that below? */
6716 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6719 flags &= ~WINEDDBLT_ROP;
6721 if (flags & WINEDDBLT_DDROPS)
6723 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6725 /* Now the 'with source' blits. */
6729 int sx, xinc, sy, yinc;
6731 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6734 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6735 && (srcwidth != dstwidth || srcheight != dstheight))
6737 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6738 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6741 sbase = (BYTE *)src_map.data + (xsrc.top * src_map.row_pitch) + xsrc.left * bpp;
6742 xinc = (srcwidth << 16) / dstwidth;
6743 yinc = (srcheight << 16) / dstheight;
6747 /* No effects, we can cheat here. */
6748 if (dstwidth == srcwidth)
6750 if (dstheight == srcheight)
6752 /* No stretching in either direction. This needs to be as
6753 * fast as possible. */
6756 /* Check for overlapping surfaces. */
6757 if (src_surface != dst_surface || xdst.top < xsrc.top
6758 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6760 /* No overlap, or dst above src, so copy from top downwards. */
6761 for (y = 0; y < dstheight; ++y)
6763 memcpy(dbuf, sbuf, width);
6764 sbuf += src_map.row_pitch;
6765 dbuf += dst_map.row_pitch;
6768 else if (xdst.top > xsrc.top)
6770 /* Copy from bottom upwards. */
6771 sbuf += src_map.row_pitch * dstheight;
6772 dbuf += dst_map.row_pitch * dstheight;
6773 for (y = 0; y < dstheight; ++y)
6775 sbuf -= src_map.row_pitch;
6776 dbuf -= dst_map.row_pitch;
6777 memcpy(dbuf, sbuf, width);
6782 /* Src and dst overlapping on the same line, use memmove. */
6783 for (y = 0; y < dstheight; ++y)
6785 memmove(dbuf, sbuf, width);
6786 sbuf += src_map.row_pitch;
6787 dbuf += dst_map.row_pitch;
6793 /* Stretching in y direction only. */
6794 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6796 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6797 memcpy(dbuf, sbuf, width);
6798 dbuf += dst_map.row_pitch;
6804 /* Stretching in X direction. */
6806 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6808 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6810 if ((sy >> 16) == (last_sy >> 16))
6812 /* This source row is the same as last source row -
6813 * Copy the already stretched row. */
6814 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
6818 #define STRETCH_ROW(type) \
6820 const type *s = (const type *)sbuf; \
6821 type *d = (type *)dbuf; \
6822 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6823 d[x] = s[sx >> 16]; \
6841 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6845 s = sbuf + 3 * (sx >> 16);
6846 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6847 d[0] = (pixel ) & 0xff;
6848 d[1] = (pixel >> 8) & 0xff;
6849 d[2] = (pixel >> 16) & 0xff;
6855 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6856 hr = WINED3DERR_NOTAVAILABLE;
6861 dbuf += dst_map.row_pitch;
6868 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
6869 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6870 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6871 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6873 /* The color keying flags are checked for correctness in ddraw */
6874 if (flags & WINEDDBLT_KEYSRC)
6876 keylow = src_surface->src_blt_color_key.color_space_low_value;
6877 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
6879 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6881 keylow = fx->ddckSrcColorkey.color_space_low_value;
6882 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
6885 if (flags & WINEDDBLT_KEYDEST)
6887 /* Destination color keys are taken from the source surface! */
6888 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
6889 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
6891 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6893 destkeylow = fx->ddckDestColorkey.color_space_low_value;
6894 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
6903 keymask = src_format->red_mask
6904 | src_format->green_mask
6905 | src_format->blue_mask;
6907 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6910 if (flags & WINEDDBLT_DDFX)
6912 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6915 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6916 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
6917 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6919 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6921 /* I don't think we need to do anything about this flag */
6922 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6924 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6927 dTopRight = dTopLeft;
6930 dBottomRight = dBottomLeft;
6932 dstxinc = dstxinc * -1;
6934 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6937 dTopLeft = dBottomLeft;
6940 dTopRight = dBottomRight;
6942 dstyinc = dstyinc * -1;
6944 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6946 /* I don't think we need to do anything about this flag */
6947 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6949 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6952 dBottomRight = dTopLeft;
6955 dBottomLeft = dTopRight;
6957 dstxinc = dstxinc * -1;
6958 dstyinc = dstyinc * -1;
6960 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6963 dTopLeft = dBottomLeft;
6964 dBottomLeft = dBottomRight;
6965 dBottomRight = dTopRight;
6970 dstxinc = dstxinc * -1;
6972 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6975 dTopLeft = dTopRight;
6976 dTopRight = dBottomRight;
6977 dBottomRight = dBottomLeft;
6982 dstyinc = dstyinc * -1;
6984 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6986 /* I don't think we need to do anything about this flag */
6987 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6990 flags &= ~(WINEDDBLT_DDFX);
6993 #define COPY_COLORKEY_FX(type) \
6996 type *d = (type *)dbuf, *dx, tmp; \
6997 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6999 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
7001 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
7003 tmp = s[sx >> 16]; \
7004 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
7005 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
7009 dx = (type *)(((BYTE *)dx) + dstxinc); \
7011 d = (type *)(((BYTE *)d) + dstyinc); \
7018 COPY_COLORKEY_FX(BYTE);
7021 COPY_COLORKEY_FX(WORD);
7024 COPY_COLORKEY_FX(DWORD);
7029 BYTE *d = dbuf, *dx;
7030 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
7032 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
7034 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
7036 DWORD pixel, dpixel = 0;
7037 s = sbuf + 3 * (sx>>16);
7038 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
7039 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
7040 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
7041 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
7043 dx[0] = (pixel ) & 0xff;
7044 dx[1] = (pixel >> 8) & 0xff;
7045 dx[2] = (pixel >> 16) & 0xff;
7054 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7055 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7056 hr = WINED3DERR_NOTAVAILABLE;
7058 #undef COPY_COLORKEY_FX
7064 if (flags && FIXME_ON(d3d_surface))
7066 FIXME("\tUnsupported flags: %#x.\n", flags);
7070 wined3d_surface_unmap(dst_surface);
7071 if (src_surface && src_surface != dst_surface)
7072 wined3d_surface_unmap(src_surface);
7073 /* Release the converted surface, if any. */
7074 if (src_surface && src_surface != orig_src)
7075 wined3d_surface_decref(src_surface);
7080 /* Do not call while under the GL lock. */
7081 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7082 const RECT *dst_rect, const struct wined3d_color *color)
7084 static const RECT src_rect;
7087 memset(&BltFx, 0, sizeof(BltFx));
7088 BltFx.dwSize = sizeof(BltFx);
7089 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7090 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7091 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7094 /* Do not call while under the GL lock. */
7095 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7096 struct wined3d_surface *surface, const RECT *rect, float depth)
7098 FIXME("Depth filling not implemented by cpu_blit.\n");
7099 return WINED3DERR_INVALIDCALL;
7102 const struct blit_shader cpu_blit = {
7108 cpu_blit_color_fill,
7109 cpu_blit_depth_fill,
7112 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7113 UINT width, UINT height, UINT level, enum wined3d_multisample_type multisample_type,
7114 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7115 WINED3DPOOL pool, DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops)
7117 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7118 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7119 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
7120 unsigned int resource_size;
7123 if (multisample_quality > 0)
7125 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7126 multisample_quality = 0;
7129 /* Quick lockable sanity check.
7130 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7131 * this function is too deep to need to care about things like this.
7132 * Levels need to be checked too, since they all affect what can be done. */
7135 case WINED3DPOOL_SCRATCH:
7138 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7139 "which are mutually exclusive, setting lockable to TRUE.\n");
7144 case WINED3DPOOL_SYSTEMMEM:
7146 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7149 case WINED3DPOOL_MANAGED:
7150 if (usage & WINED3DUSAGE_DYNAMIC)
7151 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7154 case WINED3DPOOL_DEFAULT:
7155 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7156 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7160 FIXME("Unknown pool %#x.\n", pool);
7164 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7165 FIXME("Trying to create a render target that isn't in the default pool.\n");
7167 /* FIXME: Check that the format is supported by the device. */
7169 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7171 return WINED3DERR_INVALIDCALL;
7173 surface->surface_type = surface_type;
7175 switch (surface_type)
7177 case SURFACE_OPENGL:
7178 surface->surface_ops = &surface_ops;
7182 surface->surface_ops = &gdi_surface_ops;
7186 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7187 return WINED3DERR_INVALIDCALL;
7190 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7191 multisample_type, multisample_quality, usage, pool, width, height, 1,
7192 resource_size, parent, parent_ops, &surface_resource_ops);
7195 WARN("Failed to initialize resource, returning %#x.\n", hr);
7199 /* "Standalone" surface. */
7200 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7202 surface->texture_level = level;
7203 list_init(&surface->overlays);
7206 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7207 if (flags & WINED3D_SURFACE_DISCARD)
7208 surface->flags |= SFLAG_DISCARD;
7209 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
7210 surface->flags |= SFLAG_PIN_SYSMEM;
7211 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7212 surface->flags |= SFLAG_LOCKABLE;
7213 /* I'm not sure if this qualifies as a hack or as an optimization. It
7214 * seems reasonable to assume that lockable render targets will get
7215 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7216 * creation. However, the other reason we want to do this is that several
7217 * ddraw applications access surface memory while the surface isn't
7218 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7219 * future locks prevents these from crashing. */
7220 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7221 surface->flags |= SFLAG_DYNLOCK;
7223 /* Mark the texture as dirty so that it gets loaded first time around. */
7224 surface_add_dirty_rect(surface, NULL);
7225 list_init(&surface->renderbuffers);
7227 TRACE("surface %p, memory %p, size %u\n",
7228 surface, surface->resource.allocatedMemory, surface->resource.size);
7230 /* Call the private setup routine */
7231 hr = surface->surface_ops->surface_private_setup(surface);
7234 ERR("Private setup failed, returning %#x\n", hr);
7235 surface_cleanup(surface);
7239 /* Similar to lockable rendertargets above, creating the DIB section
7240 * during surface initialization prevents the sysmem pointer from changing
7241 * after a wined3d_surface_getdc() call. */
7242 if ((usage & WINED3DUSAGE_OWNDC) && !surface->hDC
7243 && SUCCEEDED(surface_create_dib_section(surface)))
7245 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
7246 surface->resource.heapMemory = NULL;
7247 surface->resource.allocatedMemory = surface->dib.bitmap_data;
7253 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7254 enum wined3d_format_id format_id, UINT level, DWORD usage, WINED3DPOOL pool,
7255 enum wined3d_multisample_type multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7256 DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7258 struct wined3d_surface *object;
7261 TRACE("device %p, width %u, height %u, format %s, level %u\n",
7262 device, width, height, debug_d3dformat(format_id), level);
7263 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7264 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7265 TRACE("surface_type %#x, flags %#x, parent %p, parent_ops %p.\n", surface_type, flags, parent, parent_ops);
7267 if (surface_type == SURFACE_OPENGL && !device->adapter)
7269 ERR("OpenGL surfaces are not available without OpenGL.\n");
7270 return WINED3DERR_NOTAVAILABLE;
7273 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7276 ERR("Failed to allocate surface memory.\n");
7277 return WINED3DERR_OUTOFVIDEOMEMORY;
7280 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level,
7281 multisample_type, multisample_quality, device, usage, format_id, pool, flags, parent, parent_ops);
7284 WARN("Failed to initialize surface, returning %#x.\n", hr);
7285 HeapFree(GetProcessHeap(), 0, object);
7289 TRACE("Created surface %p.\n", object);