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, enum wined3d_texture_filter_type 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 enum wined3d_texture_filter_type 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, enum wined3d_texture_filter_type 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, WINED3D_TEXF_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] = WINED3D_TEXF_POINT;
367 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
368 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_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;
520 static BOOL surface_need_pbo(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
522 if (surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
524 if (!(surface->flags & SFLAG_DYNLOCK))
526 if (surface->flags & (SFLAG_CONVERTED | SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM))
528 if (!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT])
534 static void surface_load_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
536 struct wined3d_context *context;
539 context = context_acquire(surface->resource.device, NULL);
542 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
543 error = glGetError();
544 if (!surface->pbo || error != GL_NO_ERROR)
545 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
547 TRACE("Binding PBO %u.\n", surface->pbo);
549 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
550 checkGLcall("glBindBufferARB");
552 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
553 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
554 checkGLcall("glBufferDataARB");
556 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
557 checkGLcall("glBindBufferARB");
559 /* We don't need the system memory anymore and we can't even use it for PBOs. */
560 if (!(surface->flags & SFLAG_CLIENT))
562 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
563 surface->resource.heapMemory = NULL;
565 surface->resource.allocatedMemory = NULL;
566 surface->flags |= SFLAG_PBO;
568 context_release(context);
571 static void surface_prepare_system_memory(struct wined3d_surface *surface)
573 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
575 TRACE("surface %p.\n", surface);
577 if (!(surface->flags & SFLAG_PBO) && surface_need_pbo(surface, gl_info))
578 surface_load_pbo(surface, gl_info);
579 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
581 /* Whatever surface we have, make sure that there is memory allocated
582 * for the downloaded copy, or a PBO to map. */
583 if (!surface->resource.heapMemory)
584 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
586 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
587 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
589 if (surface->flags & SFLAG_INSYSMEM)
590 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
594 static void surface_evict_sysmem(struct wined3d_surface *surface)
596 if (surface->flags & SFLAG_DONOTFREE)
599 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
600 surface->resource.allocatedMemory = NULL;
601 surface->resource.heapMemory = NULL;
602 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
605 /* Context activation is done by the caller. */
606 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
607 struct wined3d_context *context, BOOL srgb)
609 struct wined3d_device *device = surface->resource.device;
610 DWORD active_sampler;
612 /* We don't need a specific texture unit, but after binding the texture
613 * the current unit is dirty. Read the unit back instead of switching to
614 * 0, this avoids messing around with the state manager's GL states. The
615 * current texture unit should always be a valid one.
617 * To be more specific, this is tricky because we can implicitly be
618 * called from sampler() in state.c. This means we can't touch anything
619 * other than whatever happens to be the currently active texture, or we
620 * would risk marking already applied sampler states dirty again. */
621 active_sampler = device->rev_tex_unit_map[context->active_texture];
623 if (active_sampler != WINED3D_UNMAPPED_STAGE)
624 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
625 surface_bind(surface, context, srgb);
628 static void surface_force_reload(struct wined3d_surface *surface)
630 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
633 static void surface_release_client_storage(struct wined3d_surface *surface)
635 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
638 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
639 if (surface->texture_name)
641 surface_bind_and_dirtify(surface, context, FALSE);
642 glTexImage2D(surface->texture_target, surface->texture_level,
643 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
645 if (surface->texture_name_srgb)
647 surface_bind_and_dirtify(surface, context, TRUE);
648 glTexImage2D(surface->texture_target, surface->texture_level,
649 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
651 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
654 context_release(context);
656 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
657 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
658 surface_force_reload(surface);
661 static HRESULT surface_private_setup(struct wined3d_surface *surface)
663 /* TODO: Check against the maximum texture sizes supported by the video card. */
664 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
665 unsigned int pow2Width, pow2Height;
667 TRACE("surface %p.\n", surface);
669 surface->texture_name = 0;
670 surface->texture_target = GL_TEXTURE_2D;
672 /* Non-power2 support */
673 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
675 pow2Width = surface->resource.width;
676 pow2Height = surface->resource.height;
680 /* Find the nearest pow2 match */
681 pow2Width = pow2Height = 1;
682 while (pow2Width < surface->resource.width)
684 while (pow2Height < surface->resource.height)
687 surface->pow2Width = pow2Width;
688 surface->pow2Height = pow2Height;
690 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
692 /* TODO: Add support for non power two compressed textures. */
693 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
695 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
696 surface, surface->resource.width, surface->resource.height);
697 return WINED3DERR_NOTAVAILABLE;
701 if (pow2Width != surface->resource.width
702 || pow2Height != surface->resource.height)
704 surface->flags |= SFLAG_NONPOW2;
707 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
708 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
710 /* One of three options:
711 * 1: Do the same as we do with NPOT and scale the texture, (any
712 * texture ops would require the texture to be scaled which is
714 * 2: Set the texture to the maximum size (bad idea).
715 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
716 * 4: Create the surface, but allow it to be used only for DirectDraw
717 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
718 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
719 * the render target. */
720 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
722 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
723 return WINED3DERR_NOTAVAILABLE;
726 /* We should never use this surface in combination with OpenGL! */
727 TRACE("Creating an oversized surface: %ux%u.\n",
728 surface->pow2Width, surface->pow2Height);
732 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
733 * and EXT_PALETTED_TEXTURE is used in combination with texture
734 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
735 * EXT_PALETTED_TEXTURE doesn't work in combination with
736 * ARB_TEXTURE_RECTANGLE. */
737 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
738 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
739 && gl_info->supported[EXT_PALETTED_TEXTURE]
740 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
742 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
743 surface->pow2Width = surface->resource.width;
744 surface->pow2Height = surface->resource.height;
745 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
749 switch (wined3d_settings.offscreen_rendering_mode)
752 surface->get_drawable_size = get_drawable_size_fbo;
756 surface->get_drawable_size = get_drawable_size_backbuffer;
760 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
761 return WINED3DERR_INVALIDCALL;
764 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
765 surface->flags |= SFLAG_DISCARDED;
770 static void surface_realize_palette(struct wined3d_surface *surface)
772 struct wined3d_palette *palette = surface->palette;
774 TRACE("surface %p.\n", surface);
776 if (!palette) return;
778 if (surface->resource.format->id == WINED3DFMT_P8_UINT
779 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
781 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
783 /* Make sure the texture is up to date. This call doesn't do
784 * anything if the texture is already up to date. */
785 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
787 /* We want to force a palette refresh, so mark the drawable as not being up to date */
788 if (!surface_is_offscreen(surface))
789 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
793 if (!(surface->flags & SFLAG_INSYSMEM))
795 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
796 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
798 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
802 if (surface->flags & SFLAG_DIBSECTION)
807 TRACE("Updating the DC's palette.\n");
809 for (i = 0; i < 256; ++i)
811 col[i].rgbRed = palette->palents[i].peRed;
812 col[i].rgbGreen = palette->palents[i].peGreen;
813 col[i].rgbBlue = palette->palents[i].peBlue;
814 col[i].rgbReserved = 0;
816 SetDIBColorTable(surface->hDC, 0, 256, col);
819 /* Propagate the changes to the drawable when we have a palette. */
820 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
821 surface_load_location(surface, surface->draw_binding, NULL);
824 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
828 /* If there's no destination surface there is nothing to do. */
829 if (!surface->overlay_dest)
832 /* Blt calls ModifyLocation on the dest surface, which in turn calls
833 * DrawOverlay to update the overlay. Prevent an endless recursion. */
834 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
837 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
838 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
839 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3D_TEXF_LINEAR);
840 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
845 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
847 struct wined3d_device *device = surface->resource.device;
848 const RECT *pass_rect = rect;
850 TRACE("surface %p, rect %s, flags %#x.\n",
851 surface, wine_dbgstr_rect(rect), flags);
853 if (flags & WINED3DLOCK_DISCARD)
855 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
856 surface_prepare_system_memory(surface);
857 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
861 /* surface_load_location() does not check if the rectangle specifies
862 * the full surface. Most callers don't need that, so do it here. */
863 if (rect && !rect->top && !rect->left
864 && rect->right == surface->resource.width
865 && rect->bottom == surface->resource.height)
867 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
870 if (surface->flags & SFLAG_PBO)
872 const struct wined3d_gl_info *gl_info;
873 struct wined3d_context *context;
875 context = context_acquire(device, NULL);
876 gl_info = context->gl_info;
879 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
880 checkGLcall("glBindBufferARB");
882 /* This shouldn't happen but could occur if some other function
883 * didn't handle the PBO properly. */
884 if (surface->resource.allocatedMemory)
885 ERR("The surface already has PBO memory allocated.\n");
887 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
888 checkGLcall("glMapBufferARB");
890 /* Make sure the PBO isn't set anymore in order not to break non-PBO
892 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
893 checkGLcall("glBindBufferARB");
896 context_release(context);
899 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
902 surface_add_dirty_rect(surface, NULL);
905 struct wined3d_box b;
909 b.right = rect->right;
910 b.bottom = rect->bottom;
913 surface_add_dirty_rect(surface, &b);
918 static void surface_unmap(struct wined3d_surface *surface)
920 struct wined3d_device *device = surface->resource.device;
923 TRACE("surface %p.\n", surface);
925 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
927 if (surface->flags & SFLAG_PBO)
929 const struct wined3d_gl_info *gl_info;
930 struct wined3d_context *context;
932 TRACE("Freeing PBO memory.\n");
934 context = context_acquire(device, NULL);
935 gl_info = context->gl_info;
938 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
939 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
940 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
941 checkGLcall("glUnmapBufferARB");
943 context_release(context);
945 surface->resource.allocatedMemory = NULL;
948 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
950 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
952 TRACE("Not dirtified, nothing to do.\n");
956 if ((surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
957 && surface->container.u.swapchain->front_buffer == surface)
958 || (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER
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);
1076 context_invalidate_state(context, STATE_FRAMEBUFFER);
1078 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1080 glDepthMask(GL_TRUE);
1081 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
1083 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1085 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1087 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1088 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
1091 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
1094 glDisable(GL_SCISSOR_TEST);
1095 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1097 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1098 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1099 checkGLcall("glBlitFramebuffer()");
1103 if (wined3d_settings.strict_draw_ordering)
1104 wglFlush(); /* Flush to ensure ordering across contexts. */
1106 context_release(context);
1109 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1110 * Depth / stencil is not supported. */
1111 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
1112 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1113 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1115 const struct wined3d_gl_info *gl_info;
1116 struct wined3d_context *context;
1117 RECT src_rect, dst_rect;
1121 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1122 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1123 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1124 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1125 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1127 src_rect = *src_rect_in;
1128 dst_rect = *dst_rect_in;
1132 case WINED3D_TEXF_LINEAR:
1133 gl_filter = GL_LINEAR;
1137 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1138 case WINED3D_TEXF_NONE:
1139 case WINED3D_TEXF_POINT:
1140 gl_filter = GL_NEAREST;
1144 /* Resolve the source surface first if needed. */
1145 if (src_location == SFLAG_INRB_MULTISAMPLE
1146 && (src_surface->resource.format->id != dst_surface->resource.format->id
1147 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1148 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1149 src_location = SFLAG_INRB_RESOLVED;
1151 /* Make sure the locations are up-to-date. Loading the destination
1152 * surface isn't required if the entire surface is overwritten. (And is
1153 * in fact harmful if we're being called by surface_load_location() with
1154 * the purpose of loading the destination surface.) */
1155 surface_load_location(src_surface, src_location, NULL);
1156 if (!surface_is_full_rect(dst_surface, &dst_rect))
1157 surface_load_location(dst_surface, dst_location, NULL);
1159 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1160 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1161 else context = context_acquire(device, NULL);
1163 if (!context->valid)
1165 context_release(context);
1166 WARN("Invalid context, skipping blit.\n");
1170 gl_info = context->gl_info;
1172 if (src_location == SFLAG_INDRAWABLE)
1174 TRACE("Source surface %p is onscreen.\n", src_surface);
1175 buffer = surface_get_gl_buffer(src_surface);
1176 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1180 TRACE("Source surface %p is offscreen.\n", src_surface);
1181 buffer = GL_COLOR_ATTACHMENT0;
1185 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1186 glReadBuffer(buffer);
1187 checkGLcall("glReadBuffer()");
1188 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1191 if (dst_location == SFLAG_INDRAWABLE)
1193 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1194 buffer = surface_get_gl_buffer(dst_surface);
1195 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1199 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1200 buffer = GL_COLOR_ATTACHMENT0;
1204 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1205 context_set_draw_buffer(context, buffer);
1206 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1207 context_invalidate_state(context, STATE_FRAMEBUFFER);
1209 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1210 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
1211 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
1212 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
1213 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
1215 glDisable(GL_SCISSOR_TEST);
1216 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1218 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1219 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1220 checkGLcall("glBlitFramebuffer()");
1224 if (wined3d_settings.strict_draw_ordering
1225 || (dst_location == SFLAG_INDRAWABLE
1226 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1229 context_release(context);
1232 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1233 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
1234 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
1236 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1239 /* Source and/or destination need to be on the GL side */
1240 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
1245 case WINED3D_BLIT_OP_COLOR_BLIT:
1246 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1248 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1252 case WINED3D_BLIT_OP_DEPTH_BLIT:
1253 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1255 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1263 if (!(src_format->id == dst_format->id
1264 || (is_identity_fixup(src_format->color_fixup)
1265 && is_identity_fixup(dst_format->color_fixup))))
1271 /* This function checks if the primary render target uses the 8bit paletted format. */
1272 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1274 if (device->fb.render_targets && device->fb.render_targets[0])
1276 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1277 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1278 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1284 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1285 DWORD color, struct wined3d_color *float_color)
1287 const struct wined3d_format *format = surface->resource.format;
1288 const struct wined3d_device *device = surface->resource.device;
1292 case WINED3DFMT_P8_UINT:
1293 if (surface->palette)
1295 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1296 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1297 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1301 float_color->r = 0.0f;
1302 float_color->g = 0.0f;
1303 float_color->b = 0.0f;
1305 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1308 case WINED3DFMT_B5G6R5_UNORM:
1309 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1310 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1311 float_color->b = (color & 0x1f) / 31.0f;
1312 float_color->a = 1.0f;
1315 case WINED3DFMT_B8G8R8_UNORM:
1316 case WINED3DFMT_B8G8R8X8_UNORM:
1317 float_color->r = D3DCOLOR_R(color);
1318 float_color->g = D3DCOLOR_G(color);
1319 float_color->b = D3DCOLOR_B(color);
1320 float_color->a = 1.0f;
1323 case WINED3DFMT_B8G8R8A8_UNORM:
1324 float_color->r = D3DCOLOR_R(color);
1325 float_color->g = D3DCOLOR_G(color);
1326 float_color->b = D3DCOLOR_B(color);
1327 float_color->a = D3DCOLOR_A(color);
1331 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1338 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1340 const struct wined3d_format *format = surface->resource.format;
1344 case WINED3DFMT_S1_UINT_D15_UNORM:
1345 *float_depth = depth / (float)0x00007fff;
1348 case WINED3DFMT_D16_UNORM:
1349 *float_depth = depth / (float)0x0000ffff;
1352 case WINED3DFMT_D24_UNORM_S8_UINT:
1353 case WINED3DFMT_X8D24_UNORM:
1354 *float_depth = depth / (float)0x00ffffff;
1357 case WINED3DFMT_D32_UNORM:
1358 *float_depth = depth / (float)0xffffffff;
1362 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1369 /* Do not call while under the GL lock. */
1370 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1372 const struct wined3d_resource *resource = &surface->resource;
1373 struct wined3d_device *device = resource->device;
1374 const struct blit_shader *blitter;
1376 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1377 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1380 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1381 return WINED3DERR_INVALIDCALL;
1384 return blitter->depth_fill(device, surface, rect, depth);
1387 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1388 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1390 struct wined3d_device *device = src_surface->resource.device;
1392 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1393 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1394 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1395 return WINED3DERR_INVALIDCALL;
1397 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1399 surface_modify_ds_location(dst_surface, SFLAG_INTEXTURE,
1400 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1405 /* Do not call while under the GL lock. */
1406 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1407 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1408 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
1410 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1411 struct wined3d_device *device = dst_surface->resource.device;
1412 DWORD src_ds_flags, dst_ds_flags;
1413 RECT src_rect, dst_rect;
1414 BOOL scale, convert;
1416 static const DWORD simple_blit = WINEDDBLT_ASYNC
1417 | WINEDDBLT_COLORFILL
1419 | WINEDDBLT_DEPTHFILL
1420 | WINEDDBLT_DONOTWAIT;
1422 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1423 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1424 flags, fx, debug_d3dtexturefiltertype(filter));
1425 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1429 TRACE("dwSize %#x.\n", fx->dwSize);
1430 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
1431 TRACE("dwROP %#x.\n", fx->dwROP);
1432 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
1433 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
1434 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
1435 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
1436 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
1437 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
1438 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
1439 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
1440 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
1441 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
1442 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
1443 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
1444 TRACE("dwReserved %#x.\n", fx->dwReserved);
1445 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
1446 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
1447 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
1448 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
1449 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
1450 TRACE("ddckDestColorkey {%#x, %#x}.\n",
1451 fx->ddckDestColorkey.color_space_low_value,
1452 fx->ddckDestColorkey.color_space_high_value);
1453 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
1454 fx->ddckSrcColorkey.color_space_low_value,
1455 fx->ddckSrcColorkey.color_space_high_value);
1458 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1460 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1461 return WINEDDERR_SURFACEBUSY;
1464 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1466 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1467 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1468 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1469 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1470 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1472 WARN("The application gave us a bad destination rectangle.\n");
1473 return WINEDDERR_INVALIDRECT;
1478 surface_get_rect(src_surface, src_rect_in, &src_rect);
1480 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1481 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1482 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1483 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1484 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1486 WARN("Application gave us bad source rectangle for Blt.\n");
1487 return WINEDDERR_INVALIDRECT;
1492 memset(&src_rect, 0, sizeof(src_rect));
1495 if (!fx || !(fx->dwDDFX))
1496 flags &= ~WINEDDBLT_DDFX;
1498 if (flags & WINEDDBLT_WAIT)
1499 flags &= ~WINEDDBLT_WAIT;
1501 if (flags & WINEDDBLT_ASYNC)
1503 static unsigned int once;
1506 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1507 flags &= ~WINEDDBLT_ASYNC;
1510 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1511 if (flags & WINEDDBLT_DONOTWAIT)
1513 static unsigned int once;
1516 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1517 flags &= ~WINEDDBLT_DONOTWAIT;
1520 if (!device->d3d_initialized)
1522 WARN("D3D not initialized, using fallback.\n");
1526 /* We want to avoid invalidating the sysmem location for converted
1527 * surfaces, since otherwise we'd have to convert the data back when
1529 if (dst_surface->flags & SFLAG_CONVERTED)
1531 WARN("Converted surface, using CPU blit.\n");
1532 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1535 if (flags & ~simple_blit)
1537 WARN("Using fallback for complex blit (%#x).\n", flags);
1541 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1542 src_swapchain = src_surface->container.u.swapchain;
1544 src_swapchain = NULL;
1546 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1547 dst_swapchain = dst_surface->container.u.swapchain;
1549 dst_swapchain = NULL;
1551 /* This isn't strictly needed. FBO blits for example could deal with
1552 * cross-swapchain blits by first downloading the source to a texture
1553 * before switching to the destination context. We just have this here to
1554 * not have to deal with the issue, since cross-swapchain blits should be
1556 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1558 FIXME("Using fallback for cross-swapchain blit.\n");
1563 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
1564 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
1565 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
1567 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1569 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1573 if (src_ds_flags || dst_ds_flags)
1575 if (flags & WINEDDBLT_DEPTHFILL)
1579 TRACE("Depth fill.\n");
1581 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1582 return WINED3DERR_INVALIDCALL;
1584 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1589 if (src_ds_flags != dst_ds_flags)
1591 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1592 return WINED3DERR_INVALIDCALL;
1595 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1601 /* In principle this would apply to depth blits as well, but we don't
1602 * implement those in the CPU blitter at the moment. */
1603 if ((dst_surface->flags & SFLAG_INSYSMEM)
1604 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
1607 TRACE("Not doing sysmem blit because of scaling.\n");
1609 TRACE("Not doing sysmem blit because of format conversion.\n");
1611 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1614 if (flags & WINEDDBLT_COLORFILL)
1616 struct wined3d_color color;
1618 TRACE("Color fill.\n");
1620 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1623 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1628 TRACE("Color blit.\n");
1631 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
1634 TRACE("Not doing upload because of scaling.\n");
1636 TRACE("Not doing upload because of format conversion.\n");
1639 POINT dst_point = {dst_rect.left, dst_rect.top};
1641 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
1643 if (!surface_is_offscreen(dst_surface))
1644 surface_load_location(dst_surface, dst_surface->draw_binding, NULL);
1650 /* Use present for back -> front blits. The idea behind this is
1651 * that present is potentially faster than a blit, in particular
1652 * when FBO blits aren't available. Some ddraw applications like
1653 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
1654 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
1655 * applications can't blit directly to the frontbuffer. */
1656 if (dst_swapchain && dst_swapchain->back_buffers
1657 && dst_surface == dst_swapchain->front_buffer
1658 && src_surface == dst_swapchain->back_buffers[0])
1660 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
1662 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1664 /* Set the swap effect to COPY, we don't want the backbuffer
1665 * to become undefined. */
1666 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
1667 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
1668 dst_swapchain->desc.swap_effect = swap_effect;
1673 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1674 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1675 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1677 TRACE("Using FBO blit.\n");
1679 surface_blt_fbo(device, filter,
1680 src_surface, src_surface->draw_binding, &src_rect,
1681 dst_surface, dst_surface->draw_binding, &dst_rect);
1682 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1686 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1687 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1688 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1690 TRACE("Using arbfp blit.\n");
1692 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1700 /* Special cases for render targets. */
1701 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1702 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1704 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1705 src_surface, &src_rect, flags, fx, filter)))
1711 /* For the rest call the X11 surface implementation. For render targets
1712 * this should be implemented OpenGL accelerated in BltOverride, other
1713 * blits are rather rare. */
1714 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1717 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1718 struct wined3d_surface *render_target)
1720 TRACE("surface %p, render_target %p.\n", surface, render_target);
1722 /* TODO: Check surface sizes, pools, etc. */
1724 if (render_target->resource.multisample_type)
1725 return WINED3DERR_INVALIDCALL;
1727 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1730 /* Context activation is done by the caller. */
1731 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1733 if (surface->flags & SFLAG_DIBSECTION)
1735 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1739 if (!surface->resource.heapMemory)
1740 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1741 else if (!(surface->flags & SFLAG_CLIENT))
1742 ERR("Surface %p has heapMemory %p and flags %#x.\n",
1743 surface, surface->resource.heapMemory, surface->flags);
1745 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1746 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1750 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1751 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1752 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1753 surface->resource.size, surface->resource.allocatedMemory));
1754 checkGLcall("glGetBufferSubDataARB");
1755 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1756 checkGLcall("glDeleteBuffersARB");
1760 surface->flags &= ~SFLAG_PBO;
1763 /* Do not call while under the GL lock. */
1764 static void surface_unload(struct wined3d_resource *resource)
1766 struct wined3d_surface *surface = surface_from_resource(resource);
1767 struct wined3d_renderbuffer_entry *entry, *entry2;
1768 struct wined3d_device *device = resource->device;
1769 const struct wined3d_gl_info *gl_info;
1770 struct wined3d_context *context;
1772 TRACE("surface %p.\n", surface);
1774 if (resource->pool == WINED3D_POOL_DEFAULT)
1776 /* Default pool resources are supposed to be destroyed before Reset is called.
1777 * Implicit resources stay however. So this means we have an implicit render target
1778 * or depth stencil. The content may be destroyed, but we still have to tear down
1779 * opengl resources, so we cannot leave early.
1781 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1782 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1783 * or the depth stencil into an FBO the texture or render buffer will be removed
1784 * and all flags get lost
1786 if (!(surface->flags & SFLAG_PBO))
1787 surface_init_sysmem(surface);
1788 /* We also get here when the ddraw swapchain is destroyed, for example
1789 * for a mode switch. In this case this surface won't necessarily be
1790 * an implicit surface. We have to mark it lost so that the
1791 * application can restore it after the mode switch. */
1792 surface->flags |= SFLAG_LOST;
1796 /* Load the surface into system memory */
1797 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1798 surface_modify_location(surface, surface->draw_binding, FALSE);
1800 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1801 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1802 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1804 context = context_acquire(device, NULL);
1805 gl_info = context->gl_info;
1807 /* Destroy PBOs, but load them into real sysmem before */
1808 if (surface->flags & SFLAG_PBO)
1809 surface_remove_pbo(surface, gl_info);
1811 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1812 * all application-created targets the application has to release the surface
1813 * before calling _Reset
1815 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1818 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1820 list_remove(&entry->entry);
1821 HeapFree(GetProcessHeap(), 0, entry);
1823 list_init(&surface->renderbuffers);
1824 surface->current_renderbuffer = NULL;
1828 /* If we're in a texture, the texture name belongs to the texture.
1829 * Otherwise, destroy it. */
1830 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1832 glDeleteTextures(1, &surface->texture_name);
1833 surface->texture_name = 0;
1834 glDeleteTextures(1, &surface->texture_name_srgb);
1835 surface->texture_name_srgb = 0;
1837 if (surface->rb_multisample)
1839 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1840 surface->rb_multisample = 0;
1842 if (surface->rb_resolved)
1844 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1845 surface->rb_resolved = 0;
1850 context_release(context);
1852 resource_unload(resource);
1855 static const struct wined3d_resource_ops surface_resource_ops =
1860 static const struct wined3d_surface_ops surface_ops =
1862 surface_private_setup,
1863 surface_realize_palette,
1868 /*****************************************************************************
1869 * Initializes the GDI surface, aka creates the DIB section we render to
1870 * The DIB section creation is done by calling GetDC, which will create the
1871 * section and releasing the dc to allow the app to use it. The dib section
1872 * will stay until the surface is released
1874 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1875 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1876 * avoid confusion in the shared surface code.
1879 * WINED3D_OK on success
1880 * The return values of called methods on failure
1882 *****************************************************************************/
1883 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1887 TRACE("surface %p.\n", surface);
1889 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1891 ERR("Overlays not yet supported by GDI surfaces.\n");
1892 return WINED3DERR_INVALIDCALL;
1895 /* Sysmem textures have memory already allocated - release it,
1896 * this avoids an unnecessary memcpy. */
1897 hr = surface_create_dib_section(surface);
1900 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1901 surface->resource.heapMemory = NULL;
1902 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1905 /* We don't mind the nonpow2 stuff in GDI. */
1906 surface->pow2Width = surface->resource.width;
1907 surface->pow2Height = surface->resource.height;
1912 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1914 struct wined3d_palette *palette = surface->palette;
1916 TRACE("surface %p.\n", surface);
1918 if (!palette) return;
1920 if (surface->flags & SFLAG_DIBSECTION)
1925 TRACE("Updating the DC's palette.\n");
1927 for (i = 0; i < 256; ++i)
1929 col[i].rgbRed = palette->palents[i].peRed;
1930 col[i].rgbGreen = palette->palents[i].peGreen;
1931 col[i].rgbBlue = palette->palents[i].peBlue;
1932 col[i].rgbReserved = 0;
1934 SetDIBColorTable(surface->hDC, 0, 256, col);
1937 /* Update the image because of the palette change. Some games like e.g.
1938 * Red Alert call SetEntries a lot to implement fading. */
1939 /* Tell the swapchain to update the screen. */
1940 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1942 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1943 if (surface == swapchain->front_buffer)
1945 x11_copy_to_screen(swapchain, NULL);
1950 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
1952 TRACE("surface %p, rect %s, flags %#x.\n",
1953 surface, wine_dbgstr_rect(rect), flags);
1955 if (!(surface->flags & SFLAG_DIBSECTION))
1959 /* This happens on gdi surfaces if the application set a user pointer
1960 * and resets it. Recreate the DIB section. */
1961 if (FAILED(hr = surface_create_dib_section(surface)))
1963 ERR("Failed to create dib section, hr %#x.\n", hr);
1966 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1967 surface->resource.heapMemory = NULL;
1968 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1972 static void gdi_surface_unmap(struct wined3d_surface *surface)
1974 TRACE("surface %p.\n", surface);
1976 /* Tell the swapchain to update the screen. */
1977 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1979 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1980 if (surface == swapchain->front_buffer)
1982 x11_copy_to_screen(swapchain, &surface->lockedRect);
1986 memset(&surface->lockedRect, 0, sizeof(RECT));
1989 static const struct wined3d_surface_ops gdi_surface_ops =
1991 gdi_surface_private_setup,
1992 gdi_surface_realize_palette,
1997 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2002 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2006 name = &surface->texture_name_srgb;
2007 flag = SFLAG_INSRGBTEX;
2011 name = &surface->texture_name;
2012 flag = SFLAG_INTEXTURE;
2015 if (!*name && new_name)
2017 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2018 * surface has no texture name yet. See if we can get rid of this. */
2019 if (surface->flags & flag)
2021 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2022 surface_modify_location(surface, flag, FALSE);
2027 surface_force_reload(surface);
2030 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2032 TRACE("surface %p, target %#x.\n", surface, target);
2034 if (surface->texture_target != target)
2036 if (target == GL_TEXTURE_RECTANGLE_ARB)
2038 surface->flags &= ~SFLAG_NORMCOORD;
2040 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2042 surface->flags |= SFLAG_NORMCOORD;
2045 surface->texture_target = target;
2046 surface_force_reload(surface);
2049 /* Context activation is done by the caller. */
2050 void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
2052 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
2054 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2056 struct wined3d_texture *texture = surface->container.u.texture;
2058 TRACE("Passing to container (%p).\n", texture);
2059 texture->texture_ops->texture_bind(texture, context, srgb);
2063 if (surface->texture_level)
2065 ERR("Standalone surface %p is non-zero texture level %u.\n",
2066 surface, surface->texture_level);
2070 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2074 if (!surface->texture_name)
2076 glGenTextures(1, &surface->texture_name);
2077 checkGLcall("glGenTextures");
2079 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2081 context_bind_texture(context, surface->texture_target, surface->texture_name);
2082 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2083 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2084 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2085 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2086 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2087 checkGLcall("glTexParameteri");
2091 context_bind_texture(context, surface->texture_target, surface->texture_name);
2098 /* This call just downloads data, the caller is responsible for binding the
2099 * correct texture. */
2100 /* Context activation is done by the caller. */
2101 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2103 const struct wined3d_format *format = surface->resource.format;
2105 /* Only support read back of converted P8 surfaces. */
2106 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2108 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
2114 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2116 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2117 surface, surface->texture_level, format->glFormat, format->glType,
2118 surface->resource.allocatedMemory);
2120 if (surface->flags & SFLAG_PBO)
2122 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2123 checkGLcall("glBindBufferARB");
2124 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2125 checkGLcall("glGetCompressedTexImageARB");
2126 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2127 checkGLcall("glBindBufferARB");
2131 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2132 surface->texture_level, surface->resource.allocatedMemory));
2133 checkGLcall("glGetCompressedTexImageARB");
2141 GLenum gl_format = format->glFormat;
2142 GLenum gl_type = format->glType;
2146 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2147 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2149 gl_format = GL_ALPHA;
2150 gl_type = GL_UNSIGNED_BYTE;
2153 if (surface->flags & SFLAG_NONPOW2)
2155 unsigned char alignment = surface->resource.device->surface_alignment;
2156 src_pitch = format->byte_count * surface->pow2Width;
2157 dst_pitch = wined3d_surface_get_pitch(surface);
2158 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2159 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2163 mem = surface->resource.allocatedMemory;
2166 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2167 surface, surface->texture_level, gl_format, gl_type, mem);
2169 if (surface->flags & SFLAG_PBO)
2171 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2172 checkGLcall("glBindBufferARB");
2174 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2175 checkGLcall("glGetTexImage");
2177 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2178 checkGLcall("glBindBufferARB");
2182 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2183 checkGLcall("glGetTexImage");
2187 if (surface->flags & SFLAG_NONPOW2)
2189 const BYTE *src_data;
2193 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2194 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2195 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2197 * We're doing this...
2199 * instead of boxing the texture :
2200 * |<-texture width ->| -->pow2width| /\
2201 * |111111111111111111| | |
2202 * |222 Texture 222222| boxed empty | texture height
2203 * |3333 Data 33333333| | |
2204 * |444444444444444444| | \/
2205 * ----------------------------------- |
2206 * | boxed empty | boxed empty | pow2height
2208 * -----------------------------------
2211 * we're repacking the data to the expected texture width
2213 * |<-texture width ->| -->pow2width| /\
2214 * |111111111111111111222222222222222| |
2215 * |222333333333333333333444444444444| texture height
2219 * | empty | pow2height
2221 * -----------------------------------
2225 * |<-texture width ->| /\
2226 * |111111111111111111|
2227 * |222222222222222222|texture height
2228 * |333333333333333333|
2229 * |444444444444444444| \/
2230 * --------------------
2232 * this also means that any references to allocatedMemory should work with the data as if were a
2233 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2235 * internally the texture is still stored in a boxed format so any references to textureName will
2236 * get a boxed texture with width pow2width and not a texture of width resource.width.
2238 * Performance should not be an issue, because applications normally do not lock the surfaces when
2239 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2240 * and doesn't have to be re-read. */
2242 dst_data = surface->resource.allocatedMemory;
2243 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2244 for (y = 1; y < surface->resource.height; ++y)
2246 /* skip the first row */
2247 src_data += src_pitch;
2248 dst_data += dst_pitch;
2249 memcpy(dst_data, src_data, dst_pitch);
2252 HeapFree(GetProcessHeap(), 0, mem);
2256 /* Surface has now been downloaded */
2257 surface->flags |= SFLAG_INSYSMEM;
2260 /* This call just uploads data, the caller is responsible for binding the
2261 * correct texture. */
2262 /* Context activation is done by the caller. */
2263 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2264 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
2265 BOOL srgb, const struct wined3d_bo_address *data)
2267 UINT update_w = src_rect->right - src_rect->left;
2268 UINT update_h = src_rect->bottom - src_rect->top;
2270 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
2271 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
2272 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2274 if (surface->flags & SFLAG_LOCKED)
2276 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
2277 surface->flags |= SFLAG_PIN_SYSMEM;
2280 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
2282 update_h *= format->height_scale.numerator;
2283 update_h /= format->height_scale.denominator;
2288 if (data->buffer_object)
2290 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2291 checkGLcall("glBindBufferARB");
2294 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2296 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2297 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2298 const BYTE *addr = data->addr;
2301 addr += (src_rect->top / format->block_height) * src_pitch;
2302 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2305 internal = format->glGammaInternal;
2306 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2307 internal = format->rtInternal;
2309 internal = format->glInternal;
2311 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2312 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2313 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2315 if (row_length == src_pitch)
2317 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2318 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2324 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2325 * can't use the unpack row length like below. */
2326 for (row = 0, y = dst_point->y; row < row_count; ++row)
2328 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2329 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2330 y += format->block_height;
2334 checkGLcall("glCompressedTexSubImage2DARB");
2338 const BYTE *addr = data->addr;
2340 addr += src_rect->top * src_pitch;
2341 addr += src_rect->left * format->byte_count;
2343 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2344 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2345 update_w, update_h, format->glFormat, format->glType, addr);
2347 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
2348 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2349 update_w, update_h, format->glFormat, format->glType, addr);
2350 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2351 checkGLcall("glTexSubImage2D");
2354 if (data->buffer_object)
2356 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2357 checkGLcall("glBindBufferARB");
2362 if (wined3d_settings.strict_draw_ordering)
2365 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2367 struct wined3d_device *device = surface->resource.device;
2370 for (i = 0; i < device->context_count; ++i)
2372 context_surface_update(device->contexts[i], surface);
2377 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
2378 struct wined3d_surface *src_surface, const RECT *src_rect)
2380 const struct wined3d_format *src_format;
2381 const struct wined3d_format *dst_format;
2382 const struct wined3d_gl_info *gl_info;
2383 enum wined3d_conversion_type convert;
2384 struct wined3d_context *context;
2385 struct wined3d_bo_address data;
2386 struct wined3d_format format;
2387 UINT update_w, update_h;
2394 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
2395 dst_surface, wine_dbgstr_point(dst_point),
2396 src_surface, wine_dbgstr_rect(src_rect));
2398 src_format = src_surface->resource.format;
2399 dst_format = dst_surface->resource.format;
2401 if (src_format->id != dst_format->id)
2403 WARN("Source and destination surfaces should have the same format.\n");
2404 return WINED3DERR_INVALIDCALL;
2413 else if (dst_point->x < 0 || dst_point->y < 0)
2415 WARN("Invalid destination point.\n");
2416 return WINED3DERR_INVALIDCALL;
2423 r.right = src_surface->resource.width;
2424 r.bottom = src_surface->resource.height;
2427 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
2428 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
2430 WARN("Invalid source rectangle.\n");
2431 return WINED3DERR_INVALIDCALL;
2434 src_w = src_surface->resource.width;
2435 src_h = src_surface->resource.height;
2437 dst_w = dst_surface->resource.width;
2438 dst_h = dst_surface->resource.height;
2440 update_w = src_rect->right - src_rect->left;
2441 update_h = src_rect->bottom - src_rect->top;
2443 if (update_w > dst_w || dst_point->x > dst_w - update_w
2444 || update_h > dst_h || dst_point->y > dst_h - update_h)
2446 WARN("Destination out of bounds.\n");
2447 return WINED3DERR_INVALIDCALL;
2450 /* NPOT block sizes would be silly. */
2451 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS)
2452 && ((update_w & (src_format->block_width - 1) || update_h & (src_format->block_height - 1))
2453 && (src_w != update_w || dst_w != update_w || src_h != update_h || dst_h != update_h)))
2455 WARN("Update rect not block-aligned.\n");
2456 return WINED3DERR_INVALIDCALL;
2459 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
2460 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
2461 if (convert != WINED3D_CT_NONE || format.convert)
2463 RECT dst_rect = {dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h};
2464 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
2467 context = context_acquire(dst_surface->resource.device, NULL);
2468 gl_info = context->gl_info;
2470 /* Only load the surface for partial updates. For newly allocated texture
2471 * the texture wouldn't be the current location, and we'd upload zeroes
2472 * just to overwrite them again. */
2473 if (update_w == dst_w && update_h == dst_h)
2474 surface_prepare_texture(dst_surface, context, FALSE);
2476 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
2477 surface_bind(dst_surface, context, FALSE);
2479 data.buffer_object = src_surface->pbo;
2480 data.addr = src_surface->resource.allocatedMemory;
2481 src_pitch = wined3d_surface_get_pitch(src_surface);
2483 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
2485 invalidate_active_texture(dst_surface->resource.device, context);
2487 context_release(context);
2489 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
2493 /* This call just allocates the texture, the caller is responsible for binding
2494 * the correct texture. */
2495 /* Context activation is done by the caller. */
2496 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2497 const struct wined3d_format *format, BOOL srgb)
2499 BOOL enable_client_storage = FALSE;
2500 GLsizei width = surface->pow2Width;
2501 GLsizei height = surface->pow2Height;
2502 const BYTE *mem = NULL;
2507 internal = format->glGammaInternal;
2509 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2511 internal = format->rtInternal;
2515 internal = format->glInternal;
2518 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
2520 height *= format->height_scale.numerator;
2521 height /= format->height_scale.denominator;
2524 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",
2525 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2526 internal, width, height, format->glFormat, format->glType);
2530 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2532 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2533 || !surface->resource.allocatedMemory)
2535 /* In some cases we want to disable client storage.
2536 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2537 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2538 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2539 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2541 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2542 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2543 surface->flags &= ~SFLAG_CLIENT;
2544 enable_client_storage = TRUE;
2548 surface->flags |= SFLAG_CLIENT;
2550 /* Point OpenGL to our allocated texture memory. Do not use
2551 * resource.allocatedMemory here because it might point into a
2552 * PBO. Instead use heapMemory, but get the alignment right. */
2553 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2554 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2558 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2560 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2561 internal, width, height, 0, surface->resource.size, mem));
2562 checkGLcall("glCompressedTexImage2DARB");
2566 glTexImage2D(surface->texture_target, surface->texture_level,
2567 internal, width, height, 0, format->glFormat, format->glType, mem);
2568 checkGLcall("glTexImage2D");
2571 if(enable_client_storage) {
2572 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2573 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2578 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2579 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2580 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2581 /* GL locking is done by the caller */
2582 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2584 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2585 struct wined3d_renderbuffer_entry *entry;
2586 GLuint renderbuffer = 0;
2587 unsigned int src_width, src_height;
2588 unsigned int width, height;
2590 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2592 width = rt->pow2Width;
2593 height = rt->pow2Height;
2597 width = surface->pow2Width;
2598 height = surface->pow2Height;
2601 src_width = surface->pow2Width;
2602 src_height = surface->pow2Height;
2604 /* A depth stencil smaller than the render target is not valid */
2605 if (width > src_width || height > src_height) return;
2607 /* Remove any renderbuffer set if the sizes match */
2608 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2609 || (width == src_width && height == src_height))
2611 surface->current_renderbuffer = NULL;
2615 /* Look if we've already got a renderbuffer of the correct dimensions */
2616 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2618 if (entry->width == width && entry->height == height)
2620 renderbuffer = entry->id;
2621 surface->current_renderbuffer = entry;
2628 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2629 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2630 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2631 surface->resource.format->glInternal, width, height);
2633 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2634 entry->width = width;
2635 entry->height = height;
2636 entry->id = renderbuffer;
2637 list_add_head(&surface->renderbuffers, &entry->entry);
2639 surface->current_renderbuffer = entry;
2642 checkGLcall("set_compatible_renderbuffer");
2645 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2647 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2649 TRACE("surface %p.\n", surface);
2651 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2653 ERR("Surface %p is not on a swapchain.\n", surface);
2657 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2659 if (swapchain->render_to_fbo)
2661 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2662 return GL_COLOR_ATTACHMENT0;
2664 TRACE("Returning GL_BACK\n");
2667 else if (surface == swapchain->front_buffer)
2669 TRACE("Returning GL_FRONT\n");
2673 FIXME("Higher back buffer, returning GL_BACK\n");
2677 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2678 void surface_add_dirty_rect(struct wined3d_surface *surface, const struct wined3d_box *dirty_rect)
2680 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2682 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2683 /* No partial locking for textures yet. */
2684 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2686 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2689 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->left);
2690 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->top);
2691 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->right);
2692 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->bottom);
2696 surface->dirtyRect.left = 0;
2697 surface->dirtyRect.top = 0;
2698 surface->dirtyRect.right = surface->resource.width;
2699 surface->dirtyRect.bottom = surface->resource.height;
2702 /* if the container is a texture then mark it dirty. */
2703 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2705 TRACE("Passing to container.\n");
2706 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2710 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2712 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2715 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2717 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
2719 ERR("Not supported on scratch surfaces.\n");
2720 return WINED3DERR_INVALIDCALL;
2723 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2725 /* Reload if either the texture and sysmem have different ideas about the
2726 * color key, or the actual key values changed. */
2727 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2728 && (surface->gl_color_key.color_space_low_value != surface->src_blt_color_key.color_space_low_value
2729 || surface->gl_color_key.color_space_high_value != surface->src_blt_color_key.color_space_high_value)))
2731 TRACE("Reloading because of color keying\n");
2732 /* To perform the color key conversion we need a sysmem copy of
2733 * the surface. Make sure we have it. */
2735 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2736 /* Make sure the texture is reloaded because of the color key change,
2737 * this kills performance though :( */
2738 /* TODO: This is not necessarily needed with hw palettized texture support. */
2739 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2740 /* Switching color keying on / off may change the internal format. */
2742 surface_force_reload(surface);
2744 else if (!(surface->flags & flag))
2746 TRACE("Reloading because surface is dirty.\n");
2750 TRACE("surface is already in texture\n");
2754 /* No partial locking for textures yet. */
2755 surface_load_location(surface, flag, NULL);
2756 surface_evict_sysmem(surface);
2761 /* See also float_16_to_32() in wined3d_private.h */
2762 static inline unsigned short float_32_to_16(const float *in)
2765 float tmp = fabsf(*in);
2766 unsigned int mantissa;
2769 /* Deal with special numbers */
2775 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2777 if (tmp < powf(2, 10))
2783 } while (tmp < powf(2, 10));
2785 else if (tmp >= powf(2, 11))
2791 } while (tmp >= powf(2, 11));
2794 mantissa = (unsigned int)tmp;
2795 if (tmp - mantissa >= 0.5f)
2796 ++mantissa; /* Round to nearest, away from zero. */
2798 exp += 10; /* Normalize the mantissa. */
2799 exp += 15; /* Exponent is encoded with excess 15. */
2801 if (exp > 30) /* too big */
2803 ret = 0x7c00; /* INF */
2807 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2810 mantissa = mantissa >> 1;
2813 ret = mantissa & 0x3ff;
2817 ret = (exp << 10) | (mantissa & 0x3ff);
2820 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2824 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2828 TRACE("Surface %p, container %p of type %#x.\n",
2829 surface, surface->container.u.base, surface->container.type);
2831 switch (surface->container.type)
2833 case WINED3D_CONTAINER_TEXTURE:
2834 return wined3d_texture_incref(surface->container.u.texture);
2836 case WINED3D_CONTAINER_SWAPCHAIN:
2837 return wined3d_swapchain_incref(surface->container.u.swapchain);
2840 ERR("Unhandled container type %#x.\n", surface->container.type);
2841 case WINED3D_CONTAINER_NONE:
2845 refcount = InterlockedIncrement(&surface->resource.ref);
2846 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2851 /* Do not call while under the GL lock. */
2852 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2856 TRACE("Surface %p, container %p of type %#x.\n",
2857 surface, surface->container.u.base, surface->container.type);
2859 switch (surface->container.type)
2861 case WINED3D_CONTAINER_TEXTURE:
2862 return wined3d_texture_decref(surface->container.u.texture);
2864 case WINED3D_CONTAINER_SWAPCHAIN:
2865 return wined3d_swapchain_decref(surface->container.u.swapchain);
2868 ERR("Unhandled container type %#x.\n", surface->container.type);
2869 case WINED3D_CONTAINER_NONE:
2873 refcount = InterlockedDecrement(&surface->resource.ref);
2874 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2878 surface_cleanup(surface);
2879 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2881 TRACE("Destroyed surface %p.\n", surface);
2882 HeapFree(GetProcessHeap(), 0, surface);
2888 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2890 return resource_set_priority(&surface->resource, priority);
2893 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2895 return resource_get_priority(&surface->resource);
2898 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2900 TRACE("surface %p.\n", surface);
2902 if (!surface->resource.device->d3d_initialized)
2904 ERR("D3D not initialized.\n");
2908 surface_internal_preload(surface, SRGB_ANY);
2911 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2913 TRACE("surface %p.\n", surface);
2915 return surface->resource.parent;
2918 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2920 TRACE("surface %p.\n", surface);
2922 return &surface->resource;
2925 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2927 TRACE("surface %p, flags %#x.\n", surface, flags);
2931 case WINEDDGBS_CANBLT:
2932 case WINEDDGBS_ISBLTDONE:
2936 return WINED3DERR_INVALIDCALL;
2940 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2942 TRACE("surface %p, flags %#x.\n", surface, flags);
2944 /* XXX: DDERR_INVALIDSURFACETYPE */
2948 case WINEDDGFS_CANFLIP:
2949 case WINEDDGFS_ISFLIPDONE:
2953 return WINED3DERR_INVALIDCALL;
2957 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2959 TRACE("surface %p.\n", surface);
2961 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2962 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2965 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2967 TRACE("surface %p.\n", surface);
2969 surface->flags &= ~SFLAG_LOST;
2973 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2975 TRACE("surface %p, palette %p.\n", surface, palette);
2977 if (surface->palette == palette)
2979 TRACE("Nop palette change.\n");
2983 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2984 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2986 surface->palette = palette;
2990 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2991 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
2993 surface->surface_ops->surface_realize_palette(surface);
2999 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
3000 DWORD flags, const struct wined3d_color_key *color_key)
3002 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3004 if (flags & WINEDDCKEY_COLORSPACE)
3006 FIXME(" colorkey value not supported (%08x) !\n", flags);
3007 return WINED3DERR_INVALIDCALL;
3010 /* Dirtify the surface, but only if a key was changed. */
3013 switch (flags & ~WINEDDCKEY_COLORSPACE)
3015 case WINEDDCKEY_DESTBLT:
3016 surface->dst_blt_color_key = *color_key;
3017 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3020 case WINEDDCKEY_DESTOVERLAY:
3021 surface->dst_overlay_color_key = *color_key;
3022 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3025 case WINEDDCKEY_SRCOVERLAY:
3026 surface->src_overlay_color_key = *color_key;
3027 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3030 case WINEDDCKEY_SRCBLT:
3031 surface->src_blt_color_key = *color_key;
3032 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3038 switch (flags & ~WINEDDCKEY_COLORSPACE)
3040 case WINEDDCKEY_DESTBLT:
3041 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3044 case WINEDDCKEY_DESTOVERLAY:
3045 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3048 case WINEDDCKEY_SRCOVERLAY:
3049 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3052 case WINEDDCKEY_SRCBLT:
3053 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3061 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3063 TRACE("surface %p.\n", surface);
3065 return surface->palette;
3068 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3070 const struct wined3d_format *format = surface->resource.format;
3073 TRACE("surface %p.\n", surface);
3075 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
3077 /* Since compressed formats are block based, pitch means the amount of
3078 * bytes to the next row of block rather than the next row of pixels. */
3079 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3080 pitch = row_block_count * format->block_byte_count;
3084 unsigned char alignment = surface->resource.device->surface_alignment;
3085 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3086 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3089 TRACE("Returning %u.\n", pitch);
3094 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3096 TRACE("surface %p, mem %p.\n", surface, mem);
3098 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3100 WARN("Surface is locked or the DC is in use.\n");
3101 return WINED3DERR_INVALIDCALL;
3104 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
3105 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3107 ERR("Not supported on render targets.\n");
3108 return WINED3DERR_INVALIDCALL;
3111 if (mem && mem != surface->resource.allocatedMemory)
3113 void *release = NULL;
3115 /* Do I have to copy the old surface content? */
3116 if (surface->flags & SFLAG_DIBSECTION)
3118 DeleteDC(surface->hDC);
3119 DeleteObject(surface->dib.DIBsection);
3120 surface->dib.bitmap_data = NULL;
3121 surface->resource.allocatedMemory = NULL;
3122 surface->hDC = NULL;
3123 surface->flags &= ~SFLAG_DIBSECTION;
3125 else if (!(surface->flags & SFLAG_USERPTR))
3127 release = surface->resource.heapMemory;
3128 surface->resource.heapMemory = NULL;
3130 surface->resource.allocatedMemory = mem;
3131 surface->flags |= SFLAG_USERPTR;
3133 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
3134 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3136 /* For client textures OpenGL has to be notified. */
3137 if (surface->flags & SFLAG_CLIENT)
3138 surface_release_client_storage(surface);
3140 /* Now free the old memory if any. */
3141 HeapFree(GetProcessHeap(), 0, release);
3143 else if (surface->flags & SFLAG_USERPTR)
3145 /* HeapMemory should be NULL already. */
3146 if (surface->resource.heapMemory)
3147 ERR("User pointer surface has heap memory allocated.\n");
3151 surface->resource.allocatedMemory = NULL;
3152 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
3154 if (surface->flags & SFLAG_CLIENT)
3155 surface_release_client_storage(surface);
3157 surface_prepare_system_memory(surface);
3160 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3166 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3170 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3172 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3174 WARN("Not an overlay surface.\n");
3175 return WINEDDERR_NOTAOVERLAYSURFACE;
3178 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3179 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3180 surface->overlay_destrect.left = x;
3181 surface->overlay_destrect.top = y;
3182 surface->overlay_destrect.right = x + w;
3183 surface->overlay_destrect.bottom = y + h;
3185 surface_draw_overlay(surface);
3190 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3192 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3194 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3196 TRACE("Not an overlay surface.\n");
3197 return WINEDDERR_NOTAOVERLAYSURFACE;
3200 if (!surface->overlay_dest)
3202 TRACE("Overlay not visible.\n");
3205 return WINEDDERR_OVERLAYNOTVISIBLE;
3208 *x = surface->overlay_destrect.left;
3209 *y = surface->overlay_destrect.top;
3211 TRACE("Returning position %d, %d.\n", *x, *y);
3216 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3217 DWORD flags, struct wined3d_surface *ref)
3219 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3221 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3223 TRACE("Not an overlay surface.\n");
3224 return WINEDDERR_NOTAOVERLAYSURFACE;
3230 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3231 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3233 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3234 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3236 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3238 WARN("Not an overlay surface.\n");
3239 return WINEDDERR_NOTAOVERLAYSURFACE;
3241 else if (!dst_surface)
3243 WARN("Dest surface is NULL.\n");
3244 return WINED3DERR_INVALIDCALL;
3249 surface->overlay_srcrect = *src_rect;
3253 surface->overlay_srcrect.left = 0;
3254 surface->overlay_srcrect.top = 0;
3255 surface->overlay_srcrect.right = surface->resource.width;
3256 surface->overlay_srcrect.bottom = surface->resource.height;
3261 surface->overlay_destrect = *dst_rect;
3265 surface->overlay_destrect.left = 0;
3266 surface->overlay_destrect.top = 0;
3267 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3268 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3271 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3273 surface->overlay_dest = NULL;
3274 list_remove(&surface->overlay_entry);
3277 if (flags & WINEDDOVER_SHOW)
3279 if (surface->overlay_dest != dst_surface)
3281 surface->overlay_dest = dst_surface;
3282 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3285 else if (flags & WINEDDOVER_HIDE)
3287 /* tests show that the rectangles are erased on hide */
3288 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3289 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3290 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3291 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3292 surface->overlay_dest = NULL;
3295 surface_draw_overlay(surface);
3300 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
3301 UINT width, UINT height, enum wined3d_format_id format_id,
3302 enum wined3d_multisample_type multisample_type, UINT multisample_quality)
3304 struct wined3d_device *device = surface->resource.device;
3305 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3306 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
3307 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height);
3309 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u.\n",
3310 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type);
3313 return WINED3DERR_INVALIDCALL;
3315 if (device->d3d_initialized)
3316 surface->resource.resource_ops->resource_unload(&surface->resource);
3318 if (surface->flags & SFLAG_DIBSECTION)
3320 DeleteDC(surface->hDC);
3321 DeleteObject(surface->dib.DIBsection);
3322 surface->dib.bitmap_data = NULL;
3323 surface->flags &= ~SFLAG_DIBSECTION;
3326 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_USERPTR);
3327 surface->resource.allocatedMemory = NULL;
3328 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
3329 surface->resource.heapMemory = NULL;
3331 surface->resource.width = width;
3332 surface->resource.height = height;
3333 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
3334 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
3336 surface->pow2Width = width;
3337 surface->pow2Height = height;
3341 surface->pow2Width = surface->pow2Height = 1;
3342 while (surface->pow2Width < width)
3343 surface->pow2Width <<= 1;
3344 while (surface->pow2Height < height)
3345 surface->pow2Height <<= 1;
3348 if (surface->pow2Width != width || surface->pow2Height != height)
3349 surface->flags |= SFLAG_NONPOW2;
3351 surface->flags &= ~SFLAG_NONPOW2;
3353 surface->resource.format = format;
3354 surface->resource.multisample_type = multisample_type;
3355 surface->resource.multisample_quality = multisample_quality;
3356 surface->resource.size = resource_size;
3358 if (!surface_init_sysmem(surface))
3359 return E_OUTOFMEMORY;
3364 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3365 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3367 unsigned short *dst_s;
3371 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3373 for (y = 0; y < h; ++y)
3375 src_f = (const float *)(src + y * pitch_in);
3376 dst_s = (unsigned short *) (dst + y * pitch_out);
3377 for (x = 0; x < w; ++x)
3379 dst_s[x] = float_32_to_16(src_f + x);
3384 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3385 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3387 static const unsigned char convert_5to8[] =
3389 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3390 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3391 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3392 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3394 static const unsigned char convert_6to8[] =
3396 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3397 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3398 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3399 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3400 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3401 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3402 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3403 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
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 WORD *src_line = (const WORD *)(src + y * pitch_in);
3412 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3413 for (x = 0; x < w; ++x)
3415 WORD pixel = src_line[x];
3416 dst_line[x] = 0xff000000
3417 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3418 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3419 | convert_5to8[(pixel & 0x001f)];
3424 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3425 * in both cases we're just setting the X / Alpha channel to 0xff. */
3426 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3427 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3431 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3433 for (y = 0; y < h; ++y)
3435 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3436 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3438 for (x = 0; x < w; ++x)
3440 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3445 static inline BYTE cliptobyte(int x)
3447 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3450 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3451 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3453 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3456 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3458 for (y = 0; y < h; ++y)
3460 const BYTE *src_line = src + y * pitch_in;
3461 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3462 for (x = 0; x < w; ++x)
3464 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3465 * C = Y - 16; D = U - 128; E = V - 128;
3466 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3467 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3468 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3469 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3470 * U and V are shared between the pixels. */
3471 if (!(x & 1)) /* For every even pixel, read new U and V. */
3473 d = (int) src_line[1] - 128;
3474 e = (int) src_line[3] - 128;
3476 g2 = - 100 * d - 208 * e + 128;
3479 c2 = 298 * ((int) src_line[0] - 16);
3480 dst_line[x] = 0xff000000
3481 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3482 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3483 | cliptobyte((c2 + b2) >> 8); /* blue */
3484 /* Scale RGB values to 0..255 range,
3485 * then clip them if still not in range (may be negative),
3486 * then shift them within DWORD if necessary. */
3492 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3493 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3496 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3498 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3500 for (y = 0; y < h; ++y)
3502 const BYTE *src_line = src + y * pitch_in;
3503 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3504 for (x = 0; x < w; ++x)
3506 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3507 * C = Y - 16; D = U - 128; E = V - 128;
3508 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3509 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3510 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3511 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3512 * U and V are shared between the pixels. */
3513 if (!(x & 1)) /* For every even pixel, read new U and V. */
3515 d = (int) src_line[1] - 128;
3516 e = (int) src_line[3] - 128;
3518 g2 = - 100 * d - 208 * e + 128;
3521 c2 = 298 * ((int) src_line[0] - 16);
3522 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3523 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3524 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3525 /* Scale RGB values to 0..255 range,
3526 * then clip them if still not in range (may be negative),
3527 * then shift them within DWORD if necessary. */
3533 struct d3dfmt_convertor_desc
3535 enum wined3d_format_id from, to;
3536 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3539 static const struct d3dfmt_convertor_desc convertors[] =
3541 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3542 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3543 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3544 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3545 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3546 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3549 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3550 enum wined3d_format_id to)
3554 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3556 if (convertors[i].from == from && convertors[i].to == to)
3557 return &convertors[i];
3563 /*****************************************************************************
3564 * surface_convert_format
3566 * Creates a duplicate of a surface in a different format. Is used by Blt to
3567 * blit between surfaces with different formats.
3570 * source: Source surface
3571 * fmt: Requested destination format
3573 *****************************************************************************/
3574 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3576 struct wined3d_mapped_rect src_map, dst_map;
3577 const struct d3dfmt_convertor_desc *conv;
3578 struct wined3d_surface *ret = NULL;
3581 conv = find_convertor(source->resource.format->id, to_fmt);
3584 FIXME("Cannot find a conversion function from format %s to %s.\n",
3585 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3589 wined3d_surface_create(source->resource.device, source->resource.width,
3590 source->resource.height, to_fmt, 0 /* level */, 0 /* usage */, WINED3D_POOL_SCRATCH,
3591 WINED3D_MULTISAMPLE_NONE /* TODO: Multisampled conversion */, 0 /* MultiSampleQuality */,
3592 source->surface_type, WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD,
3593 NULL /* parent */, &wined3d_null_parent_ops, &ret);
3596 ERR("Failed to create a destination surface for conversion.\n");
3600 memset(&src_map, 0, sizeof(src_map));
3601 memset(&dst_map, 0, sizeof(dst_map));
3603 hr = wined3d_surface_map(source, &src_map, NULL, WINED3DLOCK_READONLY);
3606 ERR("Failed to lock the source surface.\n");
3607 wined3d_surface_decref(ret);
3610 hr = wined3d_surface_map(ret, &dst_map, NULL, WINED3DLOCK_READONLY);
3613 ERR("Failed to lock the destination surface.\n");
3614 wined3d_surface_unmap(source);
3615 wined3d_surface_decref(ret);
3619 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
3620 source->resource.width, source->resource.height);
3622 wined3d_surface_unmap(ret);
3623 wined3d_surface_unmap(source);
3628 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3629 unsigned int bpp, UINT pitch, DWORD color)
3636 #define COLORFILL_ROW(type) \
3638 type *d = (type *)buf; \
3639 for (x = 0; x < width; ++x) \
3640 d[x] = (type)color; \
3646 COLORFILL_ROW(BYTE);
3650 COLORFILL_ROW(WORD);
3656 for (x = 0; x < width; ++x, d += 3)
3658 d[0] = (color ) & 0xFF;
3659 d[1] = (color >> 8) & 0xFF;
3660 d[2] = (color >> 16) & 0xFF;
3665 COLORFILL_ROW(DWORD);
3669 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3670 return WINED3DERR_NOTAVAILABLE;
3673 #undef COLORFILL_ROW
3675 /* Now copy first row. */
3677 for (y = 1; y < height; ++y)
3680 memcpy(buf, first, width * bpp);
3686 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3688 TRACE("surface %p.\n", surface);
3690 if (!(surface->flags & SFLAG_LOCKED))
3692 WARN("Trying to unmap unmapped surface.\n");
3693 return WINEDDERR_NOTLOCKED;
3695 surface->flags &= ~SFLAG_LOCKED;
3697 surface->surface_ops->surface_unmap(surface);
3702 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3703 struct wined3d_mapped_rect *mapped_rect, const RECT *rect, DWORD flags)
3705 const struct wined3d_format *format = surface->resource.format;
3707 TRACE("surface %p, mapped_rect %p, rect %s, flags %#x.\n",
3708 surface, mapped_rect, wine_dbgstr_rect(rect), flags);
3710 if (surface->flags & SFLAG_LOCKED)
3712 WARN("Surface is already mapped.\n");
3713 return WINED3DERR_INVALIDCALL;
3715 if ((format->flags & WINED3DFMT_FLAG_BLOCKS)
3716 && rect && (rect->left || rect->top
3717 || rect->right != surface->resource.width
3718 || rect->bottom != surface->resource.height))
3720 UINT width_mask = format->block_width - 1;
3721 UINT height_mask = format->block_height - 1;
3723 if ((rect->left & width_mask) || (rect->right & width_mask)
3724 || (rect->top & height_mask) || (rect->bottom & height_mask))
3726 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3727 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3729 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
3730 return WINED3DERR_INVALIDCALL;
3734 surface->flags |= SFLAG_LOCKED;
3736 if (!(surface->flags & SFLAG_LOCKABLE))
3737 WARN("Trying to lock unlockable surface.\n");
3739 /* Performance optimization: Count how often a surface is mapped, if it is
3740 * mapped regularly do not throw away the system memory copy. This avoids
3741 * the need to download the surface from OpenGL all the time. The surface
3742 * is still downloaded if the OpenGL texture is changed. */
3743 if (!(surface->flags & SFLAG_DYNLOCK))
3745 if (++surface->lockCount > MAXLOCKCOUNT)
3747 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3748 surface->flags |= SFLAG_DYNLOCK;
3752 surface->surface_ops->surface_map(surface, rect, flags);
3754 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3755 mapped_rect->row_pitch = surface->resource.width * format->byte_count;
3757 mapped_rect->row_pitch = wined3d_surface_get_pitch(surface);
3761 mapped_rect->data = surface->resource.allocatedMemory;
3762 surface->lockedRect.left = 0;
3763 surface->lockedRect.top = 0;
3764 surface->lockedRect.right = surface->resource.width;
3765 surface->lockedRect.bottom = surface->resource.height;
3769 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3771 /* Compressed textures are block based, so calculate the offset of
3772 * the block that contains the top-left pixel of the locked rectangle. */
3773 mapped_rect->data = surface->resource.allocatedMemory
3774 + ((rect->top / format->block_height) * mapped_rect->row_pitch)
3775 + ((rect->left / format->block_width) * format->block_byte_count);
3779 mapped_rect->data = surface->resource.allocatedMemory
3780 + (mapped_rect->row_pitch * rect->top)
3781 + (rect->left * format->byte_count);
3783 surface->lockedRect.left = rect->left;
3784 surface->lockedRect.top = rect->top;
3785 surface->lockedRect.right = rect->right;
3786 surface->lockedRect.bottom = rect->bottom;
3789 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3790 TRACE("Returning memory %p, pitch %u.\n", mapped_rect->data, mapped_rect->row_pitch);
3795 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3797 struct wined3d_mapped_rect map;
3800 TRACE("surface %p, dc %p.\n", surface, dc);
3802 if (surface->flags & SFLAG_USERPTR)
3804 ERR("Not supported on surfaces with application-provided memory.\n");
3805 return WINEDDERR_NODC;
3808 /* Give more detailed info for ddraw. */
3809 if (surface->flags & SFLAG_DCINUSE)
3810 return WINEDDERR_DCALREADYCREATED;
3812 /* Can't GetDC if the surface is locked. */
3813 if (surface->flags & SFLAG_LOCKED)
3814 return WINED3DERR_INVALIDCALL;
3816 /* Create a DIB section if there isn't a dc yet. */
3819 if (surface->flags & SFLAG_CLIENT)
3821 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3822 surface_release_client_storage(surface);
3824 hr = surface_create_dib_section(surface);
3826 return WINED3DERR_INVALIDCALL;
3828 /* Use the DIB section from now on if we are not using a PBO. */
3829 if (!(surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)))
3831 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
3832 surface->resource.heapMemory = NULL;
3833 surface->resource.allocatedMemory = surface->dib.bitmap_data;
3837 /* Map the surface. */
3838 hr = wined3d_surface_map(surface, &map, NULL, 0);
3841 ERR("Map failed, hr %#x.\n", hr);
3845 /* Sync the DIB with the PBO. This can't be done earlier because Map()
3846 * activates the allocatedMemory. */
3847 if (surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM))
3848 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
3850 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3851 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3853 /* GetDC on palettized formats is unsupported in D3D9, and the method
3854 * is missing in D3D8, so this should only be used for DX <=7
3855 * surfaces (with non-device palettes). */
3856 const PALETTEENTRY *pal = NULL;
3858 if (surface->palette)
3860 pal = surface->palette->palents;
3864 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3865 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3867 if (dds_primary && dds_primary->palette)
3868 pal = dds_primary->palette->palents;
3876 for (i = 0; i < 256; ++i)
3878 col[i].rgbRed = pal[i].peRed;
3879 col[i].rgbGreen = pal[i].peGreen;
3880 col[i].rgbBlue = pal[i].peBlue;
3881 col[i].rgbReserved = 0;
3883 SetDIBColorTable(surface->hDC, 0, 256, col);
3887 surface->flags |= SFLAG_DCINUSE;
3890 TRACE("Returning dc %p.\n", *dc);
3895 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3897 TRACE("surface %p, dc %p.\n", surface, dc);
3899 if (!(surface->flags & SFLAG_DCINUSE))
3900 return WINEDDERR_NODC;
3902 if (surface->hDC != dc)
3904 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3906 return WINEDDERR_NODC;
3909 /* Copy the contents of the DIB over to the PBO. */
3910 if ((surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)) && surface->resource.allocatedMemory)
3911 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3913 /* We locked first, so unlock now. */
3914 wined3d_surface_unmap(surface);
3916 surface->flags &= ~SFLAG_DCINUSE;
3921 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3923 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3929 FIXME("Ignoring flags %#x.\n", flags);
3931 WARN("Ignoring flags %#x.\n", flags);
3934 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
3936 ERR("Not supported on swapchain surfaces.\n");
3937 return WINEDDERR_NOTFLIPPABLE;
3940 /* Flipping is only supported on render targets and overlays. */
3941 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
3943 WARN("Tried to flip a non-render target, non-overlay surface.\n");
3944 return WINEDDERR_NOTFLIPPABLE;
3947 flip_surface(surface, override);
3949 /* Update overlays if they're visible. */
3950 if ((surface->resource.usage & WINED3DUSAGE_OVERLAY) && surface->overlay_dest)
3951 return surface_draw_overlay(surface);
3956 /* Do not call while under the GL lock. */
3957 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3959 struct wined3d_device *device = surface->resource.device;
3961 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3963 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3965 struct wined3d_texture *texture = surface->container.u.texture;
3967 TRACE("Passing to container (%p).\n", texture);
3968 texture->texture_ops->texture_preload(texture, srgb);
3972 struct wined3d_context *context;
3974 TRACE("(%p) : About to load surface\n", surface);
3976 /* TODO: Use already acquired context when possible. */
3977 context = context_acquire(device, NULL);
3979 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3981 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
3983 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3987 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3991 context_release(context);
3995 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3997 if (!surface->resource.allocatedMemory)
3999 if (!surface->resource.heapMemory)
4001 if (!(surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
4002 surface->resource.size + RESOURCE_ALIGNMENT)))
4004 ERR("Failed to allocate memory.\n");
4008 else if (!(surface->flags & SFLAG_CLIENT))
4010 ERR("Surface %p has heapMemory %p and flags %#x.\n",
4011 surface, surface->resource.heapMemory, surface->flags);
4014 surface->resource.allocatedMemory =
4015 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
4019 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
4022 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
4027 /* Read the framebuffer back into the surface */
4028 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
4030 struct wined3d_device *device = surface->resource.device;
4031 const struct wined3d_gl_info *gl_info;
4032 struct wined3d_context *context;
4036 BYTE *row, *top, *bottom;
4040 BOOL srcIsUpsideDown;
4045 context = context_acquire(device, surface);
4046 context_apply_blit_state(context, device);
4047 gl_info = context->gl_info;
4051 /* Select the correct read buffer, and give some debug output.
4052 * There is no need to keep track of the current read buffer or reset it, every part of the code
4053 * that reads sets the read buffer as desired.
4055 if (surface_is_offscreen(surface))
4057 /* Mapping the primary render target which is not on a swapchain.
4058 * Read from the back buffer. */
4059 TRACE("Mapping offscreen render target.\n");
4060 glReadBuffer(device->offscreenBuffer);
4061 srcIsUpsideDown = TRUE;
4065 /* Onscreen surfaces are always part of a swapchain */
4066 GLenum buffer = surface_get_gl_buffer(surface);
4067 TRACE("Mapping %#x buffer.\n", buffer);
4068 glReadBuffer(buffer);
4069 checkGLcall("glReadBuffer");
4070 srcIsUpsideDown = FALSE;
4073 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
4076 local_rect.left = 0;
4078 local_rect.right = surface->resource.width;
4079 local_rect.bottom = surface->resource.height;
4085 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
4087 switch (surface->resource.format->id)
4089 case WINED3DFMT_P8_UINT:
4091 if (primary_render_target_is_p8(device))
4093 /* In case of P8 render targets the index is stored in the alpha component */
4095 type = GL_UNSIGNED_BYTE;
4097 bpp = surface->resource.format->byte_count;
4101 /* GL can't return palettized data, so read ARGB pixels into a
4102 * separate block of memory and convert them into palettized format
4103 * in software. Slow, but if the app means to use palettized render
4104 * targets and locks it...
4106 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4107 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4108 * for the color channels when palettizing the colors.
4111 type = GL_UNSIGNED_BYTE;
4113 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4116 ERR("Out of memory\n");
4120 bpp = surface->resource.format->byte_count * 3;
4127 fmt = surface->resource.format->glFormat;
4128 type = surface->resource.format->glType;
4129 bpp = surface->resource.format->byte_count;
4132 if (surface->flags & SFLAG_PBO)
4134 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4135 checkGLcall("glBindBufferARB");
4138 ERR("mem not null for pbo -- unexpected\n");
4143 /* Save old pixel store pack state */
4144 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4145 checkGLcall("glGetIntegerv");
4146 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4147 checkGLcall("glGetIntegerv");
4148 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4149 checkGLcall("glGetIntegerv");
4151 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4152 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4153 checkGLcall("glPixelStorei");
4154 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4155 checkGLcall("glPixelStorei");
4156 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4157 checkGLcall("glPixelStorei");
4159 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4160 local_rect.right - local_rect.left,
4161 local_rect.bottom - local_rect.top,
4163 checkGLcall("glReadPixels");
4165 /* Reset previous pixel store pack state */
4166 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4167 checkGLcall("glPixelStorei");
4168 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4169 checkGLcall("glPixelStorei");
4170 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4171 checkGLcall("glPixelStorei");
4173 if (surface->flags & SFLAG_PBO)
4175 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4176 checkGLcall("glBindBufferARB");
4178 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4179 * to get a pointer to it and perform the flipping in software. This is a lot
4180 * faster than calling glReadPixels for each line. In case we want more speed
4181 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4182 if (!srcIsUpsideDown)
4184 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4185 checkGLcall("glBindBufferARB");
4187 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4188 checkGLcall("glMapBufferARB");
4192 /* TODO: Merge this with the palettization loop below for P8 targets */
4193 if(!srcIsUpsideDown) {
4195 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4196 Flip the lines in software */
4197 len = (local_rect.right - local_rect.left) * bpp;
4198 off = local_rect.left * bpp;
4200 row = HeapAlloc(GetProcessHeap(), 0, len);
4202 ERR("Out of memory\n");
4203 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4204 HeapFree(GetProcessHeap(), 0, mem);
4209 top = mem + pitch * local_rect.top;
4210 bottom = mem + pitch * (local_rect.bottom - 1);
4211 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4212 memcpy(row, top + off, len);
4213 memcpy(top + off, bottom + off, len);
4214 memcpy(bottom + off, row, len);
4218 HeapFree(GetProcessHeap(), 0, row);
4220 /* Unmap the temp PBO buffer */
4221 if (surface->flags & SFLAG_PBO)
4223 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4224 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4229 context_release(context);
4231 /* For P8 textures we need to perform an inverse palette lookup. This is
4232 * done by searching for a palette index which matches the RGB value.
4233 * Note this isn't guaranteed to work when there are multiple entries for
4234 * the same color but we have no choice. In case of P8 render targets,
4235 * the index is stored in the alpha component so no conversion is needed. */
4236 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4238 const PALETTEENTRY *pal = NULL;
4239 DWORD width = pitch / 3;
4242 if (surface->palette)
4244 pal = surface->palette->palents;
4248 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4249 HeapFree(GetProcessHeap(), 0, mem);
4253 for(y = local_rect.top; y < local_rect.bottom; y++) {
4254 for(x = local_rect.left; x < local_rect.right; x++) {
4255 /* start lines pixels */
4256 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4257 const BYTE *green = blue + 1;
4258 const BYTE *red = green + 1;
4260 for(c = 0; c < 256; c++) {
4261 if(*red == pal[c].peRed &&
4262 *green == pal[c].peGreen &&
4263 *blue == pal[c].peBlue)
4265 *((BYTE *) dest + y * width + x) = c;
4271 HeapFree(GetProcessHeap(), 0, mem);
4275 /* Read the framebuffer contents into a texture. Note that this function
4276 * doesn't do any kind of flipping. Using this on an onscreen surface will
4277 * result in a flipped D3D texture. */
4278 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
4280 struct wined3d_device *device = surface->resource.device;
4281 struct wined3d_context *context;
4283 context = context_acquire(device, surface);
4284 device_invalidate_state(device, STATE_FRAMEBUFFER);
4286 surface_prepare_texture(surface, context, srgb);
4287 surface_bind_and_dirtify(surface, context, srgb);
4289 TRACE("Reading back offscreen render target %p.\n", surface);
4293 if (surface_is_offscreen(surface))
4294 glReadBuffer(device->offscreenBuffer);
4296 glReadBuffer(surface_get_gl_buffer(surface));
4297 checkGLcall("glReadBuffer");
4299 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4300 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4301 checkGLcall("glCopyTexSubImage2D");
4305 context_release(context);
4308 /* Context activation is done by the caller. */
4309 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4310 struct wined3d_context *context, BOOL srgb)
4312 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4313 enum wined3d_conversion_type convert;
4314 struct wined3d_format format;
4316 if (surface->flags & alloc_flag) return;
4318 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4319 if (convert != WINED3D_CT_NONE || format.convert)
4320 surface->flags |= SFLAG_CONVERTED;
4321 else surface->flags &= ~SFLAG_CONVERTED;
4323 surface_bind_and_dirtify(surface, context, srgb);
4324 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4325 surface->flags |= alloc_flag;
4328 /* Context activation is done by the caller. */
4329 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4331 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4333 struct wined3d_texture *texture = surface->container.u.texture;
4334 UINT sub_count = texture->level_count * texture->layer_count;
4337 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4339 for (i = 0; i < sub_count; ++i)
4341 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4342 surface_prepare_texture_internal(s, context, srgb);
4348 surface_prepare_texture_internal(surface, context, srgb);
4351 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4355 if (surface->rb_multisample)
4358 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4359 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4360 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4361 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4362 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4366 if (surface->rb_resolved)
4369 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4370 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4371 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4372 surface->pow2Width, surface->pow2Height);
4373 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4377 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4378 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4380 struct wined3d_device *device = surface->resource.device;
4381 UINT pitch = wined3d_surface_get_pitch(surface);
4382 const struct wined3d_gl_info *gl_info;
4383 struct wined3d_context *context;
4387 surface_get_rect(surface, rect, &local_rect);
4389 mem += local_rect.top * pitch + local_rect.left * bpp;
4390 w = local_rect.right - local_rect.left;
4391 h = local_rect.bottom - local_rect.top;
4393 /* Activate the correct context for the render target */
4394 context = context_acquire(device, surface);
4395 context_apply_blit_state(context, device);
4396 gl_info = context->gl_info;
4400 if (!surface_is_offscreen(surface))
4402 GLenum buffer = surface_get_gl_buffer(surface);
4403 TRACE("Unlocking %#x buffer.\n", buffer);
4404 context_set_draw_buffer(context, buffer);
4406 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4407 glPixelZoom(1.0f, -1.0f);
4411 /* Primary offscreen render target */
4412 TRACE("Offscreen render target.\n");
4413 context_set_draw_buffer(context, device->offscreenBuffer);
4415 glPixelZoom(1.0f, 1.0f);
4418 glRasterPos3i(local_rect.left, local_rect.top, 1);
4419 checkGLcall("glRasterPos3i");
4421 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4422 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4424 if (surface->flags & SFLAG_PBO)
4426 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4427 checkGLcall("glBindBufferARB");
4430 glDrawPixels(w, h, fmt, type, mem);
4431 checkGLcall("glDrawPixels");
4433 if (surface->flags & SFLAG_PBO)
4435 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4436 checkGLcall("glBindBufferARB");
4439 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4440 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4444 if (wined3d_settings.strict_draw_ordering
4445 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4446 && surface->container.u.swapchain->front_buffer == surface))
4449 context_release(context);
4452 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck, BOOL use_texturing,
4453 struct wined3d_format *format, enum wined3d_conversion_type *conversion_type)
4455 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4456 const struct wined3d_device *device = surface->resource.device;
4457 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4458 BOOL blit_supported = FALSE;
4460 /* Copy the default values from the surface. Below we might perform fixups */
4461 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4462 *format = *surface->resource.format;
4463 *conversion_type = WINED3D_CT_NONE;
4465 /* Ok, now look if we have to do any conversion */
4466 switch (surface->resource.format->id)
4468 case WINED3DFMT_P8_UINT:
4469 /* Below the call to blit_supported is disabled for Wine 1.2
4470 * because the function isn't operating correctly yet. At the
4471 * moment 8-bit blits are handled in software and if certain GL
4472 * extensions are around, surface conversion is performed at
4473 * upload time. The blit_supported call recognizes it as a
4474 * destination fixup. This type of upload 'fixup' and 8-bit to
4475 * 8-bit blits need to be handled by the blit_shader.
4476 * TODO: get rid of this #if 0. */
4478 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4479 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4480 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4482 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4484 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4485 * texturing. Further also use conversion in case of color keying.
4486 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4487 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4488 * conflicts with this.
4490 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4491 || colorkey_active || !use_texturing)
4493 format->glFormat = GL_RGBA;
4494 format->glInternal = GL_RGBA;
4495 format->glType = GL_UNSIGNED_BYTE;
4496 format->conv_byte_count = 4;
4497 if (colorkey_active)
4498 *conversion_type = WINED3D_CT_PALETTED_CK;
4500 *conversion_type = WINED3D_CT_PALETTED;
4504 case WINED3DFMT_B2G3R3_UNORM:
4505 /* **********************
4506 GL_UNSIGNED_BYTE_3_3_2
4507 ********************** */
4508 if (colorkey_active) {
4509 /* This texture format will never be used.. So do not care about color keying
4510 up until the point in time it will be needed :-) */
4511 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4515 case WINED3DFMT_B5G6R5_UNORM:
4516 if (colorkey_active)
4518 *conversion_type = WINED3D_CT_CK_565;
4519 format->glFormat = GL_RGBA;
4520 format->glInternal = GL_RGB5_A1;
4521 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4522 format->conv_byte_count = 2;
4526 case WINED3DFMT_B5G5R5X1_UNORM:
4527 if (colorkey_active)
4529 *conversion_type = WINED3D_CT_CK_5551;
4530 format->glFormat = GL_BGRA;
4531 format->glInternal = GL_RGB5_A1;
4532 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4533 format->conv_byte_count = 2;
4537 case WINED3DFMT_B8G8R8_UNORM:
4538 if (colorkey_active)
4540 *conversion_type = WINED3D_CT_CK_RGB24;
4541 format->glFormat = GL_RGBA;
4542 format->glInternal = GL_RGBA8;
4543 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4544 format->conv_byte_count = 4;
4548 case WINED3DFMT_B8G8R8X8_UNORM:
4549 if (colorkey_active)
4551 *conversion_type = WINED3D_CT_RGB32_888;
4552 format->glFormat = GL_RGBA;
4553 format->glInternal = GL_RGBA8;
4554 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4555 format->conv_byte_count = 4;
4559 case WINED3DFMT_B8G8R8A8_UNORM:
4560 if (colorkey_active)
4562 *conversion_type = WINED3D_CT_CK_ARGB32;
4563 format->conv_byte_count = 4;
4571 if (*conversion_type != WINED3D_CT_NONE)
4573 format->rtInternal = format->glInternal;
4574 format->glGammaInternal = format->glInternal;
4580 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
4582 /* FIXME: Is this really how color keys are supposed to work? I think it
4583 * makes more sense to compare the individual channels. */
4584 return color >= color_key->color_space_low_value
4585 && color <= color_key->color_space_high_value;
4588 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4590 const struct wined3d_device *device = surface->resource.device;
4591 const struct wined3d_palette *pal = surface->palette;
4592 BOOL index_in_alpha = FALSE;
4595 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4596 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4597 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4598 * duplicate entries. Store the color key in the unused alpha component to speed the
4599 * download up and to make conversion unneeded. */
4600 index_in_alpha = primary_render_target_is_p8(device);
4604 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4607 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4608 * there's no palette at this time. */
4609 for (i = 0; i < 256; i++) table[i][3] = i;
4614 TRACE("Using surface palette %p\n", pal);
4615 /* Get the surface's palette */
4616 for (i = 0; i < 256; ++i)
4618 table[i][0] = pal->palents[i].peRed;
4619 table[i][1] = pal->palents[i].peGreen;
4620 table[i][2] = pal->palents[i].peBlue;
4622 /* When index_in_alpha is set the palette index is stored in the
4623 * alpha component. In case of a readback we can then read
4624 * GL_ALPHA. Color keying is handled in BltOverride using a
4625 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4626 * color key itself is passed to glAlphaFunc in other cases the
4627 * alpha component of pixels that should be masked away is set to 0. */
4630 else if (colorkey && color_in_range(&surface->src_blt_color_key, i))
4632 else if (pal->flags & WINEDDPCAPS_ALPHA)
4633 table[i][3] = pal->palents[i].peFlags;
4640 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
4641 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
4646 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
4647 src, dst, pitch, width, height, outpitch, conversion_type, surface);
4649 switch (conversion_type)
4651 case WINED3D_CT_NONE:
4653 memcpy(dst, src, pitch * height);
4657 case WINED3D_CT_PALETTED:
4658 case WINED3D_CT_PALETTED_CK:
4663 d3dfmt_p8_init_palette(surface, table, (conversion_type == WINED3D_CT_PALETTED_CK));
4665 for (y = 0; y < height; y++)
4667 source = src + pitch * y;
4668 dest = dst + outpitch * y;
4669 /* This is an 1 bpp format, using the width here is fine */
4670 for (x = 0; x < width; x++) {
4671 BYTE color = *source++;
4672 *dest++ = table[color][0];
4673 *dest++ = table[color][1];
4674 *dest++ = table[color][2];
4675 *dest++ = table[color][3];
4681 case WINED3D_CT_CK_565:
4683 /* Converting the 565 format in 5551 packed to emulate color-keying.
4685 Note : in all these conversion, it would be best to average the averaging
4686 pixels to get the color of the pixel that will be color-keyed to
4687 prevent 'color bleeding'. This will be done later on if ever it is
4690 Note2: Nvidia documents say that their driver does not support alpha + color keying
4691 on the same surface and disables color keying in such a case
4697 TRACE("Color keyed 565\n");
4699 for (y = 0; y < height; y++) {
4700 Source = (const WORD *)(src + y * pitch);
4701 Dest = (WORD *) (dst + y * outpitch);
4702 for (x = 0; x < width; x++ ) {
4703 WORD color = *Source++;
4704 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4705 if (!color_in_range(&surface->src_blt_color_key, color))
4713 case WINED3D_CT_CK_5551:
4715 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4719 TRACE("Color keyed 5551\n");
4720 for (y = 0; y < height; y++) {
4721 Source = (const WORD *)(src + y * pitch);
4722 Dest = (WORD *) (dst + y * outpitch);
4723 for (x = 0; x < width; x++ ) {
4724 WORD color = *Source++;
4726 if (!color_in_range(&surface->src_blt_color_key, color))
4729 *Dest &= ~(1 << 15);
4736 case WINED3D_CT_CK_RGB24:
4738 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4740 for (y = 0; y < height; y++)
4742 source = src + pitch * y;
4743 dest = dst + outpitch * y;
4744 for (x = 0; x < width; x++) {
4745 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4746 DWORD dstcolor = color << 8;
4747 if (!color_in_range(&surface->src_blt_color_key, color))
4749 *(DWORD*)dest = dstcolor;
4757 case WINED3D_CT_RGB32_888:
4759 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4761 for (y = 0; y < height; y++)
4763 source = src + pitch * y;
4764 dest = dst + outpitch * y;
4765 for (x = 0; x < width; x++) {
4766 DWORD color = 0xffffff & *(const DWORD*)source;
4767 DWORD dstcolor = color << 8;
4768 if (!color_in_range(&surface->src_blt_color_key, color))
4770 *(DWORD*)dest = dstcolor;
4778 case WINED3D_CT_CK_ARGB32:
4781 for (y = 0; y < height; ++y)
4783 source = src + pitch * y;
4784 dest = dst + outpitch * y;
4785 for (x = 0; x < width; ++x)
4787 DWORD color = *(const DWORD *)source;
4788 if (color_in_range(&surface->src_blt_color_key, color))
4789 color &= ~0xff000000;
4790 *(DWORD*)dest = color;
4799 ERR("Unsupported conversion type %#x.\n", conversion_type);
4804 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4806 /* Flip the surface contents */
4811 front->hDC = back->hDC;
4815 /* Flip the DIBsection */
4817 HBITMAP tmp = front->dib.DIBsection;
4818 front->dib.DIBsection = back->dib.DIBsection;
4819 back->dib.DIBsection = tmp;
4822 /* Flip the surface data */
4826 tmp = front->dib.bitmap_data;
4827 front->dib.bitmap_data = back->dib.bitmap_data;
4828 back->dib.bitmap_data = tmp;
4830 tmp = front->resource.allocatedMemory;
4831 front->resource.allocatedMemory = back->resource.allocatedMemory;
4832 back->resource.allocatedMemory = tmp;
4834 tmp = front->resource.heapMemory;
4835 front->resource.heapMemory = back->resource.heapMemory;
4836 back->resource.heapMemory = tmp;
4841 GLuint tmp_pbo = front->pbo;
4842 front->pbo = back->pbo;
4843 back->pbo = tmp_pbo;
4846 /* Flip the opengl texture */
4850 tmp = back->texture_name;
4851 back->texture_name = front->texture_name;
4852 front->texture_name = tmp;
4854 tmp = back->texture_name_srgb;
4855 back->texture_name_srgb = front->texture_name_srgb;
4856 front->texture_name_srgb = tmp;
4858 tmp = back->rb_multisample;
4859 back->rb_multisample = front->rb_multisample;
4860 front->rb_multisample = tmp;
4862 tmp = back->rb_resolved;
4863 back->rb_resolved = front->rb_resolved;
4864 front->rb_resolved = tmp;
4866 resource_unload(&back->resource);
4867 resource_unload(&front->resource);
4871 DWORD tmp_flags = back->flags;
4872 back->flags = front->flags;
4873 front->flags = tmp_flags;
4877 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4878 * pixel copy calls. */
4879 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4880 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4882 struct wined3d_device *device = dst_surface->resource.device;
4885 struct wined3d_context *context;
4886 BOOL upsidedown = FALSE;
4887 RECT dst_rect = *dst_rect_in;
4889 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4890 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4892 if(dst_rect.top > dst_rect.bottom) {
4893 UINT tmp = dst_rect.bottom;
4894 dst_rect.bottom = dst_rect.top;
4899 context = context_acquire(device, src_surface);
4900 context_apply_blit_state(context, device);
4901 surface_internal_preload(dst_surface, SRGB_RGB);
4904 /* Bind the target texture */
4905 context_bind_texture(context, dst_surface->texture_target, dst_surface->texture_name);
4906 if (surface_is_offscreen(src_surface))
4908 TRACE("Reading from an offscreen target\n");
4909 upsidedown = !upsidedown;
4910 glReadBuffer(device->offscreenBuffer);
4914 glReadBuffer(surface_get_gl_buffer(src_surface));
4916 checkGLcall("glReadBuffer");
4918 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4919 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4921 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4923 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4925 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4926 ERR("Texture filtering not supported in direct blit.\n");
4928 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4929 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4931 ERR("Texture filtering not supported in direct blit\n");
4935 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4936 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4938 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4940 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4941 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4942 src_rect->left, src_surface->resource.height - src_rect->bottom,
4943 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4947 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4948 /* I have to process this row by row to swap the image,
4949 * otherwise it would be upside down, so stretching in y direction
4950 * doesn't cost extra time
4952 * However, stretching in x direction can be avoided if not necessary
4954 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4955 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4957 /* Well, that stuff works, but it's very slow.
4958 * find a better way instead
4962 for (col = dst_rect.left; col < dst_rect.right; ++col)
4964 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4965 dst_rect.left + col /* x offset */, row /* y offset */,
4966 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4971 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4972 dst_rect.left /* x offset */, row /* y offset */,
4973 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4977 checkGLcall("glCopyTexSubImage2D");
4980 context_release(context);
4982 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4983 * path is never entered
4985 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4988 /* Uses the hardware to stretch and flip the image */
4989 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4990 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4992 struct wined3d_device *device = dst_surface->resource.device;
4993 struct wined3d_swapchain *src_swapchain = NULL;
4994 GLuint src, backup = 0;
4995 float left, right, top, bottom; /* Texture coordinates */
4996 UINT fbwidth = src_surface->resource.width;
4997 UINT fbheight = src_surface->resource.height;
4998 struct wined3d_context *context;
4999 GLenum drawBuffer = GL_BACK;
5000 GLenum texture_target;
5001 BOOL noBackBufferBackup;
5003 BOOL upsidedown = FALSE;
5004 RECT dst_rect = *dst_rect_in;
5006 TRACE("Using hwstretch blit\n");
5007 /* Activate the Proper context for reading from the source surface, set it up for blitting */
5008 context = context_acquire(device, src_surface);
5009 context_apply_blit_state(context, device);
5010 surface_internal_preload(dst_surface, SRGB_RGB);
5012 src_offscreen = surface_is_offscreen(src_surface);
5013 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
5014 if (!noBackBufferBackup && !src_surface->texture_name)
5016 /* Get it a description */
5017 surface_internal_preload(src_surface, SRGB_RGB);
5021 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
5022 * This way we don't have to wait for the 2nd readback to finish to leave this function.
5024 if (context->aux_buffers >= 2)
5026 /* Got more than one aux buffer? Use the 2nd aux buffer */
5027 drawBuffer = GL_AUX1;
5029 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
5031 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
5032 drawBuffer = GL_AUX0;
5035 if(noBackBufferBackup) {
5036 glGenTextures(1, &backup);
5037 checkGLcall("glGenTextures");
5038 context_bind_texture(context, GL_TEXTURE_2D, backup);
5039 texture_target = GL_TEXTURE_2D;
5041 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
5042 * we are reading from the back buffer, the backup can be used as source texture
5044 texture_target = src_surface->texture_target;
5045 context_bind_texture(context, texture_target, src_surface->texture_name);
5046 glEnable(texture_target);
5047 checkGLcall("glEnable(texture_target)");
5049 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
5050 src_surface->flags &= ~SFLAG_INTEXTURE;
5053 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
5054 * glCopyTexSubImage is a bit picky about the parameters we pass to it
5056 if(dst_rect.top > dst_rect.bottom) {
5057 UINT tmp = dst_rect.bottom;
5058 dst_rect.bottom = dst_rect.top;
5065 TRACE("Reading from an offscreen target\n");
5066 upsidedown = !upsidedown;
5067 glReadBuffer(device->offscreenBuffer);
5071 glReadBuffer(surface_get_gl_buffer(src_surface));
5074 /* TODO: Only back up the part that will be overwritten */
5075 glCopyTexSubImage2D(texture_target, 0,
5076 0, 0 /* read offsets */,
5081 checkGLcall("glCopyTexSubImage2D");
5083 /* No issue with overriding these - the sampler is dirty due to blit usage */
5084 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
5085 wined3d_gl_mag_filter(magLookup, filter));
5086 checkGLcall("glTexParameteri");
5087 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5088 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
5089 checkGLcall("glTexParameteri");
5091 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5092 src_swapchain = src_surface->container.u.swapchain;
5093 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5095 src = backup ? backup : src_surface->texture_name;
5099 glReadBuffer(GL_FRONT);
5100 checkGLcall("glReadBuffer(GL_FRONT)");
5102 glGenTextures(1, &src);
5103 checkGLcall("glGenTextures(1, &src)");
5104 context_bind_texture(context, GL_TEXTURE_2D, src);
5106 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5107 * out for power of 2 sizes
5109 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5110 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5111 checkGLcall("glTexImage2D");
5112 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5113 0, 0 /* read offsets */,
5118 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5119 checkGLcall("glTexParameteri");
5120 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5121 checkGLcall("glTexParameteri");
5123 glReadBuffer(GL_BACK);
5124 checkGLcall("glReadBuffer(GL_BACK)");
5126 if(texture_target != GL_TEXTURE_2D) {
5127 glDisable(texture_target);
5128 glEnable(GL_TEXTURE_2D);
5129 texture_target = GL_TEXTURE_2D;
5132 checkGLcall("glEnd and previous");
5134 left = src_rect->left;
5135 right = src_rect->right;
5139 top = src_surface->resource.height - src_rect->top;
5140 bottom = src_surface->resource.height - src_rect->bottom;
5144 top = src_surface->resource.height - src_rect->bottom;
5145 bottom = src_surface->resource.height - src_rect->top;
5148 if (src_surface->flags & SFLAG_NORMCOORD)
5150 left /= src_surface->pow2Width;
5151 right /= src_surface->pow2Width;
5152 top /= src_surface->pow2Height;
5153 bottom /= src_surface->pow2Height;
5156 /* draw the source texture stretched and upside down. The correct surface is bound already */
5157 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5158 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5160 context_set_draw_buffer(context, drawBuffer);
5161 glReadBuffer(drawBuffer);
5165 glTexCoord2f(left, bottom);
5169 glTexCoord2f(left, top);
5170 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5173 glTexCoord2f(right, top);
5174 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5177 glTexCoord2f(right, bottom);
5178 glVertex2i(dst_rect.right - dst_rect.left, 0);
5180 checkGLcall("glEnd and previous");
5182 if (texture_target != dst_surface->texture_target)
5184 glDisable(texture_target);
5185 glEnable(dst_surface->texture_target);
5186 texture_target = dst_surface->texture_target;
5189 /* Now read the stretched and upside down image into the destination texture */
5190 context_bind_texture(context, texture_target, dst_surface->texture_name);
5191 glCopyTexSubImage2D(texture_target,
5193 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5194 0, 0, /* We blitted the image to the origin */
5195 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5196 checkGLcall("glCopyTexSubImage2D");
5198 if(drawBuffer == GL_BACK) {
5199 /* Write the back buffer backup back */
5201 if(texture_target != GL_TEXTURE_2D) {
5202 glDisable(texture_target);
5203 glEnable(GL_TEXTURE_2D);
5204 texture_target = GL_TEXTURE_2D;
5206 context_bind_texture(context, GL_TEXTURE_2D, backup);
5210 if (texture_target != src_surface->texture_target)
5212 glDisable(texture_target);
5213 glEnable(src_surface->texture_target);
5214 texture_target = src_surface->texture_target;
5216 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5221 glTexCoord2f(0.0f, 0.0f);
5222 glVertex2i(0, fbheight);
5225 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5229 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5230 (float)fbheight / (float)src_surface->pow2Height);
5231 glVertex2i(fbwidth, 0);
5234 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5235 glVertex2i(fbwidth, fbheight);
5238 glDisable(texture_target);
5239 checkGLcall("glDisable(texture_target)");
5242 if (src != src_surface->texture_name && src != backup)
5244 glDeleteTextures(1, &src);
5245 checkGLcall("glDeleteTextures(1, &src)");
5248 glDeleteTextures(1, &backup);
5249 checkGLcall("glDeleteTextures(1, &backup)");
5254 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5256 context_release(context);
5258 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5259 * path is never entered
5261 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5264 /* Front buffer coordinates are always full screen coordinates, but our GL
5265 * drawable is limited to the window's client area. The sysmem and texture
5266 * copies do have the full screen size. Note that GL has a bottom-left
5267 * origin, while D3D has a top-left origin. */
5268 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5270 UINT drawable_height;
5272 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5273 && surface == surface->container.u.swapchain->front_buffer)
5275 POINT offset = {0, 0};
5278 ScreenToClient(window, &offset);
5279 OffsetRect(rect, offset.x, offset.y);
5281 GetClientRect(window, &windowsize);
5282 drawable_height = windowsize.bottom - windowsize.top;
5286 drawable_height = surface->resource.height;
5289 rect->top = drawable_height - rect->top;
5290 rect->bottom = drawable_height - rect->bottom;
5293 static void surface_blt_to_drawable(const struct wined3d_device *device,
5294 enum wined3d_texture_filter_type filter, BOOL color_key,
5295 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5296 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5298 struct wined3d_context *context;
5299 RECT src_rect, dst_rect;
5301 src_rect = *src_rect_in;
5302 dst_rect = *dst_rect_in;
5304 /* Make sure the surface is up-to-date. This should probably use
5305 * surface_load_location() and worry about the destination surface too,
5306 * unless we're overwriting it completely. */
5307 surface_internal_preload(src_surface, SRGB_RGB);
5309 /* Activate the destination context, set it up for blitting */
5310 context = context_acquire(device, dst_surface);
5311 context_apply_blit_state(context, device);
5313 if (!surface_is_offscreen(dst_surface))
5314 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5316 device->blitter->set_shader(device->blit_priv, context, src_surface);
5322 glEnable(GL_ALPHA_TEST);
5323 checkGLcall("glEnable(GL_ALPHA_TEST)");
5325 /* When the primary render target uses P8, the alpha component
5326 * contains the palette index. Which means that the colorkey is one of
5327 * the palette entries. In other cases pixels that should be masked
5328 * away have alpha set to 0. */
5329 if (primary_render_target_is_p8(device))
5330 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->src_blt_color_key.color_space_low_value / 256.0f);
5332 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5333 checkGLcall("glAlphaFunc");
5337 glDisable(GL_ALPHA_TEST);
5338 checkGLcall("glDisable(GL_ALPHA_TEST)");
5341 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5345 glDisable(GL_ALPHA_TEST);
5346 checkGLcall("glDisable(GL_ALPHA_TEST)");
5351 /* Leave the opengl state valid for blitting */
5352 device->blitter->unset_shader(context->gl_info);
5354 if (wined3d_settings.strict_draw_ordering
5355 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5356 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5357 wglFlush(); /* Flush to ensure ordering across contexts. */
5359 context_release(context);
5362 /* Do not call while under the GL lock. */
5363 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
5365 struct wined3d_device *device = s->resource.device;
5366 const struct blit_shader *blitter;
5368 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5369 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5372 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5373 return WINED3DERR_INVALIDCALL;
5376 return blitter->color_fill(device, s, rect, color);
5379 /* Do not call while under the GL lock. */
5380 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5381 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5382 enum wined3d_texture_filter_type filter)
5384 struct wined3d_device *device = dst_surface->resource.device;
5385 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5386 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5388 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5389 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5390 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
5392 /* Get the swapchain. One of the surfaces has to be a primary surface */
5393 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
5395 WARN("Destination is in sysmem, rejecting gl blt\n");
5396 return WINED3DERR_INVALIDCALL;
5399 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5400 dstSwapchain = dst_surface->container.u.swapchain;
5404 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
5406 WARN("Src is in sysmem, rejecting gl blt\n");
5407 return WINED3DERR_INVALIDCALL;
5410 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5411 srcSwapchain = src_surface->container.u.swapchain;
5414 /* Early sort out of cases where no render target is used */
5415 if (!dstSwapchain && !srcSwapchain
5416 && src_surface != device->fb.render_targets[0]
5417 && dst_surface != device->fb.render_targets[0])
5419 TRACE("No surface is render target, not using hardware blit.\n");
5420 return WINED3DERR_INVALIDCALL;
5423 /* No destination color keying supported */
5424 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5426 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5427 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5428 return WINED3DERR_INVALIDCALL;
5431 if (dstSwapchain && dstSwapchain == srcSwapchain)
5433 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5434 return WINED3DERR_INVALIDCALL;
5437 if (dstSwapchain && srcSwapchain)
5439 FIXME("Implement hardware blit between two different swapchains\n");
5440 return WINED3DERR_INVALIDCALL;
5445 /* Handled with regular texture -> swapchain blit */
5446 if (src_surface == device->fb.render_targets[0])
5447 TRACE("Blit from active render target to a swapchain\n");
5449 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5451 FIXME("Implement blit from a swapchain to the active render target\n");
5452 return WINED3DERR_INVALIDCALL;
5455 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5457 /* Blit from render target to texture */
5460 /* P8 read back is not implemented */
5461 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5462 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5464 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5465 return WINED3DERR_INVALIDCALL;
5468 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5470 TRACE("Color keying not supported by frame buffer to texture blit\n");
5471 return WINED3DERR_INVALIDCALL;
5472 /* Destination color key is checked above */
5475 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5480 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5481 * flip the image nor scale it.
5483 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5484 * -> If the app wants a image width an unscaled width, copy it line per line
5485 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5486 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5487 * back buffer. This is slower than reading line per line, thus not used for flipping
5488 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5489 * pixel by pixel. */
5490 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5491 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5493 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
5494 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
5498 TRACE("Using hardware stretching to flip / stretch the texture.\n");
5499 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
5502 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5504 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5505 dst_surface->resource.allocatedMemory = NULL;
5506 dst_surface->resource.heapMemory = NULL;
5510 dst_surface->flags &= ~SFLAG_INSYSMEM;
5515 else if (src_surface)
5517 /* Blit from offscreen surface to render target */
5518 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
5519 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5521 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5523 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5524 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5525 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5527 FIXME("Unsupported blit operation falling back to software\n");
5528 return WINED3DERR_INVALIDCALL;
5531 /* Color keying: Check if we have to do a color keyed blt,
5532 * and if not check if a color key is activated.
5534 * Just modify the color keying parameters in the surface and restore them afterwards
5535 * The surface keeps track of the color key last used to load the opengl surface.
5536 * PreLoad will catch the change to the flags and color key and reload if necessary.
5538 if (flags & WINEDDBLT_KEYSRC)
5540 /* Use color key from surface */
5542 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5544 /* Use color key from DDBltFx */
5545 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5546 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
5550 /* Do not use color key */
5551 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5554 surface_blt_to_drawable(device, filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5555 src_surface, src_rect, dst_surface, dst_rect);
5557 /* Restore the color key parameters */
5558 src_surface->CKeyFlags = oldCKeyFlags;
5559 src_surface->src_blt_color_key = old_blt_key;
5561 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5566 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5567 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5568 return WINED3DERR_INVALIDCALL;
5571 /* GL locking is done by the caller */
5572 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5573 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5575 struct wined3d_device *device = surface->resource.device;
5576 const struct wined3d_gl_info *gl_info = context->gl_info;
5577 GLint compare_mode = GL_NONE;
5578 struct blt_info info;
5579 GLint old_binding = 0;
5582 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5584 glDisable(GL_CULL_FACE);
5585 glDisable(GL_BLEND);
5586 glDisable(GL_ALPHA_TEST);
5587 glDisable(GL_SCISSOR_TEST);
5588 glDisable(GL_STENCIL_TEST);
5589 glEnable(GL_DEPTH_TEST);
5590 glDepthFunc(GL_ALWAYS);
5591 glDepthMask(GL_TRUE);
5592 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5593 glViewport(x, y, w, h);
5594 glDepthRange(0.0, 1.0);
5596 SetRect(&rect, 0, h, w, 0);
5597 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5598 context_active_texture(context, context->gl_info, 0);
5599 glGetIntegerv(info.binding, &old_binding);
5600 glBindTexture(info.bind_target, texture);
5601 if (gl_info->supported[ARB_SHADOW])
5603 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5604 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5607 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5608 gl_info, info.tex_type, &surface->ds_current_size);
5610 glBegin(GL_TRIANGLE_STRIP);
5611 glTexCoord3fv(info.coords[0]);
5612 glVertex2f(-1.0f, -1.0f);
5613 glTexCoord3fv(info.coords[1]);
5614 glVertex2f(1.0f, -1.0f);
5615 glTexCoord3fv(info.coords[2]);
5616 glVertex2f(-1.0f, 1.0f);
5617 glTexCoord3fv(info.coords[3]);
5618 glVertex2f(1.0f, 1.0f);
5621 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5622 glBindTexture(info.bind_target, old_binding);
5626 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5629 void surface_modify_ds_location(struct wined3d_surface *surface,
5630 DWORD location, UINT w, UINT h)
5632 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5634 if (location & ~(SFLAG_LOCATIONS | SFLAG_DISCARDED))
5635 FIXME("Invalid location (%#x) specified.\n", location);
5637 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5638 || (!(surface->flags & SFLAG_INTEXTURE) && (location & SFLAG_INTEXTURE)))
5640 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5642 TRACE("Passing to container.\n");
5643 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5647 surface->ds_current_size.cx = w;
5648 surface->ds_current_size.cy = h;
5649 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_DISCARDED);
5650 surface->flags |= location;
5653 /* Context activation is done by the caller. */
5654 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5656 struct wined3d_device *device = surface->resource.device;
5659 TRACE("surface %p, new location %#x.\n", surface, location);
5661 /* TODO: Make this work for modes other than FBO */
5662 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5664 if (!(surface->flags & location))
5666 w = surface->ds_current_size.cx;
5667 h = surface->ds_current_size.cy;
5668 surface->ds_current_size.cx = 0;
5669 surface->ds_current_size.cy = 0;
5673 w = surface->resource.width;
5674 h = surface->resource.height;
5677 if (surface->ds_current_size.cx == surface->resource.width
5678 && surface->ds_current_size.cy == surface->resource.height)
5680 TRACE("Location (%#x) is already up to date.\n", location);
5684 if (surface->current_renderbuffer)
5686 FIXME("Not supported with fixed up depth stencil.\n");
5690 if (surface->flags & SFLAG_DISCARDED)
5692 TRACE("Surface was discarded, no need copy data.\n");
5695 case SFLAG_INTEXTURE:
5696 surface_prepare_texture(surface, context, FALSE);
5698 case SFLAG_INRB_MULTISAMPLE:
5699 surface_prepare_rb(surface, context->gl_info, TRUE);
5701 case SFLAG_INDRAWABLE:
5705 FIXME("Unhandled location %#x\n", location);
5707 surface->flags &= ~SFLAG_DISCARDED;
5708 surface->flags |= location;
5709 surface->ds_current_size.cx = surface->resource.width;
5710 surface->ds_current_size.cy = surface->resource.height;
5714 if (!(surface->flags & SFLAG_LOCATIONS))
5716 FIXME("No up to date depth stencil location.\n");
5717 surface->flags |= location;
5718 surface->ds_current_size.cx = surface->resource.width;
5719 surface->ds_current_size.cy = surface->resource.height;
5723 if (location == SFLAG_INTEXTURE)
5725 GLint old_binding = 0;
5728 /* The render target is allowed to be smaller than the depth/stencil
5729 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5730 * than the offscreen surface. Don't overwrite the offscreen surface
5731 * with undefined data. */
5732 w = min(w, context->swapchain->desc.backbuffer_width);
5733 h = min(h, context->swapchain->desc.backbuffer_height);
5735 TRACE("Copying onscreen depth buffer to depth texture.\n");
5739 if (!device->depth_blt_texture)
5741 glGenTextures(1, &device->depth_blt_texture);
5744 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5745 * directly on the FBO texture. That's because we need to flip. */
5746 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5747 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5748 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5750 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5751 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5755 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5756 bind_target = GL_TEXTURE_2D;
5758 glBindTexture(bind_target, device->depth_blt_texture);
5759 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5760 * internal format, because the internal format might include stencil
5761 * data. In principle we should copy stencil data as well, but unless
5762 * the driver supports stencil export it's hard to do, and doesn't
5763 * seem to be needed in practice. If the hardware doesn't support
5764 * writing stencil data, the glCopyTexImage2D() call might trigger
5765 * software fallbacks. */
5766 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5767 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5768 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5769 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5770 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5771 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5772 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5773 glBindTexture(bind_target, old_binding);
5775 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5776 NULL, surface, SFLAG_INTEXTURE);
5777 context_set_draw_buffer(context, GL_NONE);
5778 glReadBuffer(GL_NONE);
5780 /* Do the actual blit */
5781 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5782 checkGLcall("depth_blt");
5784 context_invalidate_state(context, STATE_FRAMEBUFFER);
5788 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5790 else if (location == SFLAG_INDRAWABLE)
5792 TRACE("Copying depth texture to onscreen depth buffer.\n");
5796 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5797 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5798 surface_depth_blt(surface, context, surface->texture_name,
5799 0, surface->pow2Height - h, w, h, surface->texture_target);
5800 checkGLcall("depth_blt");
5802 context_invalidate_state(context, STATE_FRAMEBUFFER);
5806 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5810 ERR("Invalid location (%#x) specified.\n", location);
5813 surface->flags |= location;
5814 surface->ds_current_size.cx = surface->resource.width;
5815 surface->ds_current_size.cy = surface->resource.height;
5818 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5820 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5821 struct wined3d_surface *overlay;
5823 TRACE("surface %p, location %s, persistent %#x.\n",
5824 surface, debug_surflocation(location), persistent);
5826 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5827 && !(surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5828 && (location & SFLAG_INDRAWABLE))
5829 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5831 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5832 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5833 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5837 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5838 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5840 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5842 TRACE("Passing to container.\n");
5843 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5846 surface->flags &= ~SFLAG_LOCATIONS;
5847 surface->flags |= location;
5849 /* Redraw emulated overlays, if any */
5850 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5852 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5854 surface_draw_overlay(overlay);
5860 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5862 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5864 TRACE("Passing to container\n");
5865 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5868 surface->flags &= ~location;
5871 if (!(surface->flags & SFLAG_LOCATIONS))
5873 ERR("Surface %p does not have any up to date location.\n", surface);
5877 static DWORD resource_access_from_location(DWORD location)
5881 case SFLAG_INSYSMEM:
5882 return WINED3D_RESOURCE_ACCESS_CPU;
5884 case SFLAG_INDRAWABLE:
5885 case SFLAG_INSRGBTEX:
5886 case SFLAG_INTEXTURE:
5887 case SFLAG_INRB_MULTISAMPLE:
5888 case SFLAG_INRB_RESOLVED:
5889 return WINED3D_RESOURCE_ACCESS_GPU;
5892 FIXME("Unhandled location %#x.\n", location);
5897 static void surface_load_sysmem(struct wined3d_surface *surface,
5898 const struct wined3d_gl_info *gl_info, const RECT *rect)
5900 surface_prepare_system_memory(surface);
5902 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5903 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5905 /* Download the surface to system memory. */
5906 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5908 struct wined3d_device *device = surface->resource.device;
5909 struct wined3d_context *context;
5911 /* TODO: Use already acquired context when possible. */
5912 context = context_acquire(device, NULL);
5914 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5915 surface_download_data(surface, gl_info);
5917 context_release(context);
5922 if (surface->flags & SFLAG_INDRAWABLE)
5924 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5925 wined3d_surface_get_pitch(surface));
5929 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5930 surface, surface->flags & SFLAG_LOCATIONS);
5933 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5934 const struct wined3d_gl_info *gl_info, const RECT *rect)
5936 struct wined3d_device *device = surface->resource.device;
5937 enum wined3d_conversion_type convert;
5938 struct wined3d_format format;
5942 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5944 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5945 return WINED3DERR_INVALIDCALL;
5948 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5949 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5951 if (surface->flags & SFLAG_INTEXTURE)
5955 surface_get_rect(surface, rect, &r);
5956 surface_blt_to_drawable(device, WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
5961 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5963 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5964 * path through sysmem. */
5965 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5968 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5970 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5971 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5973 if ((convert != WINED3D_CT_NONE) && (surface->flags & SFLAG_PBO))
5975 struct wined3d_context *context;
5977 TRACE("Removing the pbo attached to surface %p.\n", surface);
5979 /* TODO: Use already acquired context when possible. */
5980 context = context_acquire(device, NULL);
5982 surface_remove_pbo(surface, gl_info);
5984 context_release(context);
5987 if ((convert != WINED3D_CT_NONE) && surface->resource.allocatedMemory)
5989 UINT height = surface->resource.height;
5990 UINT width = surface->resource.width;
5991 UINT src_pitch, dst_pitch;
5993 byte_count = format.conv_byte_count;
5994 src_pitch = wined3d_surface_get_pitch(surface);
5996 /* Stick to the alignment for the converted surface too, makes it
5997 * easier to load the surface. */
5998 dst_pitch = width * byte_count;
5999 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6001 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6003 ERR("Out of memory (%u).\n", dst_pitch * height);
6004 return E_OUTOFMEMORY;
6007 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
6008 src_pitch, width, height, dst_pitch, convert, surface);
6010 surface->flags |= SFLAG_CONVERTED;
6014 surface->flags &= ~SFLAG_CONVERTED;
6015 mem = surface->resource.allocatedMemory;
6016 byte_count = format.byte_count;
6019 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
6021 /* Don't delete PBO memory. */
6022 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6023 HeapFree(GetProcessHeap(), 0, mem);
6028 static HRESULT surface_load_texture(struct wined3d_surface *surface,
6029 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
6031 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
6032 struct wined3d_device *device = surface->resource.device;
6033 enum wined3d_conversion_type convert;
6034 struct wined3d_context *context;
6035 UINT width, src_pitch, dst_pitch;
6036 struct wined3d_bo_address data;
6037 struct wined3d_format format;
6038 POINT dst_point = {0, 0};
6041 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
6042 && surface_is_offscreen(surface)
6043 && (surface->flags & SFLAG_INDRAWABLE))
6045 surface_load_fb_texture(surface, srgb);
6050 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6051 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
6052 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6053 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6054 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6057 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INTEXTURE,
6058 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6060 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INSRGBTEX,
6061 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6066 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
6067 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
6068 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6069 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6070 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6072 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
6073 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
6074 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6076 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
6077 &rect, surface, dst_location, &rect);
6082 /* Upload from system memory */
6084 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6085 TRUE /* We will use textures */, &format, &convert);
6089 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6091 /* Performance warning... */
6092 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6093 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6098 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6100 /* Performance warning... */
6101 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6102 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6106 if (!(surface->flags & SFLAG_INSYSMEM))
6108 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6109 /* Lets hope we get it from somewhere... */
6110 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6113 /* TODO: Use already acquired context when possible. */
6114 context = context_acquire(device, NULL);
6116 surface_prepare_texture(surface, context, srgb);
6117 surface_bind_and_dirtify(surface, context, srgb);
6119 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6121 surface->flags |= SFLAG_GLCKEY;
6122 surface->gl_color_key = surface->src_blt_color_key;
6124 else surface->flags &= ~SFLAG_GLCKEY;
6126 width = surface->resource.width;
6127 src_pitch = wined3d_surface_get_pitch(surface);
6129 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6130 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6132 if ((convert != WINED3D_CT_NONE || format.convert) && (surface->flags & SFLAG_PBO))
6134 TRACE("Removing the pbo attached to surface %p.\n", surface);
6135 surface_remove_pbo(surface, gl_info);
6140 /* This code is entered for texture formats which need a fixup. */
6141 UINT height = surface->resource.height;
6143 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6144 dst_pitch = width * format.conv_byte_count;
6145 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6147 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6149 ERR("Out of memory (%u).\n", dst_pitch * height);
6150 context_release(context);
6151 return E_OUTOFMEMORY;
6153 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6154 format.byte_count = format.conv_byte_count;
6155 src_pitch = dst_pitch;
6157 else if (convert != WINED3D_CT_NONE && surface->resource.allocatedMemory)
6159 /* This code is only entered for color keying fixups */
6160 UINT height = surface->resource.height;
6162 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6163 dst_pitch = width * format.conv_byte_count;
6164 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6166 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6168 ERR("Out of memory (%u).\n", dst_pitch * height);
6169 context_release(context);
6170 return E_OUTOFMEMORY;
6172 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6173 width, height, dst_pitch, convert, surface);
6174 format.byte_count = format.conv_byte_count;
6175 src_pitch = dst_pitch;
6179 mem = surface->resource.allocatedMemory;
6182 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6184 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
6186 context_release(context);
6188 /* Don't delete PBO memory. */
6189 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6190 HeapFree(GetProcessHeap(), 0, mem);
6195 static void surface_multisample_resolve(struct wined3d_surface *surface)
6197 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6199 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6200 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6202 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
6203 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6206 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6208 struct wined3d_device *device = surface->resource.device;
6209 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6212 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6214 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6216 if (location == SFLAG_INTEXTURE)
6218 struct wined3d_context *context = context_acquire(device, NULL);
6219 surface_load_ds_location(surface, context, location);
6220 context_release(context);
6225 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6226 return WINED3DERR_INVALIDCALL;
6230 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6231 location = SFLAG_INTEXTURE;
6233 if (surface->flags & location)
6235 TRACE("Location already up to date.\n");
6237 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
6238 && surface_need_pbo(surface, gl_info))
6239 surface_load_pbo(surface, gl_info);
6244 if (WARN_ON(d3d_surface))
6246 DWORD required_access = resource_access_from_location(location);
6247 if ((surface->resource.access_flags & required_access) != required_access)
6248 WARN("Operation requires %#x access, but surface only has %#x.\n",
6249 required_access, surface->resource.access_flags);
6252 if (!(surface->flags & SFLAG_LOCATIONS))
6254 ERR("Surface %p does not have any up to date location.\n", surface);
6255 surface->flags |= SFLAG_LOST;
6256 return WINED3DERR_DEVICELOST;
6261 case SFLAG_INSYSMEM:
6262 surface_load_sysmem(surface, gl_info, rect);
6265 case SFLAG_INDRAWABLE:
6266 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6270 case SFLAG_INRB_RESOLVED:
6271 surface_multisample_resolve(surface);
6274 case SFLAG_INTEXTURE:
6275 case SFLAG_INSRGBTEX:
6276 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6281 ERR("Don't know how to handle location %#x.\n", location);
6287 surface->flags |= location;
6289 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6290 surface_evict_sysmem(surface);
6293 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6294 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6296 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6302 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6304 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6306 /* Not on a swapchain - must be offscreen */
6307 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6309 /* The front buffer is always onscreen */
6310 if (surface == swapchain->front_buffer) return FALSE;
6312 /* If the swapchain is rendered to an FBO, the backbuffer is
6313 * offscreen, otherwise onscreen */
6314 return swapchain->render_to_fbo;
6317 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6318 /* Context activation is done by the caller. */
6319 static void ffp_blit_free(struct wined3d_device *device) { }
6321 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6322 /* Context activation is done by the caller. */
6323 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6326 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6328 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6330 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6332 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6336 /* Context activation is done by the caller. */
6337 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6339 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6341 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6342 * else the surface is converted in software at upload time in LoadLocation.
6344 if (!(surface->flags & SFLAG_CONVERTED) && fixup == COMPLEX_FIXUP_P8
6345 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6346 ffp_blit_p8_upload_palette(surface, context->gl_info);
6349 glEnable(surface->texture_target);
6350 checkGLcall("glEnable(surface->texture_target)");
6355 /* Context activation is done by the caller. */
6356 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6359 glDisable(GL_TEXTURE_2D);
6360 checkGLcall("glDisable(GL_TEXTURE_2D)");
6361 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6363 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6364 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6366 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6368 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6369 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6374 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6375 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
6376 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
6378 enum complex_fixup src_fixup;
6382 case WINED3D_BLIT_OP_COLOR_BLIT:
6383 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
6386 src_fixup = get_complex_fixup(src_format->color_fixup);
6387 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6389 TRACE("Checking support for fixup:\n");
6390 dump_color_fixup_desc(src_format->color_fixup);
6393 if (!is_identity_fixup(dst_format->color_fixup))
6395 TRACE("Destination fixups are not supported\n");
6399 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6401 TRACE("P8 fixup supported\n");
6405 /* We only support identity conversions. */
6406 if (is_identity_fixup(src_format->color_fixup))
6412 TRACE("[FAILED]\n");
6415 case WINED3D_BLIT_OP_COLOR_FILL:
6416 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
6419 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6421 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6424 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6426 TRACE("Color fill not supported\n");
6430 /* FIXME: We should reject color fills on formats with fixups,
6431 * but this would break P8 color fills for example. */
6435 case WINED3D_BLIT_OP_DEPTH_FILL:
6439 TRACE("Unsupported blit_op=%d\n", blit_op);
6444 /* Do not call while under the GL lock. */
6445 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6446 const RECT *dst_rect, const struct wined3d_color *color)
6448 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6449 struct wined3d_fb_state fb = {&dst_surface, NULL};
6451 return device_clear_render_targets(device, 1, &fb,
6452 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6455 /* Do not call while under the GL lock. */
6456 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6457 struct wined3d_surface *surface, const RECT *rect, float depth)
6459 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6460 struct wined3d_fb_state fb = {NULL, surface};
6462 return device_clear_render_targets(device, 0, &fb,
6463 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6466 const struct blit_shader ffp_blit = {
6472 ffp_blit_color_fill,
6473 ffp_blit_depth_fill,
6476 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6481 /* Context activation is done by the caller. */
6482 static void cpu_blit_free(struct wined3d_device *device)
6486 /* Context activation is done by the caller. */
6487 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6492 /* Context activation is done by the caller. */
6493 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6497 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6498 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
6499 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
6501 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6509 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
6510 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
6511 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
6513 UINT row_block_count;
6514 const BYTE *src_row;
6521 row_block_count = (update_w + format->block_width - 1) / format->block_width;
6525 for (y = 0; y < update_h; y += format->block_height)
6527 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
6528 src_row += src_pitch;
6529 dst_row += dst_pitch;
6535 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
6537 src_row += (((update_h / format->block_height) - 1) * src_pitch);
6541 case WINED3DFMT_DXT1:
6542 for (y = 0; y < update_h; y += format->block_height)
6547 BYTE control_row[4];
6550 const struct block *s = (const struct block *)src_row;
6551 struct block *d = (struct block *)dst_row;
6553 for (x = 0; x < row_block_count; ++x)
6555 d[x].color[0] = s[x].color[0];
6556 d[x].color[1] = s[x].color[1];
6557 d[x].control_row[0] = s[x].control_row[3];
6558 d[x].control_row[1] = s[x].control_row[2];
6559 d[x].control_row[2] = s[x].control_row[1];
6560 d[x].control_row[3] = s[x].control_row[0];
6562 src_row -= src_pitch;
6563 dst_row += dst_pitch;
6567 case WINED3DFMT_DXT3:
6568 for (y = 0; y < update_h; y += format->block_height)
6574 BYTE control_row[4];
6577 const struct block *s = (const struct block *)src_row;
6578 struct block *d = (struct block *)dst_row;
6580 for (x = 0; x < row_block_count; ++x)
6582 d[x].alpha_row[0] = s[x].alpha_row[3];
6583 d[x].alpha_row[1] = s[x].alpha_row[2];
6584 d[x].alpha_row[2] = s[x].alpha_row[1];
6585 d[x].alpha_row[3] = s[x].alpha_row[0];
6586 d[x].color[0] = s[x].color[0];
6587 d[x].color[1] = s[x].color[1];
6588 d[x].control_row[0] = s[x].control_row[3];
6589 d[x].control_row[1] = s[x].control_row[2];
6590 d[x].control_row[2] = s[x].control_row[1];
6591 d[x].control_row[3] = s[x].control_row[0];
6593 src_row -= src_pitch;
6594 dst_row += dst_pitch;
6599 FIXME("Compressed flip not implemented for format %s.\n",
6600 debug_d3dformat(format->id));
6605 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
6606 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
6611 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6612 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6613 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
6615 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6616 const struct wined3d_format *src_format, *dst_format;
6617 struct wined3d_surface *orig_src = src_surface;
6618 struct wined3d_mapped_rect dst_map, src_map;
6619 const BYTE *sbase = NULL;
6620 HRESULT hr = WINED3D_OK;
6625 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6626 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6627 flags, fx, debug_d3dtexturefiltertype(filter));
6629 if (src_surface == dst_surface)
6631 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6633 src_format = dst_surface->resource.format;
6634 dst_format = src_format;
6638 dst_format = dst_surface->resource.format;
6641 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6643 src_surface = surface_convert_format(src_surface, dst_format->id);
6646 /* The conv function writes a FIXME */
6647 WARN("Cannot convert source surface format to dest format.\n");
6651 wined3d_surface_map(src_surface, &src_map, NULL, WINED3DLOCK_READONLY);
6652 src_format = src_surface->resource.format;
6656 src_format = dst_format;
6659 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
6662 bpp = dst_surface->resource.format->byte_count;
6663 srcheight = src_rect->bottom - src_rect->top;
6664 srcwidth = src_rect->right - src_rect->left;
6665 dstheight = dst_rect->bottom - dst_rect->top;
6666 dstwidth = dst_rect->right - dst_rect->left;
6667 width = (dst_rect->right - dst_rect->left) * bpp;
6670 sbase = (BYTE *)src_map.data
6671 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
6672 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
6673 if (src_surface != dst_surface)
6674 dbuf = dst_map.data;
6676 dbuf = (BYTE *)dst_map.data
6677 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
6678 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
6680 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
6682 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6684 if (src_surface == dst_surface)
6686 FIXME("Only plain blits supported on compressed surfaces.\n");
6691 if (srcheight != dstheight || srcwidth != dstwidth)
6693 WARN("Stretching not supported on compressed surfaces.\n");
6694 hr = WINED3DERR_INVALIDCALL;
6698 if (srcwidth & (src_format->block_width - 1) || srcheight & (src_format->block_height - 1))
6700 WARN("Rectangle not block-aligned.\n");
6701 hr = WINED3DERR_INVALIDCALL;
6705 hr = surface_cpu_blt_compressed(sbase, dbuf,
6706 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
6707 src_format, flags, fx);
6711 /* First, all the 'source-less' blits */
6712 if (flags & WINEDDBLT_COLORFILL)
6714 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
6715 flags &= ~WINEDDBLT_COLORFILL;
6718 if (flags & WINEDDBLT_DEPTHFILL)
6720 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6722 if (flags & WINEDDBLT_ROP)
6724 /* Catch some degenerate cases here. */
6728 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
6730 case 0xAA0029: /* No-op */
6733 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
6735 case SRCCOPY: /* Well, we do that below? */
6738 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6741 flags &= ~WINEDDBLT_ROP;
6743 if (flags & WINEDDBLT_DDROPS)
6745 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6747 /* Now the 'with source' blits. */
6750 int sx, xinc, sy, yinc;
6752 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6755 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
6756 && (srcwidth != dstwidth || srcheight != dstheight))
6758 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6759 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6762 xinc = (srcwidth << 16) / dstwidth;
6763 yinc = (srcheight << 16) / dstheight;
6767 /* No effects, we can cheat here. */
6768 if (dstwidth == srcwidth)
6770 if (dstheight == srcheight)
6772 /* No stretching in either direction. This needs to be as
6773 * fast as possible. */
6776 /* Check for overlapping surfaces. */
6777 if (src_surface != dst_surface || dst_rect->top < src_rect->top
6778 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
6780 /* No overlap, or dst above src, so copy from top downwards. */
6781 for (y = 0; y < dstheight; ++y)
6783 memcpy(dbuf, sbuf, width);
6784 sbuf += src_map.row_pitch;
6785 dbuf += dst_map.row_pitch;
6788 else if (dst_rect->top > src_rect->top)
6790 /* Copy from bottom upwards. */
6791 sbuf += src_map.row_pitch * dstheight;
6792 dbuf += dst_map.row_pitch * dstheight;
6793 for (y = 0; y < dstheight; ++y)
6795 sbuf -= src_map.row_pitch;
6796 dbuf -= dst_map.row_pitch;
6797 memcpy(dbuf, sbuf, width);
6802 /* Src and dst overlapping on the same line, use memmove. */
6803 for (y = 0; y < dstheight; ++y)
6805 memmove(dbuf, sbuf, width);
6806 sbuf += src_map.row_pitch;
6807 dbuf += dst_map.row_pitch;
6813 /* Stretching in y direction only. */
6814 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6816 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6817 memcpy(dbuf, sbuf, width);
6818 dbuf += dst_map.row_pitch;
6824 /* Stretching in X direction. */
6826 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6828 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6830 if ((sy >> 16) == (last_sy >> 16))
6832 /* This source row is the same as last source row -
6833 * Copy the already stretched row. */
6834 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
6838 #define STRETCH_ROW(type) \
6840 const type *s = (const type *)sbuf; \
6841 type *d = (type *)dbuf; \
6842 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6843 d[x] = s[sx >> 16]; \
6861 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6865 s = sbuf + 3 * (sx >> 16);
6866 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6867 d[0] = (pixel ) & 0xff;
6868 d[1] = (pixel >> 8) & 0xff;
6869 d[2] = (pixel >> 16) & 0xff;
6875 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6876 hr = WINED3DERR_NOTAVAILABLE;
6881 dbuf += dst_map.row_pitch;
6888 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
6889 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6890 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6891 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6893 /* The color keying flags are checked for correctness in ddraw */
6894 if (flags & WINEDDBLT_KEYSRC)
6896 keylow = src_surface->src_blt_color_key.color_space_low_value;
6897 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
6899 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6901 keylow = fx->ddckSrcColorkey.color_space_low_value;
6902 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
6905 if (flags & WINEDDBLT_KEYDEST)
6907 /* Destination color keys are taken from the source surface! */
6908 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
6909 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
6911 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6913 destkeylow = fx->ddckDestColorkey.color_space_low_value;
6914 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
6923 keymask = src_format->red_mask
6924 | src_format->green_mask
6925 | src_format->blue_mask;
6927 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6930 if (flags & WINEDDBLT_DDFX)
6932 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6935 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6936 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
6937 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6939 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6941 /* I don't think we need to do anything about this flag */
6942 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6944 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6947 dTopRight = dTopLeft;
6950 dBottomRight = dBottomLeft;
6952 dstxinc = dstxinc * -1;
6954 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6957 dTopLeft = dBottomLeft;
6960 dTopRight = dBottomRight;
6962 dstyinc = dstyinc * -1;
6964 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6966 /* I don't think we need to do anything about this flag */
6967 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6969 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6972 dBottomRight = dTopLeft;
6975 dBottomLeft = dTopRight;
6977 dstxinc = dstxinc * -1;
6978 dstyinc = dstyinc * -1;
6980 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6983 dTopLeft = dBottomLeft;
6984 dBottomLeft = dBottomRight;
6985 dBottomRight = dTopRight;
6990 dstxinc = dstxinc * -1;
6992 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6995 dTopLeft = dTopRight;
6996 dTopRight = dBottomRight;
6997 dBottomRight = dBottomLeft;
7002 dstyinc = dstyinc * -1;
7004 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
7006 /* I don't think we need to do anything about this flag */
7007 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
7010 flags &= ~(WINEDDBLT_DDFX);
7013 #define COPY_COLORKEY_FX(type) \
7016 type *d = (type *)dbuf, *dx, tmp; \
7017 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
7019 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
7021 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
7023 tmp = s[sx >> 16]; \
7024 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
7025 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
7029 dx = (type *)(((BYTE *)dx) + dstxinc); \
7031 d = (type *)(((BYTE *)d) + dstyinc); \
7038 COPY_COLORKEY_FX(BYTE);
7041 COPY_COLORKEY_FX(WORD);
7044 COPY_COLORKEY_FX(DWORD);
7049 BYTE *d = dbuf, *dx;
7050 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
7052 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
7054 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
7056 DWORD pixel, dpixel = 0;
7057 s = sbuf + 3 * (sx>>16);
7058 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
7059 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
7060 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
7061 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
7063 dx[0] = (pixel ) & 0xff;
7064 dx[1] = (pixel >> 8) & 0xff;
7065 dx[2] = (pixel >> 16) & 0xff;
7074 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7075 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7076 hr = WINED3DERR_NOTAVAILABLE;
7078 #undef COPY_COLORKEY_FX
7084 if (flags && FIXME_ON(d3d_surface))
7086 FIXME("\tUnsupported flags: %#x.\n", flags);
7090 wined3d_surface_unmap(dst_surface);
7091 if (src_surface && src_surface != dst_surface)
7092 wined3d_surface_unmap(src_surface);
7093 /* Release the converted surface, if any. */
7094 if (src_surface && src_surface != orig_src)
7095 wined3d_surface_decref(src_surface);
7100 /* Do not call while under the GL lock. */
7101 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7102 const RECT *dst_rect, const struct wined3d_color *color)
7104 static const RECT src_rect;
7107 memset(&BltFx, 0, sizeof(BltFx));
7108 BltFx.dwSize = sizeof(BltFx);
7109 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7110 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7111 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
7114 /* Do not call while under the GL lock. */
7115 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7116 struct wined3d_surface *surface, const RECT *rect, float depth)
7118 FIXME("Depth filling not implemented by cpu_blit.\n");
7119 return WINED3DERR_INVALIDCALL;
7122 const struct blit_shader cpu_blit = {
7128 cpu_blit_color_fill,
7129 cpu_blit_depth_fill,
7132 static HRESULT surface_init(struct wined3d_surface *surface, enum wined3d_surface_type surface_type, UINT alignment,
7133 UINT width, UINT height, UINT level, enum wined3d_multisample_type multisample_type,
7134 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7135 enum wined3d_pool pool, DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops)
7137 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7138 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7139 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
7140 unsigned int resource_size;
7143 if (multisample_quality > 0)
7145 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7146 multisample_quality = 0;
7149 /* Quick lockable sanity check.
7150 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7151 * this function is too deep to need to care about things like this.
7152 * Levels need to be checked too, since they all affect what can be done. */
7155 case WINED3D_POOL_SCRATCH:
7158 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7159 "which are mutually exclusive, setting lockable to TRUE.\n");
7164 case WINED3D_POOL_SYSTEM_MEM:
7166 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7169 case WINED3D_POOL_MANAGED:
7170 if (usage & WINED3DUSAGE_DYNAMIC)
7171 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7174 case WINED3D_POOL_DEFAULT:
7175 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7176 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7180 FIXME("Unknown pool %#x.\n", pool);
7184 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3D_POOL_DEFAULT)
7185 FIXME("Trying to create a render target that isn't in the default pool.\n");
7187 /* FIXME: Check that the format is supported by the device. */
7189 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7191 return WINED3DERR_INVALIDCALL;
7193 surface->surface_type = surface_type;
7195 switch (surface_type)
7197 case WINED3D_SURFACE_TYPE_OPENGL:
7198 surface->surface_ops = &surface_ops;
7201 case WINED3D_SURFACE_TYPE_GDI:
7202 surface->surface_ops = &gdi_surface_ops;
7206 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7207 return WINED3DERR_INVALIDCALL;
7210 hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
7211 multisample_type, multisample_quality, usage, pool, width, height, 1,
7212 resource_size, parent, parent_ops, &surface_resource_ops);
7215 WARN("Failed to initialize resource, returning %#x.\n", hr);
7219 /* "Standalone" surface. */
7220 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7222 surface->texture_level = level;
7223 list_init(&surface->overlays);
7226 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7227 if (flags & WINED3D_SURFACE_DISCARD)
7228 surface->flags |= SFLAG_DISCARD;
7229 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
7230 surface->flags |= SFLAG_PIN_SYSMEM;
7231 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7232 surface->flags |= SFLAG_LOCKABLE;
7233 /* I'm not sure if this qualifies as a hack or as an optimization. It
7234 * seems reasonable to assume that lockable render targets will get
7235 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7236 * creation. However, the other reason we want to do this is that several
7237 * ddraw applications access surface memory while the surface isn't
7238 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7239 * future locks prevents these from crashing. */
7240 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7241 surface->flags |= SFLAG_DYNLOCK;
7243 /* Mark the texture as dirty so that it gets loaded first time around. */
7244 surface_add_dirty_rect(surface, NULL);
7245 list_init(&surface->renderbuffers);
7247 TRACE("surface %p, memory %p, size %u\n",
7248 surface, surface->resource.allocatedMemory, surface->resource.size);
7250 /* Call the private setup routine */
7251 hr = surface->surface_ops->surface_private_setup(surface);
7254 ERR("Private setup failed, returning %#x\n", hr);
7255 surface_cleanup(surface);
7259 /* Similar to lockable rendertargets above, creating the DIB section
7260 * during surface initialization prevents the sysmem pointer from changing
7261 * after a wined3d_surface_getdc() call. */
7262 if ((usage & WINED3DUSAGE_OWNDC) && !surface->hDC
7263 && SUCCEEDED(surface_create_dib_section(surface)))
7265 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
7266 surface->resource.heapMemory = NULL;
7267 surface->resource.allocatedMemory = surface->dib.bitmap_data;
7273 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7274 enum wined3d_format_id format_id, UINT level, DWORD usage, enum wined3d_pool pool,
7275 enum wined3d_multisample_type multisample_type, DWORD multisample_quality,
7276 enum wined3d_surface_type surface_type, DWORD flags, void *parent,
7277 const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7279 struct wined3d_surface *object;
7282 TRACE("device %p, width %u, height %u, format %s, level %u\n",
7283 device, width, height, debug_d3dformat(format_id), level);
7284 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7285 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7286 TRACE("surface_type %#x, flags %#x, parent %p, parent_ops %p.\n", surface_type, flags, parent, parent_ops);
7288 if (surface_type == WINED3D_SURFACE_TYPE_OPENGL && !device->adapter)
7290 ERR("OpenGL surfaces are not available without OpenGL.\n");
7291 return WINED3DERR_NOTAVAILABLE;
7294 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7297 ERR("Failed to allocate surface memory.\n");
7298 return WINED3DERR_OUTOFVIDEOMEMORY;
7301 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level,
7302 multisample_type, multisample_quality, device, usage, format_id, pool, flags, parent, parent_ops);
7305 WARN("Failed to initialize surface, returning %#x.\n", hr);
7306 HeapFree(GetProcessHeap(), 0, object);
7310 TRACE("Created surface %p.\n", object);