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-2008 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d);
36 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
37 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
38 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter);
39 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
40 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *fx,
41 WINED3DTEXTUREFILTERTYPE filter);
43 static void surface_cleanup(struct wined3d_surface *surface)
45 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->texture_name || (surface->flags & SFLAG_PBO)
50 || surface->rb_multisample || surface->rb_resolved
51 || !list_empty(&surface->renderbuffers))
53 struct wined3d_renderbuffer_entry *entry, *entry2;
54 const struct wined3d_gl_info *gl_info;
55 struct wined3d_context *context;
57 context = context_acquire(surface->resource.device, NULL);
58 gl_info = context->gl_info;
62 if (surface->texture_name)
64 TRACE("Deleting texture %u.\n", surface->texture_name);
65 glDeleteTextures(1, &surface->texture_name);
68 if (surface->flags & SFLAG_PBO)
70 TRACE("Deleting PBO %u.\n", surface->pbo);
71 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
74 if (surface->rb_multisample)
76 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
77 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
80 if (surface->rb_resolved)
82 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
83 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
86 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
88 TRACE("Deleting renderbuffer %u.\n", entry->id);
89 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
90 HeapFree(GetProcessHeap(), 0, entry);
95 context_release(context);
98 if (surface->flags & SFLAG_DIBSECTION)
100 /* Release the DC. */
101 SelectObject(surface->hDC, surface->dib.holdbitmap);
102 DeleteDC(surface->hDC);
103 /* Release the DIB section. */
104 DeleteObject(surface->dib.DIBsection);
105 surface->dib.bitmap_data = NULL;
106 surface->resource.allocatedMemory = NULL;
109 if (surface->flags & SFLAG_USERPTR)
110 wined3d_surface_set_mem(surface, NULL);
111 if (surface->overlay_dest)
112 list_remove(&surface->overlay_entry);
114 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
116 list_remove(&overlay->overlay_entry);
117 overlay->overlay_dest = NULL;
120 HeapFree(GetProcessHeap(), 0, surface->palette9);
122 resource_cleanup(&surface->resource);
125 void surface_update_draw_binding(struct wined3d_surface *surface)
127 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
128 surface->draw_binding = SFLAG_INDRAWABLE;
129 else if (surface->resource.multisample_type)
130 surface->draw_binding = SFLAG_INRB_MULTISAMPLE;
132 surface->draw_binding = SFLAG_INTEXTURE;
135 void surface_set_container(struct wined3d_surface *surface, enum wined3d_container_type type, void *container)
137 TRACE("surface %p, container %p.\n", surface, container);
139 if (!container && type != WINED3D_CONTAINER_NONE)
140 ERR("Setting NULL container of type %#x.\n", type);
142 if (type == WINED3D_CONTAINER_SWAPCHAIN)
144 surface->get_drawable_size = get_drawable_size_swapchain;
148 switch (wined3d_settings.offscreen_rendering_mode)
151 surface->get_drawable_size = get_drawable_size_fbo;
155 surface->get_drawable_size = get_drawable_size_backbuffer;
159 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
164 surface->container.type = type;
165 surface->container.u.base = container;
166 surface_update_draw_binding(surface);
173 enum tex_types tex_type;
174 GLfloat coords[4][3];
185 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
187 f->l = ((r->left * 2.0f) / w) - 1.0f;
188 f->t = ((r->top * 2.0f) / h) - 1.0f;
189 f->r = ((r->right * 2.0f) / w) - 1.0f;
190 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
193 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
195 GLfloat (*coords)[3] = info->coords;
201 FIXME("Unsupported texture target %#x\n", target);
202 /* Fall back to GL_TEXTURE_2D */
204 info->binding = GL_TEXTURE_BINDING_2D;
205 info->bind_target = GL_TEXTURE_2D;
206 info->tex_type = tex_2d;
207 coords[0][0] = (float)rect->left / w;
208 coords[0][1] = (float)rect->top / h;
211 coords[1][0] = (float)rect->right / w;
212 coords[1][1] = (float)rect->top / h;
215 coords[2][0] = (float)rect->left / w;
216 coords[2][1] = (float)rect->bottom / h;
219 coords[3][0] = (float)rect->right / w;
220 coords[3][1] = (float)rect->bottom / h;
224 case GL_TEXTURE_RECTANGLE_ARB:
225 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
226 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
227 info->tex_type = tex_rect;
228 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
229 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
230 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
231 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
234 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
235 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
236 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
237 info->tex_type = tex_cube;
238 cube_coords_float(rect, w, h, &f);
240 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
241 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
242 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
243 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
246 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
247 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
248 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
249 info->tex_type = tex_cube;
250 cube_coords_float(rect, w, h, &f);
252 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
253 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
254 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
255 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
258 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
259 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
260 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
261 info->tex_type = tex_cube;
262 cube_coords_float(rect, w, h, &f);
264 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
265 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
266 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
267 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
270 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
271 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
272 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
273 info->tex_type = tex_cube;
274 cube_coords_float(rect, w, h, &f);
276 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
277 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
278 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
279 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
282 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
283 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
284 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
285 info->tex_type = tex_cube;
286 cube_coords_float(rect, w, h, &f);
288 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
289 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
290 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
291 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
294 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
295 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
296 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
297 info->tex_type = tex_cube;
298 cube_coords_float(rect, w, h, &f);
300 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
301 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
302 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
303 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
308 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
311 *rect_out = *rect_in;
316 rect_out->right = surface->resource.width;
317 rect_out->bottom = surface->resource.height;
321 /* GL locking and context activation is done by the caller */
322 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
323 const RECT *src_rect, const RECT *dst_rect, WINED3DTEXTUREFILTERTYPE Filter)
325 struct blt_info info;
327 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
329 glEnable(info.bind_target);
330 checkGLcall("glEnable(bind_target)");
332 context_bind_texture(context, info.bind_target, src_surface->texture_name);
334 /* Filtering for StretchRect */
335 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
336 wined3d_gl_mag_filter(magLookup, Filter));
337 checkGLcall("glTexParameteri");
338 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
339 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
340 checkGLcall("glTexParameteri");
341 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
342 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
343 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
344 glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
345 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
346 checkGLcall("glTexEnvi");
349 glBegin(GL_TRIANGLE_STRIP);
350 glTexCoord3fv(info.coords[0]);
351 glVertex2i(dst_rect->left, dst_rect->top);
353 glTexCoord3fv(info.coords[1]);
354 glVertex2i(dst_rect->right, dst_rect->top);
356 glTexCoord3fv(info.coords[2]);
357 glVertex2i(dst_rect->left, dst_rect->bottom);
359 glTexCoord3fv(info.coords[3]);
360 glVertex2i(dst_rect->right, dst_rect->bottom);
363 /* Unbind the texture */
364 context_bind_texture(context, info.bind_target, 0);
366 /* We changed the filtering settings on the texture. Inform the
367 * container about this to get the filters reset properly next draw. */
368 if (src_surface->container.type == WINED3D_CONTAINER_TEXTURE)
370 struct wined3d_texture *texture = src_surface->container.u.texture;
371 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
372 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
373 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
374 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
378 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
380 const struct wined3d_format *format = surface->resource.format;
388 TRACE("surface %p.\n", surface);
390 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
392 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
393 return WINED3DERR_INVALIDCALL;
396 switch (format->byte_count)
400 /* Allocate extra space to store the RGB bit masks. */
401 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
405 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
409 /* Allocate extra space for a palette. */
410 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
411 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
416 return E_OUTOFMEMORY;
418 /* Some applications access the surface in via DWORDs, and do not take
419 * the necessary care at the end of the surface. So we need at least
420 * 4 extra bytes at the end of the surface. Check against the page size,
421 * if the last page used for the surface has at least 4 spare bytes we're
422 * safe, otherwise add an extra line to the DIB section. */
423 GetSystemInfo(&sysInfo);
424 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
427 TRACE("Adding an extra line to the DIB section.\n");
430 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
431 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
432 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
433 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
434 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
435 * wined3d_surface_get_pitch(surface);
436 b_info->bmiHeader.biPlanes = 1;
437 b_info->bmiHeader.biBitCount = format->byte_count * 8;
439 b_info->bmiHeader.biXPelsPerMeter = 0;
440 b_info->bmiHeader.biYPelsPerMeter = 0;
441 b_info->bmiHeader.biClrUsed = 0;
442 b_info->bmiHeader.biClrImportant = 0;
444 /* Get the bit masks */
445 masks = (DWORD *)b_info->bmiColors;
446 switch (surface->resource.format->id)
448 case WINED3DFMT_B8G8R8_UNORM:
449 usage = DIB_RGB_COLORS;
450 b_info->bmiHeader.biCompression = BI_RGB;
453 case WINED3DFMT_B5G5R5X1_UNORM:
454 case WINED3DFMT_B5G5R5A1_UNORM:
455 case WINED3DFMT_B4G4R4A4_UNORM:
456 case WINED3DFMT_B4G4R4X4_UNORM:
457 case WINED3DFMT_B2G3R3_UNORM:
458 case WINED3DFMT_B2G3R3A8_UNORM:
459 case WINED3DFMT_R10G10B10A2_UNORM:
460 case WINED3DFMT_R8G8B8A8_UNORM:
461 case WINED3DFMT_R8G8B8X8_UNORM:
462 case WINED3DFMT_B10G10R10A2_UNORM:
463 case WINED3DFMT_B5G6R5_UNORM:
464 case WINED3DFMT_R16G16B16A16_UNORM:
466 b_info->bmiHeader.biCompression = BI_BITFIELDS;
467 masks[0] = format->red_mask;
468 masks[1] = format->green_mask;
469 masks[2] = format->blue_mask;
473 /* Don't know palette */
474 b_info->bmiHeader.biCompression = BI_RGB;
479 if (!(dc = GetDC(0)))
481 HeapFree(GetProcessHeap(), 0, b_info);
482 return HRESULT_FROM_WIN32(GetLastError());
485 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
486 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
487 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
488 surface->dib.DIBsection = CreateDIBSection(dc, b_info, usage, &surface->dib.bitmap_data, 0, 0);
491 if (!surface->dib.DIBsection)
493 ERR("Failed to create DIB section.\n");
494 HeapFree(GetProcessHeap(), 0, b_info);
495 return HRESULT_FROM_WIN32(GetLastError());
498 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
499 /* Copy the existing surface to the dib section. */
500 if (surface->resource.allocatedMemory)
502 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
503 surface->resource.height * wined3d_surface_get_pitch(surface));
507 /* This is to make maps read the GL texture although memory is allocated. */
508 surface->flags &= ~SFLAG_INSYSMEM;
510 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
512 HeapFree(GetProcessHeap(), 0, b_info);
514 /* Now allocate a DC. */
515 surface->hDC = CreateCompatibleDC(0);
516 surface->dib.holdbitmap = SelectObject(surface->hDC, surface->dib.DIBsection);
517 TRACE("Using wined3d palette %p.\n", surface->palette);
518 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
520 surface->flags |= SFLAG_DIBSECTION;
522 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
523 surface->resource.heapMemory = NULL;
528 static void surface_prepare_system_memory(struct wined3d_surface *surface)
530 struct wined3d_device *device = surface->resource.device;
531 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
533 TRACE("surface %p.\n", surface);
535 /* Performance optimization: Count how often a surface is locked, if it is
536 * locked regularly do not throw away the system memory copy. This avoids
537 * the need to download the surface from OpenGL all the time. The surface
538 * is still downloaded if the OpenGL texture is changed. */
539 if (!(surface->flags & SFLAG_DYNLOCK))
541 if (++surface->lockCount > MAXLOCKCOUNT)
543 TRACE("Surface is locked regularly, not freeing the system memory copy any more.\n");
544 surface->flags |= SFLAG_DYNLOCK;
548 /* Create a PBO for dynamically locked surfaces but don't do it for
549 * converted or NPOT surfaces. Also don't create a PBO for systemmem
551 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (surface->flags & SFLAG_DYNLOCK)
552 && !(surface->flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
553 && (surface->resource.pool != WINED3DPOOL_SYSTEMMEM))
555 struct wined3d_context *context;
558 context = context_acquire(device, NULL);
561 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
562 error = glGetError();
563 if (!surface->pbo || error != GL_NO_ERROR)
564 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
566 TRACE("Binding PBO %u.\n", surface->pbo);
568 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
569 checkGLcall("glBindBufferARB");
571 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
572 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
573 checkGLcall("glBufferDataARB");
575 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
576 checkGLcall("glBindBufferARB");
578 /* We don't need the system memory anymore and we can't even use it for PBOs. */
579 if (!(surface->flags & SFLAG_CLIENT))
581 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
582 surface->resource.heapMemory = NULL;
584 surface->resource.allocatedMemory = NULL;
585 surface->flags |= SFLAG_PBO;
587 context_release(context);
589 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
591 /* Whatever surface we have, make sure that there is memory allocated
592 * for the downloaded copy, or a PBO to map. */
593 if (!surface->resource.heapMemory)
594 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
596 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
597 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
599 if (surface->flags & SFLAG_INSYSMEM)
600 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
604 static void surface_evict_sysmem(struct wined3d_surface *surface)
606 if (surface->flags & SFLAG_DONOTFREE)
609 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
610 surface->resource.allocatedMemory = NULL;
611 surface->resource.heapMemory = NULL;
612 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
615 /* Context activation is done by the caller. */
616 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
617 struct wined3d_context *context, BOOL srgb)
619 struct wined3d_device *device = surface->resource.device;
620 DWORD active_sampler;
622 /* We don't need a specific texture unit, but after binding the texture
623 * the current unit is dirty. Read the unit back instead of switching to
624 * 0, this avoids messing around with the state manager's GL states. The
625 * current texture unit should always be a valid one.
627 * To be more specific, this is tricky because we can implicitly be
628 * called from sampler() in state.c. This means we can't touch anything
629 * other than whatever happens to be the currently active texture, or we
630 * would risk marking already applied sampler states dirty again. */
631 active_sampler = device->rev_tex_unit_map[context->active_texture];
633 if (active_sampler != WINED3D_UNMAPPED_STAGE)
634 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
635 surface_bind(surface, context, srgb);
638 static void surface_force_reload(struct wined3d_surface *surface)
640 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
643 static void surface_release_client_storage(struct wined3d_surface *surface)
645 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
648 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
649 if (surface->texture_name)
651 surface_bind_and_dirtify(surface, context, FALSE);
652 glTexImage2D(surface->texture_target, surface->texture_level,
653 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
655 if (surface->texture_name_srgb)
657 surface_bind_and_dirtify(surface, context, TRUE);
658 glTexImage2D(surface->texture_target, surface->texture_level,
659 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
661 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
664 context_release(context);
666 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
667 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
668 surface_force_reload(surface);
671 static HRESULT surface_private_setup(struct wined3d_surface *surface)
673 /* TODO: Check against the maximum texture sizes supported by the video card. */
674 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
675 unsigned int pow2Width, pow2Height;
677 TRACE("surface %p.\n", surface);
679 surface->texture_name = 0;
680 surface->texture_target = GL_TEXTURE_2D;
682 /* Non-power2 support */
683 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
685 pow2Width = surface->resource.width;
686 pow2Height = surface->resource.height;
690 /* Find the nearest pow2 match */
691 pow2Width = pow2Height = 1;
692 while (pow2Width < surface->resource.width)
694 while (pow2Height < surface->resource.height)
697 surface->pow2Width = pow2Width;
698 surface->pow2Height = pow2Height;
700 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
702 /* TODO: Add support for non power two compressed textures. */
703 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
705 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
706 surface, surface->resource.width, surface->resource.height);
707 return WINED3DERR_NOTAVAILABLE;
711 if (pow2Width != surface->resource.width
712 || pow2Height != surface->resource.height)
714 surface->flags |= SFLAG_NONPOW2;
717 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
718 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
720 /* One of three options:
721 * 1: Do the same as we do with NPOT and scale the texture, (any
722 * texture ops would require the texture to be scaled which is
724 * 2: Set the texture to the maximum size (bad idea).
725 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
726 * 4: Create the surface, but allow it to be used only for DirectDraw
727 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
728 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
729 * the render target. */
730 if (surface->resource.pool == WINED3DPOOL_DEFAULT || surface->resource.pool == WINED3DPOOL_MANAGED)
732 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
733 return WINED3DERR_NOTAVAILABLE;
736 /* We should never use this surface in combination with OpenGL! */
737 TRACE("Creating an oversized surface: %ux%u.\n",
738 surface->pow2Width, surface->pow2Height);
742 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
743 * and EXT_PALETTED_TEXTURE is used in combination with texture
744 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
745 * EXT_PALETTED_TEXTURE doesn't work in combination with
746 * ARB_TEXTURE_RECTANGLE. */
747 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
748 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
749 && gl_info->supported[EXT_PALETTED_TEXTURE]
750 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
752 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
753 surface->pow2Width = surface->resource.width;
754 surface->pow2Height = surface->resource.height;
755 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
759 switch (wined3d_settings.offscreen_rendering_mode)
762 surface->get_drawable_size = get_drawable_size_fbo;
766 surface->get_drawable_size = get_drawable_size_backbuffer;
770 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
771 return WINED3DERR_INVALIDCALL;
774 surface->flags |= SFLAG_INSYSMEM;
779 static void surface_realize_palette(struct wined3d_surface *surface)
781 struct wined3d_palette *palette = surface->palette;
783 TRACE("surface %p.\n", surface);
785 if (!palette) return;
787 if (surface->resource.format->id == WINED3DFMT_P8_UINT
788 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
790 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
792 /* Make sure the texture is up to date. This call doesn't do
793 * anything if the texture is already up to date. */
794 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
796 /* We want to force a palette refresh, so mark the drawable as not being up to date */
797 if (!surface_is_offscreen(surface))
798 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
802 if (!(surface->flags & SFLAG_INSYSMEM))
804 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
805 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
807 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
811 if (surface->flags & SFLAG_DIBSECTION)
816 TRACE("Updating the DC's palette.\n");
818 for (i = 0; i < 256; ++i)
820 col[i].rgbRed = palette->palents[i].peRed;
821 col[i].rgbGreen = palette->palents[i].peGreen;
822 col[i].rgbBlue = palette->palents[i].peBlue;
823 col[i].rgbReserved = 0;
825 SetDIBColorTable(surface->hDC, 0, 256, col);
828 /* Propagate the changes to the drawable when we have a palette. */
829 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
830 surface_load_location(surface, surface->draw_binding, NULL);
833 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
837 /* If there's no destination surface there is nothing to do. */
838 if (!surface->overlay_dest)
841 /* Blt calls ModifyLocation on the dest surface, which in turn calls
842 * DrawOverlay to update the overlay. Prevent an endless recursion. */
843 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
846 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
847 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
848 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3DTEXF_LINEAR);
849 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
854 static void surface_preload(struct wined3d_surface *surface)
856 TRACE("surface %p.\n", surface);
858 surface_internal_preload(surface, SRGB_ANY);
861 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
863 struct wined3d_device *device = surface->resource.device;
864 const RECT *pass_rect = rect;
866 TRACE("surface %p, rect %s, flags %#x.\n",
867 surface, wine_dbgstr_rect(rect), flags);
869 if (flags & WINED3DLOCK_DISCARD)
871 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
872 surface_prepare_system_memory(surface);
873 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
877 /* surface_load_location() does not check if the rectangle specifies
878 * the full surface. Most callers don't need that, so do it here. */
879 if (rect && !rect->top && !rect->left
880 && rect->right == surface->resource.width
881 && rect->bottom == surface->resource.height)
884 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
885 && ((surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
886 || surface == device->fb.render_targets[0])))
887 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
890 if (surface->flags & SFLAG_PBO)
892 const struct wined3d_gl_info *gl_info;
893 struct wined3d_context *context;
895 context = context_acquire(device, NULL);
896 gl_info = context->gl_info;
899 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
900 checkGLcall("glBindBufferARB");
902 /* This shouldn't happen but could occur if some other function
903 * didn't handle the PBO properly. */
904 if (surface->resource.allocatedMemory)
905 ERR("The surface already has PBO memory allocated.\n");
907 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
908 checkGLcall("glMapBufferARB");
910 /* Make sure the PBO isn't set anymore in order not to break non-PBO
912 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
913 checkGLcall("glBindBufferARB");
916 context_release(context);
919 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
922 surface_add_dirty_rect(surface, NULL);
929 b.Right = rect->right;
930 b.Bottom = rect->bottom;
933 surface_add_dirty_rect(surface, &b);
938 static void surface_unmap(struct wined3d_surface *surface)
940 struct wined3d_device *device = surface->resource.device;
943 TRACE("surface %p.\n", surface);
945 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
947 if (surface->flags & SFLAG_PBO)
949 const struct wined3d_gl_info *gl_info;
950 struct wined3d_context *context;
952 TRACE("Freeing PBO memory.\n");
954 context = context_acquire(device, NULL);
955 gl_info = context->gl_info;
958 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
959 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
960 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
961 checkGLcall("glUnmapBufferARB");
963 context_release(context);
965 surface->resource.allocatedMemory = NULL;
968 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
970 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
972 TRACE("Not dirtified, nothing to do.\n");
976 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
977 || (device->fb.render_targets && surface == device->fb.render_targets[0]))
979 if (wined3d_settings.rendertargetlock_mode == RTL_DISABLE)
981 static BOOL warned = FALSE;
984 ERR("The application tries to write to the render target, but render target locking is disabled.\n");
990 if (!surface->dirtyRect.left && !surface->dirtyRect.top
991 && surface->dirtyRect.right == surface->resource.width
992 && surface->dirtyRect.bottom == surface->resource.height)
998 /* TODO: Proper partial rectangle tracking. */
1000 surface->flags |= SFLAG_INSYSMEM;
1003 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
1005 /* Partial rectangle tracking is not commonly implemented, it is only
1006 * done for render targets. INSYSMEM was set before to tell
1007 * surface_load_location() where to read the rectangle from.
1008 * Indrawable is set because all modifications from the partial
1009 * sysmem copy are written back to the drawable, thus the surface is
1010 * merged again in the drawable. The sysmem copy is not fully up to
1011 * date because only a subrectangle was read in Map(). */
1014 surface_modify_location(surface, surface->draw_binding, TRUE);
1015 surface_evict_sysmem(surface);
1018 surface->dirtyRect.left = surface->resource.width;
1019 surface->dirtyRect.top = surface->resource.height;
1020 surface->dirtyRect.right = 0;
1021 surface->dirtyRect.bottom = 0;
1023 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
1025 FIXME("Depth / stencil buffer locking is not implemented.\n");
1029 /* Overlays have to be redrawn manually after changes with the GL implementation */
1030 if (surface->overlay_dest)
1031 surface->surface_ops->surface_draw_overlay(surface);
1034 static HRESULT surface_getdc(struct wined3d_surface *surface)
1036 WINED3DLOCKED_RECT lock;
1039 TRACE("surface %p.\n", surface);
1041 /* Create a DIB section if there isn't a dc yet. */
1044 if (surface->flags & SFLAG_CLIENT)
1046 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1047 surface_release_client_storage(surface);
1049 hr = surface_create_dib_section(surface);
1051 return WINED3DERR_INVALIDCALL;
1053 /* Use the DIB section from now on if we are not using a PBO. */
1054 if (!(surface->flags & SFLAG_PBO))
1055 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1058 /* Map the surface. */
1059 hr = wined3d_surface_map(surface, &lock, NULL, 0);
1061 ERR("Map failed, hr %#x.\n", hr);
1063 /* Sync the DIB with the PBO. This can't be done earlier because Map()
1064 * activates the allocatedMemory. */
1065 if (surface->flags & SFLAG_PBO)
1066 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
1071 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1073 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1075 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1080 static void wined3d_surface_depth_blt_fbo(struct wined3d_device *device, struct wined3d_surface *src_surface,
1081 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1083 const struct wined3d_gl_info *gl_info;
1084 struct wined3d_context *context;
1085 DWORD src_mask, dst_mask;
1088 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1089 device, src_surface, wine_dbgstr_rect(src_rect),
1090 dst_surface, wine_dbgstr_rect(dst_rect));
1092 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1093 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1095 if (src_mask != dst_mask)
1097 ERR("Incompatible formats %s and %s.\n",
1098 debug_d3dformat(src_surface->resource.format->id),
1099 debug_d3dformat(dst_surface->resource.format->id));
1105 ERR("Not a depth / stencil format: %s.\n",
1106 debug_d3dformat(src_surface->resource.format->id));
1111 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1112 gl_mask |= GL_DEPTH_BUFFER_BIT;
1113 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1114 gl_mask |= GL_STENCIL_BUFFER_BIT;
1116 /* Make sure the locations are up-to-date. Loading the destination
1117 * surface isn't required if the entire surface is overwritten. */
1118 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1119 if (!surface_is_full_rect(dst_surface, dst_rect))
1120 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1122 context = context_acquire(device, NULL);
1123 if (!context->valid)
1125 context_release(context);
1126 WARN("Invalid context, skipping blit.\n");
1130 gl_info = context->gl_info;
1134 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1135 glReadBuffer(GL_NONE);
1136 checkGLcall("glReadBuffer()");
1137 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1139 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1140 context_set_draw_buffer(context, GL_NONE);
1141 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1143 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1145 glDepthMask(GL_TRUE);
1146 context_invalidate_state(context, STATE_RENDER(WINED3DRS_ZWRITEENABLE));
1148 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1150 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1152 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1153 context_invalidate_state(context, STATE_RENDER(WINED3DRS_TWOSIDEDSTENCILMODE));
1156 context_invalidate_state(context, STATE_RENDER(WINED3DRS_STENCILWRITEMASK));
1159 glDisable(GL_SCISSOR_TEST);
1160 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1162 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1163 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1164 checkGLcall("glBlitFramebuffer()");
1168 if (wined3d_settings.strict_draw_ordering)
1169 wglFlush(); /* Flush to ensure ordering across contexts. */
1171 context_release(context);
1174 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1175 * Depth / stencil is not supported. */
1176 static void surface_blt_fbo(struct wined3d_device *device, const WINED3DTEXTUREFILTERTYPE filter,
1177 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1178 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1180 const struct wined3d_gl_info *gl_info;
1181 struct wined3d_context *context;
1182 RECT src_rect, dst_rect;
1186 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1187 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1188 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1189 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1190 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1192 src_rect = *src_rect_in;
1193 dst_rect = *dst_rect_in;
1197 case WINED3DTEXF_LINEAR:
1198 gl_filter = GL_LINEAR;
1202 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1203 case WINED3DTEXF_NONE:
1204 case WINED3DTEXF_POINT:
1205 gl_filter = GL_NEAREST;
1209 /* Resolve the source surface first if needed. */
1210 if (src_location == SFLAG_INRB_MULTISAMPLE
1211 && (src_surface->resource.format->id != dst_surface->resource.format->id
1212 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1213 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1214 src_location = SFLAG_INRB_RESOLVED;
1216 /* Make sure the locations are up-to-date. Loading the destination
1217 * surface isn't required if the entire surface is overwritten. (And is
1218 * in fact harmful if we're being called by surface_load_location() with
1219 * the purpose of loading the destination surface.) */
1220 surface_load_location(src_surface, src_location, NULL);
1221 if (!surface_is_full_rect(dst_surface, &dst_rect))
1222 surface_load_location(dst_surface, dst_location, NULL);
1224 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1225 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1226 else context = context_acquire(device, NULL);
1228 if (!context->valid)
1230 context_release(context);
1231 WARN("Invalid context, skipping blit.\n");
1235 gl_info = context->gl_info;
1237 if (src_location == SFLAG_INDRAWABLE)
1239 TRACE("Source surface %p is onscreen.\n", src_surface);
1240 buffer = surface_get_gl_buffer(src_surface);
1241 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1245 TRACE("Source surface %p is offscreen.\n", src_surface);
1246 buffer = GL_COLOR_ATTACHMENT0;
1250 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1251 glReadBuffer(buffer);
1252 checkGLcall("glReadBuffer()");
1253 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1256 if (dst_location == SFLAG_INDRAWABLE)
1258 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1259 buffer = surface_get_gl_buffer(dst_surface);
1260 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1264 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1265 buffer = GL_COLOR_ATTACHMENT0;
1269 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1270 context_set_draw_buffer(context, buffer);
1271 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1272 context_invalidate_state(context, STATE_FRAMEBUFFER);
1274 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1275 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE));
1276 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE1));
1277 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE2));
1278 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE3));
1280 glDisable(GL_SCISSOR_TEST);
1281 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1283 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1284 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1285 checkGLcall("glBlitFramebuffer()");
1289 if (wined3d_settings.strict_draw_ordering
1290 || (dst_location == SFLAG_INDRAWABLE
1291 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1294 context_release(context);
1297 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1298 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
1299 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
1301 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1304 /* Source and/or destination need to be on the GL side */
1305 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
1310 case WINED3D_BLIT_OP_COLOR_BLIT:
1311 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1313 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1317 case WINED3D_BLIT_OP_DEPTH_BLIT:
1318 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1320 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1328 if (!(src_format->id == dst_format->id
1329 || (is_identity_fixup(src_format->color_fixup)
1330 && is_identity_fixup(dst_format->color_fixup))))
1336 /* This function checks if the primary render target uses the 8bit paletted format. */
1337 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1339 if (device->fb.render_targets && device->fb.render_targets[0])
1341 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1342 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1343 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1349 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1350 DWORD color, WINED3DCOLORVALUE *float_color)
1352 const struct wined3d_format *format = surface->resource.format;
1353 const struct wined3d_device *device = surface->resource.device;
1357 case WINED3DFMT_P8_UINT:
1358 if (surface->palette)
1360 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1361 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1362 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1366 float_color->r = 0.0f;
1367 float_color->g = 0.0f;
1368 float_color->b = 0.0f;
1370 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1373 case WINED3DFMT_B5G6R5_UNORM:
1374 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1375 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1376 float_color->b = (color & 0x1f) / 31.0f;
1377 float_color->a = 1.0f;
1380 case WINED3DFMT_B8G8R8_UNORM:
1381 case WINED3DFMT_B8G8R8X8_UNORM:
1382 float_color->r = D3DCOLOR_R(color);
1383 float_color->g = D3DCOLOR_G(color);
1384 float_color->b = D3DCOLOR_B(color);
1385 float_color->a = 1.0f;
1388 case WINED3DFMT_B8G8R8A8_UNORM:
1389 float_color->r = D3DCOLOR_R(color);
1390 float_color->g = D3DCOLOR_G(color);
1391 float_color->b = D3DCOLOR_B(color);
1392 float_color->a = D3DCOLOR_A(color);
1396 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1403 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1405 const struct wined3d_format *format = surface->resource.format;
1409 case WINED3DFMT_S1_UINT_D15_UNORM:
1410 *float_depth = depth / (float)0x00007fff;
1413 case WINED3DFMT_D16_UNORM:
1414 *float_depth = depth / (float)0x0000ffff;
1417 case WINED3DFMT_D24_UNORM_S8_UINT:
1418 case WINED3DFMT_X8D24_UNORM:
1419 *float_depth = depth / (float)0x00ffffff;
1422 case WINED3DFMT_D32_UNORM:
1423 *float_depth = depth / (float)0xffffffff;
1427 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1434 /* Do not call while under the GL lock. */
1435 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1437 const struct wined3d_resource *resource = &surface->resource;
1438 struct wined3d_device *device = resource->device;
1439 const struct blit_shader *blitter;
1441 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1442 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1445 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1446 return WINED3DERR_INVALIDCALL;
1449 return blitter->depth_fill(device, surface, rect, depth);
1452 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1453 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1455 struct wined3d_device *device = src_surface->resource.device;
1457 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1458 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1459 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1460 return WINED3DERR_INVALIDCALL;
1462 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1464 surface_modify_ds_location(dst_surface, SFLAG_DS_OFFSCREEN,
1465 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1466 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
1471 /* Do not call while under the GL lock. */
1472 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1473 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1474 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1476 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1477 struct wined3d_device *device = dst_surface->resource.device;
1478 DWORD src_ds_flags, dst_ds_flags;
1479 RECT src_rect, dst_rect;
1480 BOOL scale, convert;
1482 static const DWORD simple_blit = WINEDDBLT_ASYNC
1483 | WINEDDBLT_COLORFILL
1485 | WINEDDBLT_DEPTHFILL
1486 | WINEDDBLT_DONOTWAIT;
1488 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1489 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1490 flags, fx, debug_d3dtexturefiltertype(filter));
1491 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1495 TRACE("dwSize %#x.\n", fx->dwSize);
1496 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
1497 TRACE("dwROP %#x.\n", fx->dwROP);
1498 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
1499 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
1500 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
1501 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
1502 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
1503 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
1504 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
1505 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
1506 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
1507 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
1508 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
1509 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
1510 TRACE("dwReserved %#x.\n", fx->dwReserved);
1511 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
1512 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
1513 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
1514 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
1515 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
1516 TRACE("ddckDestColorkey {%#x, %#x}.\n",
1517 fx->ddckDestColorkey.dwColorSpaceLowValue,
1518 fx->ddckDestColorkey.dwColorSpaceHighValue);
1519 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
1520 fx->ddckSrcColorkey.dwColorSpaceLowValue,
1521 fx->ddckSrcColorkey.dwColorSpaceHighValue);
1524 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1526 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1527 return WINEDDERR_SURFACEBUSY;
1530 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1532 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1533 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1534 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1535 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1536 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1538 /* The destination rect can be out of bounds on the condition
1539 * that a clipper is set for the surface. */
1540 if (dst_surface->clipper)
1541 FIXME("Blit clipping not implemented.\n");
1543 WARN("The application gave us a bad destination rectangle without a clipper set.\n");
1544 return WINEDDERR_INVALIDRECT;
1549 surface_get_rect(src_surface, src_rect_in, &src_rect);
1551 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1552 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1553 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1554 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1555 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1557 WARN("Application gave us bad source rectangle for Blt.\n");
1558 return WINEDDERR_INVALIDRECT;
1563 memset(&src_rect, 0, sizeof(src_rect));
1566 if (!fx || !(fx->dwDDFX))
1567 flags &= ~WINEDDBLT_DDFX;
1569 if (flags & WINEDDBLT_WAIT)
1570 flags &= ~WINEDDBLT_WAIT;
1572 if (flags & WINEDDBLT_ASYNC)
1574 static unsigned int once;
1577 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1578 flags &= ~WINEDDBLT_ASYNC;
1581 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1582 if (flags & WINEDDBLT_DONOTWAIT)
1584 static unsigned int once;
1587 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1588 flags &= ~WINEDDBLT_DONOTWAIT;
1591 if (!device->d3d_initialized)
1593 WARN("D3D not initialized, using fallback.\n");
1597 /* We want to avoid invalidating the sysmem location for converted
1598 * surfaces, since otherwise we'd have to convert the data back when
1600 if (dst_surface->flags & SFLAG_CONVERTED)
1602 WARN("Converted surface, using CPU blit.\n");
1603 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1606 if (flags & ~simple_blit)
1608 WARN("Using fallback for complex blit (%#x).\n", flags);
1612 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1613 src_swapchain = src_surface->container.u.swapchain;
1615 src_swapchain = NULL;
1617 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1618 dst_swapchain = dst_surface->container.u.swapchain;
1620 dst_swapchain = NULL;
1622 /* This isn't strictly needed. FBO blits for example could deal with
1623 * cross-swapchain blits by first downloading the source to a texture
1624 * before switching to the destination context. We just have this here to
1625 * not have to deal with the issue, since cross-swapchain blits should be
1627 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1629 FIXME("Using fallback for cross-swapchain blit.\n");
1634 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
1635 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
1636 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
1638 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1640 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1644 if (src_ds_flags || dst_ds_flags)
1646 if (flags & WINEDDBLT_DEPTHFILL)
1650 TRACE("Depth fill.\n");
1652 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1653 return WINED3DERR_INVALIDCALL;
1655 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1660 /* Accessing depth / stencil surfaces is supposed to fail while in
1661 * a scene, except for fills, which seem to work. */
1662 if (device->inScene)
1664 WARN("Rejecting depth / stencil access while in scene.\n");
1665 return WINED3DERR_INVALIDCALL;
1668 if (src_ds_flags != dst_ds_flags)
1670 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1671 return WINED3DERR_INVALIDCALL;
1674 if (src_rect.top || src_rect.left
1675 || src_rect.bottom != src_surface->resource.height
1676 || src_rect.right != src_surface->resource.width)
1678 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1679 wine_dbgstr_rect(&src_rect));
1680 return WINED3DERR_INVALIDCALL;
1683 if (dst_rect.top || dst_rect.left
1684 || dst_rect.bottom != dst_surface->resource.height
1685 || dst_rect.right != dst_surface->resource.width)
1687 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1688 wine_dbgstr_rect(&src_rect));
1689 return WINED3DERR_INVALIDCALL;
1694 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1695 return WINED3DERR_INVALIDCALL;
1698 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1704 /* In principle this would apply to depth blits as well, but we don't
1705 * implement those in the CPU blitter at the moment. */
1706 if ((dst_surface->flags & SFLAG_INSYSMEM)
1707 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
1710 TRACE("Not doing sysmem blit because of scaling.\n");
1712 TRACE("Not doing sysmem blit because of format conversion.\n");
1714 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1717 if (flags & WINEDDBLT_COLORFILL)
1719 WINED3DCOLORVALUE color;
1721 TRACE("Color fill.\n");
1723 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1726 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1731 TRACE("Color blit.\n");
1733 /* Use present for back -> front blits. The idea behind this is
1734 * that present is potentially faster than a blit, in particular
1735 * when FBO blits aren't available. Some ddraw applications like
1736 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
1737 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
1738 * applications can't blit directly to the frontbuffer. */
1739 if (dst_swapchain && dst_swapchain->back_buffers
1740 && dst_surface == dst_swapchain->front_buffer
1741 && src_surface == dst_swapchain->back_buffers[0])
1743 WINED3DSWAPEFFECT swap_effect = dst_swapchain->presentParms.SwapEffect;
1745 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1747 /* Set the swap effect to COPY, we don't want the backbuffer
1748 * to become undefined. */
1749 dst_swapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
1750 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
1751 dst_swapchain->presentParms.SwapEffect = swap_effect;
1756 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1757 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1758 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1760 TRACE("Using FBO blit.\n");
1762 surface_blt_fbo(device, filter,
1763 src_surface, src_surface->draw_binding, &src_rect,
1764 dst_surface, dst_surface->draw_binding, &dst_rect);
1765 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1769 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1770 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1771 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1773 TRACE("Using arbfp blit.\n");
1775 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1783 /* Special cases for render targets. */
1784 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1785 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1787 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1788 src_surface, &src_rect, flags, fx, filter)))
1794 /* For the rest call the X11 surface implementation. For render targets
1795 * this should be implemented OpenGL accelerated in BltOverride, other
1796 * blits are rather rare. */
1797 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1800 /* Do not call while under the GL lock. */
1801 HRESULT CDECL wined3d_surface_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
1802 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD trans)
1804 RECT src_rect, dst_rect;
1807 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect_in %s, trans %#x.\n",
1808 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect_in), trans);
1810 surface_get_rect(src_surface, src_rect_in, &src_rect);
1812 dst_rect.left = dst_x;
1813 dst_rect.top = dst_y;
1814 dst_rect.right = dst_x + src_rect.right - src_rect.left;
1815 dst_rect.bottom = dst_y + src_rect.bottom - src_rect.top;
1817 if (trans & WINEDDBLTFAST_SRCCOLORKEY)
1818 flags |= WINEDDBLT_KEYSRC;
1819 if (trans & WINEDDBLTFAST_DESTCOLORKEY)
1820 flags |= WINEDDBLT_KEYDEST;
1821 if (trans & WINEDDBLTFAST_WAIT)
1822 flags |= WINEDDBLT_WAIT;
1823 if (trans & WINEDDBLTFAST_DONOTWAIT)
1824 flags |= WINEDDBLT_DONOTWAIT;
1826 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, NULL, WINED3DTEXF_POINT);
1829 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1830 struct wined3d_surface *render_target)
1832 TRACE("surface %p, render_target %p.\n", surface, render_target);
1834 /* TODO: Check surface sizes, pools, etc. */
1836 if (render_target->resource.multisample_type)
1837 return WINED3DERR_INVALIDCALL;
1839 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3DTEXF_POINT);
1842 /* Context activation is done by the caller. */
1843 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1845 if (!surface->resource.heapMemory)
1847 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1848 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1849 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1853 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1854 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1855 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1856 surface->resource.size, surface->resource.allocatedMemory));
1857 checkGLcall("glGetBufferSubDataARB");
1858 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1859 checkGLcall("glDeleteBuffersARB");
1863 surface->flags &= ~SFLAG_PBO;
1866 /* Do not call while under the GL lock. */
1867 static void surface_unload(struct wined3d_resource *resource)
1869 struct wined3d_surface *surface = surface_from_resource(resource);
1870 struct wined3d_renderbuffer_entry *entry, *entry2;
1871 struct wined3d_device *device = resource->device;
1872 const struct wined3d_gl_info *gl_info;
1873 struct wined3d_context *context;
1875 TRACE("surface %p.\n", surface);
1877 if (resource->pool == WINED3DPOOL_DEFAULT)
1879 /* Default pool resources are supposed to be destroyed before Reset is called.
1880 * Implicit resources stay however. So this means we have an implicit render target
1881 * or depth stencil. The content may be destroyed, but we still have to tear down
1882 * opengl resources, so we cannot leave early.
1884 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1885 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1886 * or the depth stencil into an FBO the texture or render buffer will be removed
1887 * and all flags get lost
1889 surface_init_sysmem(surface);
1890 /* We also get here when the ddraw swapchain is destroyed, for example
1891 * for a mode switch. In this case this surface won't necessarily be
1892 * an implicit surface. We have to mark it lost so that the
1893 * application can restore it after the mode switch. */
1894 surface->flags |= SFLAG_LOST;
1898 /* Load the surface into system memory */
1899 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1900 surface_modify_location(surface, surface->draw_binding, FALSE);
1902 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1903 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1904 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1906 context = context_acquire(device, NULL);
1907 gl_info = context->gl_info;
1909 /* Destroy PBOs, but load them into real sysmem before */
1910 if (surface->flags & SFLAG_PBO)
1911 surface_remove_pbo(surface, gl_info);
1913 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1914 * all application-created targets the application has to release the surface
1915 * before calling _Reset
1917 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1920 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1922 list_remove(&entry->entry);
1923 HeapFree(GetProcessHeap(), 0, entry);
1925 list_init(&surface->renderbuffers);
1926 surface->current_renderbuffer = NULL;
1930 /* If we're in a texture, the texture name belongs to the texture.
1931 * Otherwise, destroy it. */
1932 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1934 glDeleteTextures(1, &surface->texture_name);
1935 surface->texture_name = 0;
1936 glDeleteTextures(1, &surface->texture_name_srgb);
1937 surface->texture_name_srgb = 0;
1939 if (surface->rb_multisample)
1941 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1942 surface->rb_multisample = 0;
1944 if (surface->rb_resolved)
1946 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1947 surface->rb_resolved = 0;
1952 context_release(context);
1954 resource_unload(resource);
1957 static const struct wined3d_resource_ops surface_resource_ops =
1962 static const struct wined3d_surface_ops surface_ops =
1964 surface_private_setup,
1965 surface_realize_palette,
1966 surface_draw_overlay,
1973 /*****************************************************************************
1974 * Initializes the GDI surface, aka creates the DIB section we render to
1975 * The DIB section creation is done by calling GetDC, which will create the
1976 * section and releasing the dc to allow the app to use it. The dib section
1977 * will stay until the surface is released
1979 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1980 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1981 * avoid confusion in the shared surface code.
1984 * WINED3D_OK on success
1985 * The return values of called methods on failure
1987 *****************************************************************************/
1988 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1992 TRACE("surface %p.\n", surface);
1994 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1996 ERR("Overlays not yet supported by GDI surfaces.\n");
1997 return WINED3DERR_INVALIDCALL;
2000 /* Sysmem textures have memory already allocated - release it,
2001 * this avoids an unnecessary memcpy. */
2002 hr = surface_create_dib_section(surface);
2005 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
2006 surface->resource.heapMemory = NULL;
2007 surface->resource.allocatedMemory = surface->dib.bitmap_data;
2010 /* We don't mind the nonpow2 stuff in GDI. */
2011 surface->pow2Width = surface->resource.width;
2012 surface->pow2Height = surface->resource.height;
2017 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
2019 struct wined3d_palette *palette = surface->palette;
2021 TRACE("surface %p.\n", surface);
2023 if (!palette) return;
2025 if (surface->flags & SFLAG_DIBSECTION)
2030 TRACE("Updating the DC's palette.\n");
2032 for (i = 0; i < 256; ++i)
2034 col[i].rgbRed = palette->palents[i].peRed;
2035 col[i].rgbGreen = palette->palents[i].peGreen;
2036 col[i].rgbBlue = palette->palents[i].peBlue;
2037 col[i].rgbReserved = 0;
2039 SetDIBColorTable(surface->hDC, 0, 256, col);
2042 /* Update the image because of the palette change. Some games like e.g.
2043 * Red Alert call SetEntries a lot to implement fading. */
2044 /* Tell the swapchain to update the screen. */
2045 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2047 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2048 if (surface == swapchain->front_buffer)
2050 x11_copy_to_screen(swapchain, NULL);
2055 static HRESULT gdi_surface_draw_overlay(struct wined3d_surface *surface)
2057 FIXME("GDI surfaces can't draw overlays yet.\n");
2061 static void gdi_surface_preload(struct wined3d_surface *surface)
2063 TRACE("surface %p.\n", surface);
2065 ERR("Preloading GDI surfaces is not supported.\n");
2068 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
2070 TRACE("surface %p, rect %s, flags %#x.\n",
2071 surface, wine_dbgstr_rect(rect), flags);
2073 if (!surface->resource.allocatedMemory)
2075 /* This happens on gdi surfaces if the application set a user pointer
2076 * and resets it. Recreate the DIB section. */
2077 surface_create_dib_section(surface);
2078 surface->resource.allocatedMemory = surface->dib.bitmap_data;
2082 static void gdi_surface_unmap(struct wined3d_surface *surface)
2084 TRACE("surface %p.\n", surface);
2086 /* Tell the swapchain to update the screen. */
2087 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2089 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2090 if (surface == swapchain->front_buffer)
2092 x11_copy_to_screen(swapchain, &surface->lockedRect);
2096 memset(&surface->lockedRect, 0, sizeof(RECT));
2099 static HRESULT gdi_surface_getdc(struct wined3d_surface *surface)
2101 WINED3DLOCKED_RECT lock;
2104 TRACE("surface %p.\n", surface);
2106 /* Should have a DIB section already. */
2107 if (!(surface->flags & SFLAG_DIBSECTION))
2109 WARN("DC not supported on this surface\n");
2110 return WINED3DERR_INVALIDCALL;
2113 /* Map the surface. */
2114 hr = wined3d_surface_map(surface, &lock, NULL, 0);
2116 ERR("Map failed, hr %#x.\n", hr);
2121 static const struct wined3d_surface_ops gdi_surface_ops =
2123 gdi_surface_private_setup,
2124 gdi_surface_realize_palette,
2125 gdi_surface_draw_overlay,
2126 gdi_surface_preload,
2132 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2137 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2141 name = &surface->texture_name_srgb;
2142 flag = SFLAG_INSRGBTEX;
2146 name = &surface->texture_name;
2147 flag = SFLAG_INTEXTURE;
2150 if (!*name && new_name)
2152 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2153 * surface has no texture name yet. See if we can get rid of this. */
2154 if (surface->flags & flag)
2155 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2156 surface_modify_location(surface, flag, FALSE);
2160 surface_force_reload(surface);
2163 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2165 TRACE("surface %p, target %#x.\n", surface, target);
2167 if (surface->texture_target != target)
2169 if (target == GL_TEXTURE_RECTANGLE_ARB)
2171 surface->flags &= ~SFLAG_NORMCOORD;
2173 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2175 surface->flags |= SFLAG_NORMCOORD;
2178 surface->texture_target = target;
2179 surface_force_reload(surface);
2182 /* Context activation is done by the caller. */
2183 void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
2185 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
2187 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2189 struct wined3d_texture *texture = surface->container.u.texture;
2191 TRACE("Passing to container (%p).\n", texture);
2192 texture->texture_ops->texture_bind(texture, context, srgb);
2196 if (surface->texture_level)
2198 ERR("Standalone surface %p is non-zero texture level %u.\n",
2199 surface, surface->texture_level);
2203 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2207 if (!surface->texture_name)
2209 glGenTextures(1, &surface->texture_name);
2210 checkGLcall("glGenTextures");
2212 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2214 context_bind_texture(context, surface->texture_target, surface->texture_name);
2215 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2216 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2217 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2218 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2219 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2220 checkGLcall("glTexParameteri");
2224 context_bind_texture(context, surface->texture_target, surface->texture_name);
2231 /* This call just downloads data, the caller is responsible for binding the
2232 * correct texture. */
2233 /* Context activation is done by the caller. */
2234 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2236 const struct wined3d_format *format = surface->resource.format;
2238 /* Only support read back of converted P8 surfaces. */
2239 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2241 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
2247 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2249 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2250 surface, surface->texture_level, format->glFormat, format->glType,
2251 surface->resource.allocatedMemory);
2253 if (surface->flags & SFLAG_PBO)
2255 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2256 checkGLcall("glBindBufferARB");
2257 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2258 checkGLcall("glGetCompressedTexImageARB");
2259 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2260 checkGLcall("glBindBufferARB");
2264 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2265 surface->texture_level, surface->resource.allocatedMemory));
2266 checkGLcall("glGetCompressedTexImageARB");
2274 GLenum gl_format = format->glFormat;
2275 GLenum gl_type = format->glType;
2279 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2280 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2282 gl_format = GL_ALPHA;
2283 gl_type = GL_UNSIGNED_BYTE;
2286 if (surface->flags & SFLAG_NONPOW2)
2288 unsigned char alignment = surface->resource.device->surface_alignment;
2289 src_pitch = format->byte_count * surface->pow2Width;
2290 dst_pitch = wined3d_surface_get_pitch(surface);
2291 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2292 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2296 mem = surface->resource.allocatedMemory;
2299 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2300 surface, surface->texture_level, gl_format, gl_type, mem);
2302 if (surface->flags & SFLAG_PBO)
2304 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2305 checkGLcall("glBindBufferARB");
2307 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2308 checkGLcall("glGetTexImage");
2310 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2311 checkGLcall("glBindBufferARB");
2315 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2316 checkGLcall("glGetTexImage");
2320 if (surface->flags & SFLAG_NONPOW2)
2322 const BYTE *src_data;
2326 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2327 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2328 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2330 * We're doing this...
2332 * instead of boxing the texture :
2333 * |<-texture width ->| -->pow2width| /\
2334 * |111111111111111111| | |
2335 * |222 Texture 222222| boxed empty | texture height
2336 * |3333 Data 33333333| | |
2337 * |444444444444444444| | \/
2338 * ----------------------------------- |
2339 * | boxed empty | boxed empty | pow2height
2341 * -----------------------------------
2344 * we're repacking the data to the expected texture width
2346 * |<-texture width ->| -->pow2width| /\
2347 * |111111111111111111222222222222222| |
2348 * |222333333333333333333444444444444| texture height
2352 * | empty | pow2height
2354 * -----------------------------------
2358 * |<-texture width ->| /\
2359 * |111111111111111111|
2360 * |222222222222222222|texture height
2361 * |333333333333333333|
2362 * |444444444444444444| \/
2363 * --------------------
2365 * this also means that any references to allocatedMemory should work with the data as if were a
2366 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2368 * internally the texture is still stored in a boxed format so any references to textureName will
2369 * get a boxed texture with width pow2width and not a texture of width resource.width.
2371 * Performance should not be an issue, because applications normally do not lock the surfaces when
2372 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2373 * and doesn't have to be re-read. */
2375 dst_data = surface->resource.allocatedMemory;
2376 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2377 for (y = 1; y < surface->resource.height; ++y)
2379 /* skip the first row */
2380 src_data += src_pitch;
2381 dst_data += dst_pitch;
2382 memcpy(dst_data, src_data, dst_pitch);
2385 HeapFree(GetProcessHeap(), 0, mem);
2389 /* Surface has now been downloaded */
2390 surface->flags |= SFLAG_INSYSMEM;
2393 /* This call just uploads data, the caller is responsible for binding the
2394 * correct texture. */
2395 /* Context activation is done by the caller. */
2396 void surface_upload_data(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2397 const struct wined3d_format *format, const RECT *src_rect, UINT src_w, const POINT *dst_point,
2398 BOOL srgb, const struct wined3d_bo_address *data)
2400 UINT update_w = src_rect->right - src_rect->left;
2401 UINT update_h = src_rect->bottom - src_rect->top;
2403 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_w %u, dst_point %p, srgb %#x, data {%#x:%p}.\n",
2404 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_w,
2405 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2407 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2408 update_h *= format->heightscale;
2412 if (data->buffer_object)
2414 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2415 checkGLcall("glBindBufferARB");
2418 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2420 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2421 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2422 UINT src_pitch = wined3d_format_calculate_size(format, 1, src_w, 1);
2423 const BYTE *addr = data->addr;
2426 addr += (src_rect->top / format->block_height) * src_pitch;
2427 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2430 internal = format->glGammaInternal;
2431 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2432 internal = format->rtInternal;
2434 internal = format->glInternal;
2436 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2437 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2438 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2440 if (row_length == src_pitch)
2442 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2443 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2449 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2450 * can't use the unpack row length like below. */
2451 for (row = 0, y = dst_point->y; row < row_count; ++row)
2453 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2454 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2455 y += format->block_height;
2459 checkGLcall("glCompressedTexSubImage2DARB");
2463 const BYTE *addr = data->addr;
2465 addr += src_rect->top * src_w * format->byte_count;
2466 addr += src_rect->left * format->byte_count;
2468 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2469 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2470 update_w, update_h, format->glFormat, format->glType, addr);
2472 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_w);
2473 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2474 update_w, update_h, format->glFormat, format->glType, addr);
2475 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2476 checkGLcall("glTexSubImage2D");
2479 if (data->buffer_object)
2481 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2482 checkGLcall("glBindBufferARB");
2487 if (wined3d_settings.strict_draw_ordering)
2490 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2492 struct wined3d_device *device = surface->resource.device;
2495 for (i = 0; i < device->context_count; ++i)
2497 context_surface_update(device->contexts[i], surface);
2502 /* This call just allocates the texture, the caller is responsible for binding
2503 * the correct texture. */
2504 /* Context activation is done by the caller. */
2505 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2506 const struct wined3d_format *format, BOOL srgb)
2508 BOOL enable_client_storage = FALSE;
2509 GLsizei width = surface->pow2Width;
2510 GLsizei height = surface->pow2Height;
2511 const BYTE *mem = NULL;
2516 internal = format->glGammaInternal;
2518 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2520 internal = format->rtInternal;
2524 internal = format->glInternal;
2527 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2529 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",
2530 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2531 internal, width, height, format->glFormat, format->glType);
2535 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2537 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2538 || !surface->resource.allocatedMemory)
2540 /* In some cases we want to disable client storage.
2541 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2542 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2543 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2544 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2546 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2547 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2548 surface->flags &= ~SFLAG_CLIENT;
2549 enable_client_storage = TRUE;
2553 surface->flags |= SFLAG_CLIENT;
2555 /* Point OpenGL to our allocated texture memory. Do not use
2556 * resource.allocatedMemory here because it might point into a
2557 * PBO. Instead use heapMemory, but get the alignment right. */
2558 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2559 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2563 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2565 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2566 internal, width, height, 0, surface->resource.size, mem));
2567 checkGLcall("glCompressedTexImage2DARB");
2571 glTexImage2D(surface->texture_target, surface->texture_level,
2572 internal, width, height, 0, format->glFormat, format->glType, mem);
2573 checkGLcall("glTexImage2D");
2576 if(enable_client_storage) {
2577 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2578 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2583 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2584 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2585 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2586 /* GL locking is done by the caller */
2587 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2589 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2590 struct wined3d_renderbuffer_entry *entry;
2591 GLuint renderbuffer = 0;
2592 unsigned int src_width, src_height;
2593 unsigned int width, height;
2595 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2597 width = rt->pow2Width;
2598 height = rt->pow2Height;
2602 width = surface->pow2Width;
2603 height = surface->pow2Height;
2606 src_width = surface->pow2Width;
2607 src_height = surface->pow2Height;
2609 /* A depth stencil smaller than the render target is not valid */
2610 if (width > src_width || height > src_height) return;
2612 /* Remove any renderbuffer set if the sizes match */
2613 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2614 || (width == src_width && height == src_height))
2616 surface->current_renderbuffer = NULL;
2620 /* Look if we've already got a renderbuffer of the correct dimensions */
2621 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2623 if (entry->width == width && entry->height == height)
2625 renderbuffer = entry->id;
2626 surface->current_renderbuffer = entry;
2633 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2634 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2635 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2636 surface->resource.format->glInternal, width, height);
2638 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2639 entry->width = width;
2640 entry->height = height;
2641 entry->id = renderbuffer;
2642 list_add_head(&surface->renderbuffers, &entry->entry);
2644 surface->current_renderbuffer = entry;
2647 checkGLcall("set_compatible_renderbuffer");
2650 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2652 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2654 TRACE("surface %p.\n", surface);
2656 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2658 ERR("Surface %p is not on a swapchain.\n", surface);
2662 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2664 if (swapchain->render_to_fbo)
2666 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2667 return GL_COLOR_ATTACHMENT0;
2669 TRACE("Returning GL_BACK\n");
2672 else if (surface == swapchain->front_buffer)
2674 TRACE("Returning GL_FRONT\n");
2678 FIXME("Higher back buffer, returning GL_BACK\n");
2682 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2683 void surface_add_dirty_rect(struct wined3d_surface *surface, const WINED3DBOX *dirty_rect)
2685 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2687 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2688 /* No partial locking for textures yet. */
2689 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2691 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2694 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->Left);
2695 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->Top);
2696 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->Right);
2697 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->Bottom);
2701 surface->dirtyRect.left = 0;
2702 surface->dirtyRect.top = 0;
2703 surface->dirtyRect.right = surface->resource.width;
2704 surface->dirtyRect.bottom = surface->resource.height;
2707 /* if the container is a texture then mark it dirty. */
2708 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2710 TRACE("Passing to container.\n");
2711 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2715 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2717 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2720 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2722 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2724 ERR("Not supported on scratch surfaces.\n");
2725 return WINED3DERR_INVALIDCALL;
2728 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2730 /* Reload if either the texture and sysmem have different ideas about the
2731 * color key, or the actual key values changed. */
2732 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2733 && (surface->glCKey.dwColorSpaceLowValue != surface->SrcBltCKey.dwColorSpaceLowValue
2734 || surface->glCKey.dwColorSpaceHighValue != surface->SrcBltCKey.dwColorSpaceHighValue)))
2736 TRACE("Reloading because of color keying\n");
2737 /* To perform the color key conversion we need a sysmem copy of
2738 * the surface. Make sure we have it. */
2740 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2741 /* Make sure the texture is reloaded because of the color key change,
2742 * this kills performance though :( */
2743 /* TODO: This is not necessarily needed with hw palettized texture support. */
2744 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2745 /* Switching color keying on / off may change the internal format. */
2747 surface_force_reload(surface);
2749 else if (!(surface->flags & flag))
2751 TRACE("Reloading because surface is dirty.\n");
2755 TRACE("surface is already in texture\n");
2759 /* No partial locking for textures yet. */
2760 surface_load_location(surface, flag, NULL);
2761 surface_evict_sysmem(surface);
2766 /* See also float_16_to_32() in wined3d_private.h */
2767 static inline unsigned short float_32_to_16(const float *in)
2770 float tmp = fabsf(*in);
2771 unsigned int mantissa;
2774 /* Deal with special numbers */
2780 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2782 if (tmp < powf(2, 10))
2788 } while (tmp < powf(2, 10));
2790 else if (tmp >= powf(2, 11))
2796 } while (tmp >= powf(2, 11));
2799 mantissa = (unsigned int)tmp;
2800 if (tmp - mantissa >= 0.5f)
2801 ++mantissa; /* Round to nearest, away from zero. */
2803 exp += 10; /* Normalize the mantissa. */
2804 exp += 15; /* Exponent is encoded with excess 15. */
2806 if (exp > 30) /* too big */
2808 ret = 0x7c00; /* INF */
2812 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2815 mantissa = mantissa >> 1;
2818 ret = mantissa & 0x3ff;
2822 ret = (exp << 10) | (mantissa & 0x3ff);
2825 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2829 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2833 TRACE("Surface %p, container %p of type %#x.\n",
2834 surface, surface->container.u.base, surface->container.type);
2836 switch (surface->container.type)
2838 case WINED3D_CONTAINER_TEXTURE:
2839 return wined3d_texture_incref(surface->container.u.texture);
2841 case WINED3D_CONTAINER_SWAPCHAIN:
2842 return wined3d_swapchain_incref(surface->container.u.swapchain);
2845 ERR("Unhandled container type %#x.\n", surface->container.type);
2846 case WINED3D_CONTAINER_NONE:
2850 refcount = InterlockedIncrement(&surface->resource.ref);
2851 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2856 /* Do not call while under the GL lock. */
2857 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2861 TRACE("Surface %p, container %p of type %#x.\n",
2862 surface, surface->container.u.base, surface->container.type);
2864 switch (surface->container.type)
2866 case WINED3D_CONTAINER_TEXTURE:
2867 return wined3d_texture_decref(surface->container.u.texture);
2869 case WINED3D_CONTAINER_SWAPCHAIN:
2870 return wined3d_swapchain_decref(surface->container.u.swapchain);
2873 ERR("Unhandled container type %#x.\n", surface->container.type);
2874 case WINED3D_CONTAINER_NONE:
2878 refcount = InterlockedDecrement(&surface->resource.ref);
2879 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2883 surface_cleanup(surface);
2884 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2886 TRACE("Destroyed surface %p.\n", surface);
2887 HeapFree(GetProcessHeap(), 0, surface);
2893 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2895 return resource_set_priority(&surface->resource, priority);
2898 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2900 return resource_get_priority(&surface->resource);
2903 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2905 TRACE("surface %p.\n", surface);
2907 surface->surface_ops->surface_preload(surface);
2910 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2912 TRACE("surface %p.\n", surface);
2914 return surface->resource.parent;
2917 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2919 TRACE("surface %p.\n", surface);
2921 return &surface->resource;
2924 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2926 TRACE("surface %p, flags %#x.\n", surface, flags);
2930 case WINEDDGBS_CANBLT:
2931 case WINEDDGBS_ISBLTDONE:
2935 return WINED3DERR_INVALIDCALL;
2939 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2941 TRACE("surface %p, flags %#x.\n", surface, flags);
2943 /* XXX: DDERR_INVALIDSURFACETYPE */
2947 case WINEDDGFS_CANFLIP:
2948 case WINEDDGFS_ISFLIPDONE:
2952 return WINED3DERR_INVALIDCALL;
2956 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2958 TRACE("surface %p.\n", surface);
2960 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2961 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2964 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2966 TRACE("surface %p.\n", surface);
2968 surface->flags &= ~SFLAG_LOST;
2972 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2974 TRACE("surface %p, palette %p.\n", surface, palette);
2976 if (surface->palette == palette)
2978 TRACE("Nop palette change.\n");
2982 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2983 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2985 surface->palette = palette;
2989 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2990 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
2992 surface->surface_ops->surface_realize_palette(surface);
2998 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
2999 DWORD flags, const WINEDDCOLORKEY *color_key)
3001 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3003 if (flags & WINEDDCKEY_COLORSPACE)
3005 FIXME(" colorkey value not supported (%08x) !\n", flags);
3006 return WINED3DERR_INVALIDCALL;
3009 /* Dirtify the surface, but only if a key was changed. */
3012 switch (flags & ~WINEDDCKEY_COLORSPACE)
3014 case WINEDDCKEY_DESTBLT:
3015 surface->DestBltCKey = *color_key;
3016 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3019 case WINEDDCKEY_DESTOVERLAY:
3020 surface->DestOverlayCKey = *color_key;
3021 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3024 case WINEDDCKEY_SRCOVERLAY:
3025 surface->SrcOverlayCKey = *color_key;
3026 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3029 case WINEDDCKEY_SRCBLT:
3030 surface->SrcBltCKey = *color_key;
3031 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3037 switch (flags & ~WINEDDCKEY_COLORSPACE)
3039 case WINEDDCKEY_DESTBLT:
3040 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3043 case WINEDDCKEY_DESTOVERLAY:
3044 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3047 case WINEDDCKEY_SRCOVERLAY:
3048 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3051 case WINEDDCKEY_SRCBLT:
3052 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3060 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3062 TRACE("surface %p.\n", surface);
3064 return surface->palette;
3067 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3069 const struct wined3d_format *format = surface->resource.format;
3072 TRACE("surface %p.\n", surface);
3074 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3076 /* Since compressed formats are block based, pitch means the amount of
3077 * bytes to the next row of block rather than the next row of pixels. */
3078 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3079 pitch = row_block_count * format->block_byte_count;
3083 unsigned char alignment = surface->resource.device->surface_alignment;
3084 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3085 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3088 TRACE("Returning %u.\n", pitch);
3093 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3095 TRACE("surface %p, mem %p.\n", surface, mem);
3097 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3099 WARN("Surface is locked or the DC is in use.\n");
3100 return WINED3DERR_INVALIDCALL;
3103 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
3104 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3106 ERR("Not supported on render targets.\n");
3107 return WINED3DERR_INVALIDCALL;
3110 if (mem && mem != surface->resource.allocatedMemory)
3112 void *release = NULL;
3114 /* Do I have to copy the old surface content? */
3115 if (surface->flags & SFLAG_DIBSECTION)
3117 SelectObject(surface->hDC, surface->dib.holdbitmap);
3118 DeleteDC(surface->hDC);
3119 /* Release the DIB section. */
3120 DeleteObject(surface->dib.DIBsection);
3121 surface->dib.bitmap_data = NULL;
3122 surface->resource.allocatedMemory = NULL;
3123 surface->hDC = NULL;
3124 surface->flags &= ~SFLAG_DIBSECTION;
3126 else if (!(surface->flags & SFLAG_USERPTR))
3128 release = surface->resource.heapMemory;
3129 surface->resource.heapMemory = NULL;
3131 surface->resource.allocatedMemory = mem;
3132 surface->flags |= SFLAG_USERPTR;
3134 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
3135 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3137 /* For client textures OpenGL has to be notified. */
3138 if (surface->flags & SFLAG_CLIENT)
3139 surface_release_client_storage(surface);
3141 /* Now free the old memory if any. */
3142 HeapFree(GetProcessHeap(), 0, release);
3144 else if (surface->flags & SFLAG_USERPTR)
3146 /* HeapMemory should be NULL already. */
3147 if (surface->resource.heapMemory)
3148 ERR("User pointer surface has heap memory allocated.\n");
3152 surface->resource.allocatedMemory = NULL;
3153 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
3155 if (surface->flags & SFLAG_CLIENT)
3156 surface_release_client_storage(surface);
3158 surface_prepare_system_memory(surface);
3161 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3167 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3171 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3173 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3175 WARN("Not an overlay surface.\n");
3176 return WINEDDERR_NOTAOVERLAYSURFACE;
3179 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3180 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3181 surface->overlay_destrect.left = x;
3182 surface->overlay_destrect.top = y;
3183 surface->overlay_destrect.right = x + w;
3184 surface->overlay_destrect.bottom = y + h;
3186 surface->surface_ops->surface_draw_overlay(surface);
3191 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3193 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3195 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3197 TRACE("Not an overlay surface.\n");
3198 return WINEDDERR_NOTAOVERLAYSURFACE;
3201 if (!surface->overlay_dest)
3203 TRACE("Overlay not visible.\n");
3206 return WINEDDERR_OVERLAYNOTVISIBLE;
3209 *x = surface->overlay_destrect.left;
3210 *y = surface->overlay_destrect.top;
3212 TRACE("Returning position %d, %d.\n", *x, *y);
3217 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3218 DWORD flags, struct wined3d_surface *ref)
3220 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3222 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3224 TRACE("Not an overlay surface.\n");
3225 return WINEDDERR_NOTAOVERLAYSURFACE;
3231 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3232 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3234 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3235 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3237 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3239 WARN("Not an overlay surface.\n");
3240 return WINEDDERR_NOTAOVERLAYSURFACE;
3242 else if (!dst_surface)
3244 WARN("Dest surface is NULL.\n");
3245 return WINED3DERR_INVALIDCALL;
3250 surface->overlay_srcrect = *src_rect;
3254 surface->overlay_srcrect.left = 0;
3255 surface->overlay_srcrect.top = 0;
3256 surface->overlay_srcrect.right = surface->resource.width;
3257 surface->overlay_srcrect.bottom = surface->resource.height;
3262 surface->overlay_destrect = *dst_rect;
3266 surface->overlay_destrect.left = 0;
3267 surface->overlay_destrect.top = 0;
3268 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3269 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3272 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3274 surface->overlay_dest = NULL;
3275 list_remove(&surface->overlay_entry);
3278 if (flags & WINEDDOVER_SHOW)
3280 if (surface->overlay_dest != dst_surface)
3282 surface->overlay_dest = dst_surface;
3283 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3286 else if (flags & WINEDDOVER_HIDE)
3288 /* tests show that the rectangles are erased on hide */
3289 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3290 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3291 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3292 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3293 surface->overlay_dest = NULL;
3296 surface->surface_ops->surface_draw_overlay(surface);
3301 HRESULT CDECL wined3d_surface_set_clipper(struct wined3d_surface *surface, struct wined3d_clipper *clipper)
3303 TRACE("surface %p, clipper %p.\n", surface, clipper);
3305 surface->clipper = clipper;
3310 struct wined3d_clipper * CDECL wined3d_surface_get_clipper(const struct wined3d_surface *surface)
3312 TRACE("surface %p.\n", surface);
3314 return surface->clipper;
3317 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3319 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3321 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3323 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3325 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3326 return WINED3DERR_INVALIDCALL;
3329 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3330 surface->pow2Width, surface->pow2Height);
3331 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3332 surface->resource.format = format;
3334 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3335 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3336 format->glFormat, format->glInternal, format->glType);
3341 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3342 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3344 unsigned short *dst_s;
3348 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3350 for (y = 0; y < h; ++y)
3352 src_f = (const float *)(src + y * pitch_in);
3353 dst_s = (unsigned short *) (dst + y * pitch_out);
3354 for (x = 0; x < w; ++x)
3356 dst_s[x] = float_32_to_16(src_f + x);
3361 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3362 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3364 static const unsigned char convert_5to8[] =
3366 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3367 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3368 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3369 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3371 static const unsigned char convert_6to8[] =
3373 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3374 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3375 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3376 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3377 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3378 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3379 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3380 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3384 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3386 for (y = 0; y < h; ++y)
3388 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3389 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3390 for (x = 0; x < w; ++x)
3392 WORD pixel = src_line[x];
3393 dst_line[x] = 0xff000000
3394 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3395 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3396 | convert_5to8[(pixel & 0x001f)];
3401 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3402 * in both cases we're just setting the X / Alpha channel to 0xff. */
3403 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3404 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3408 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3410 for (y = 0; y < h; ++y)
3412 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3413 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3415 for (x = 0; x < w; ++x)
3417 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3422 static inline BYTE cliptobyte(int x)
3424 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3427 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3428 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3430 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3433 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3435 for (y = 0; y < h; ++y)
3437 const BYTE *src_line = src + y * pitch_in;
3438 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3439 for (x = 0; x < w; ++x)
3441 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3442 * C = Y - 16; D = U - 128; E = V - 128;
3443 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3444 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3445 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3446 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3447 * U and V are shared between the pixels. */
3448 if (!(x & 1)) /* For every even pixel, read new U and V. */
3450 d = (int) src_line[1] - 128;
3451 e = (int) src_line[3] - 128;
3453 g2 = - 100 * d - 208 * e + 128;
3456 c2 = 298 * ((int) src_line[0] - 16);
3457 dst_line[x] = 0xff000000
3458 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3459 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3460 | cliptobyte((c2 + b2) >> 8); /* blue */
3461 /* Scale RGB values to 0..255 range,
3462 * then clip them if still not in range (may be negative),
3463 * then shift them within DWORD if necessary. */
3469 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3470 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3473 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3475 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3477 for (y = 0; y < h; ++y)
3479 const BYTE *src_line = src + y * pitch_in;
3480 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3481 for (x = 0; x < w; ++x)
3483 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3484 * C = Y - 16; D = U - 128; E = V - 128;
3485 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3486 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3487 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3488 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3489 * U and V are shared between the pixels. */
3490 if (!(x & 1)) /* For every even pixel, read new U and V. */
3492 d = (int) src_line[1] - 128;
3493 e = (int) src_line[3] - 128;
3495 g2 = - 100 * d - 208 * e + 128;
3498 c2 = 298 * ((int) src_line[0] - 16);
3499 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3500 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3501 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3502 /* Scale RGB values to 0..255 range,
3503 * then clip them if still not in range (may be negative),
3504 * then shift them within DWORD if necessary. */
3510 struct d3dfmt_convertor_desc
3512 enum wined3d_format_id from, to;
3513 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3516 static const struct d3dfmt_convertor_desc convertors[] =
3518 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3519 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3520 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3521 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3522 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3523 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3526 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3527 enum wined3d_format_id to)
3531 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3533 if (convertors[i].from == from && convertors[i].to == to)
3534 return &convertors[i];
3540 /*****************************************************************************
3541 * surface_convert_format
3543 * Creates a duplicate of a surface in a different format. Is used by Blt to
3544 * blit between surfaces with different formats.
3547 * source: Source surface
3548 * fmt: Requested destination format
3550 *****************************************************************************/
3551 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3553 const struct d3dfmt_convertor_desc *conv;
3554 WINED3DLOCKED_RECT lock_src, lock_dst;
3555 struct wined3d_surface *ret = NULL;
3558 conv = find_convertor(source->resource.format->id, to_fmt);
3561 FIXME("Cannot find a conversion function from format %s to %s.\n",
3562 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3566 wined3d_surface_create(source->resource.device, source->resource.width,
3567 source->resource.height, to_fmt, TRUE /* lockable */, TRUE /* discard */, 0 /* level */,
3568 0 /* usage */, WINED3DPOOL_SCRATCH, WINED3DMULTISAMPLE_NONE /* TODO: Multisampled conversion */,
3569 0 /* MultiSampleQuality */, source->surface_type, NULL /* parent */, &wined3d_null_parent_ops, &ret);
3572 ERR("Failed to create a destination surface for conversion.\n");
3576 memset(&lock_src, 0, sizeof(lock_src));
3577 memset(&lock_dst, 0, sizeof(lock_dst));
3579 hr = wined3d_surface_map(source, &lock_src, NULL, WINED3DLOCK_READONLY);
3582 ERR("Failed to lock the source surface.\n");
3583 wined3d_surface_decref(ret);
3586 hr = wined3d_surface_map(ret, &lock_dst, NULL, WINED3DLOCK_READONLY);
3589 ERR("Failed to lock the destination surface.\n");
3590 wined3d_surface_unmap(source);
3591 wined3d_surface_decref(ret);
3595 conv->convert(lock_src.pBits, lock_dst.pBits, lock_src.Pitch, lock_dst.Pitch,
3596 source->resource.width, source->resource.height);
3598 wined3d_surface_unmap(ret);
3599 wined3d_surface_unmap(source);
3604 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3605 unsigned int bpp, UINT pitch, DWORD color)
3612 #define COLORFILL_ROW(type) \
3614 type *d = (type *)buf; \
3615 for (x = 0; x < width; ++x) \
3616 d[x] = (type)color; \
3622 COLORFILL_ROW(BYTE);
3626 COLORFILL_ROW(WORD);
3632 for (x = 0; x < width; ++x, d += 3)
3634 d[0] = (color ) & 0xFF;
3635 d[1] = (color >> 8) & 0xFF;
3636 d[2] = (color >> 16) & 0xFF;
3641 COLORFILL_ROW(DWORD);
3645 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3646 return WINED3DERR_NOTAVAILABLE;
3649 #undef COLORFILL_ROW
3651 /* Now copy first row. */
3653 for (y = 1; y < height; ++y)
3656 memcpy(buf, first, width * bpp);
3662 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3664 TRACE("surface %p.\n", surface);
3666 if (!(surface->flags & SFLAG_LOCKED))
3668 WARN("Trying to unmap unmapped surface.\n");
3669 return WINEDDERR_NOTLOCKED;
3671 surface->flags &= ~SFLAG_LOCKED;
3673 surface->surface_ops->surface_unmap(surface);
3678 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3679 WINED3DLOCKED_RECT *locked_rect, const RECT *rect, DWORD flags)
3681 TRACE("surface %p, locked_rect %p, rect %s, flags %#x.\n",
3682 surface, locked_rect, wine_dbgstr_rect(rect), flags);
3684 if (surface->flags & SFLAG_LOCKED)
3686 WARN("Surface is already mapped.\n");
3687 return WINED3DERR_INVALIDCALL;
3689 surface->flags |= SFLAG_LOCKED;
3691 if (!(surface->flags & SFLAG_LOCKABLE))
3692 WARN("Trying to lock unlockable surface.\n");
3694 surface->surface_ops->surface_map(surface, rect, flags);
3696 locked_rect->Pitch = wined3d_surface_get_pitch(surface);
3700 locked_rect->pBits = surface->resource.allocatedMemory;
3701 surface->lockedRect.left = 0;
3702 surface->lockedRect.top = 0;
3703 surface->lockedRect.right = surface->resource.width;
3704 surface->lockedRect.bottom = surface->resource.height;
3708 const struct wined3d_format *format = surface->resource.format;
3710 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3712 /* Compressed textures are block based, so calculate the offset of
3713 * the block that contains the top-left pixel of the locked rectangle. */
3714 locked_rect->pBits = surface->resource.allocatedMemory
3715 + ((rect->top / format->block_height) * locked_rect->Pitch)
3716 + ((rect->left / format->block_width) * format->block_byte_count);
3720 locked_rect->pBits = surface->resource.allocatedMemory
3721 + (locked_rect->Pitch * rect->top)
3722 + (rect->left * format->byte_count);
3724 surface->lockedRect.left = rect->left;
3725 surface->lockedRect.top = rect->top;
3726 surface->lockedRect.right = rect->right;
3727 surface->lockedRect.bottom = rect->bottom;
3730 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3731 TRACE("Returning memory %p, pitch %u.\n", locked_rect->pBits, locked_rect->Pitch);
3736 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3740 TRACE("surface %p, dc %p.\n", surface, dc);
3742 if (surface->flags & SFLAG_USERPTR)
3744 ERR("Not supported on surfaces with application-provided memory.\n");
3745 return WINEDDERR_NODC;
3748 /* Give more detailed info for ddraw. */
3749 if (surface->flags & SFLAG_DCINUSE)
3750 return WINEDDERR_DCALREADYCREATED;
3752 /* Can't GetDC if the surface is locked. */
3753 if (surface->flags & SFLAG_LOCKED)
3754 return WINED3DERR_INVALIDCALL;
3756 hr = surface->surface_ops->surface_getdc(surface);
3760 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3761 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3763 /* GetDC on palettized formats is unsupported in D3D9, and the method
3764 * is missing in D3D8, so this should only be used for DX <=7
3765 * surfaces (with non-device palettes). */
3766 const PALETTEENTRY *pal = NULL;
3768 if (surface->palette)
3770 pal = surface->palette->palents;
3774 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3775 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3777 if (dds_primary && dds_primary->palette)
3778 pal = dds_primary->palette->palents;
3786 for (i = 0; i < 256; ++i)
3788 col[i].rgbRed = pal[i].peRed;
3789 col[i].rgbGreen = pal[i].peGreen;
3790 col[i].rgbBlue = pal[i].peBlue;
3791 col[i].rgbReserved = 0;
3793 SetDIBColorTable(surface->hDC, 0, 256, col);
3797 surface->flags |= SFLAG_DCINUSE;
3800 TRACE("Returning dc %p.\n", *dc);
3805 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3807 TRACE("surface %p, dc %p.\n", surface, dc);
3809 if (!(surface->flags & SFLAG_DCINUSE))
3810 return WINEDDERR_NODC;
3812 if (surface->hDC != dc)
3814 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3816 return WINEDDERR_NODC;
3819 /* Copy the contents of the DIB over to the PBO. */
3820 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3821 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3823 /* We locked first, so unlock now. */
3824 wined3d_surface_unmap(surface);
3826 surface->flags &= ~SFLAG_DCINUSE;
3831 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3833 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3839 FIXME("Ignoring flags %#x.\n", flags);
3841 WARN("Ignoring flags %#x.\n", flags);
3844 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
3846 ERR("Not supported on swapchain surfaces.\n");
3847 return WINEDDERR_NOTFLIPPABLE;
3850 /* Flipping is only supported on render targets and overlays. */
3851 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
3853 WARN("Tried to flip a non-render target, non-overlay surface.\n");
3854 return WINEDDERR_NOTFLIPPABLE;
3857 flip_surface(surface, override);
3859 /* Update overlays if they're visible. */
3860 if ((surface->resource.usage & WINED3DUSAGE_OVERLAY) && surface->overlay_dest)
3861 return surface->surface_ops->surface_draw_overlay(surface);
3866 /* Do not call while under the GL lock. */
3867 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3869 struct wined3d_device *device = surface->resource.device;
3871 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3873 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3875 struct wined3d_texture *texture = surface->container.u.texture;
3877 TRACE("Passing to container (%p).\n", texture);
3878 texture->texture_ops->texture_preload(texture, srgb);
3882 struct wined3d_context *context;
3884 TRACE("(%p) : About to load surface\n", surface);
3886 /* TODO: Use already acquired context when possible. */
3887 context = context_acquire(device, NULL);
3889 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3891 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3893 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3897 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3901 context_release(context);
3905 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3907 if (!surface->resource.allocatedMemory)
3909 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3910 surface->resource.size + RESOURCE_ALIGNMENT);
3911 if (!surface->resource.heapMemory)
3913 ERR("Out of memory\n");
3916 surface->resource.allocatedMemory =
3917 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
3921 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
3924 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3929 /* Read the framebuffer back into the surface */
3930 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
3932 struct wined3d_device *device = surface->resource.device;
3933 const struct wined3d_gl_info *gl_info;
3934 struct wined3d_context *context;
3938 BYTE *row, *top, *bottom;
3942 BOOL srcIsUpsideDown;
3947 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
3948 static BOOL warned = FALSE;
3950 ERR("The application tries to lock the render target, but render target locking is disabled\n");
3956 context = context_acquire(device, surface);
3957 context_apply_blit_state(context, device);
3958 gl_info = context->gl_info;
3962 /* Select the correct read buffer, and give some debug output.
3963 * There is no need to keep track of the current read buffer or reset it, every part of the code
3964 * that reads sets the read buffer as desired.
3966 if (surface_is_offscreen(surface))
3968 /* Mapping the primary render target which is not on a swapchain.
3969 * Read from the back buffer. */
3970 TRACE("Mapping offscreen render target.\n");
3971 glReadBuffer(device->offscreenBuffer);
3972 srcIsUpsideDown = TRUE;
3976 /* Onscreen surfaces are always part of a swapchain */
3977 GLenum buffer = surface_get_gl_buffer(surface);
3978 TRACE("Mapping %#x buffer.\n", buffer);
3979 glReadBuffer(buffer);
3980 checkGLcall("glReadBuffer");
3981 srcIsUpsideDown = FALSE;
3984 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
3987 local_rect.left = 0;
3989 local_rect.right = surface->resource.width;
3990 local_rect.bottom = surface->resource.height;
3996 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
3998 switch (surface->resource.format->id)
4000 case WINED3DFMT_P8_UINT:
4002 if (primary_render_target_is_p8(device))
4004 /* In case of P8 render targets the index is stored in the alpha component */
4006 type = GL_UNSIGNED_BYTE;
4008 bpp = surface->resource.format->byte_count;
4012 /* GL can't return palettized data, so read ARGB pixels into a
4013 * separate block of memory and convert them into palettized format
4014 * in software. Slow, but if the app means to use palettized render
4015 * targets and locks it...
4017 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4018 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4019 * for the color channels when palettizing the colors.
4022 type = GL_UNSIGNED_BYTE;
4024 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4027 ERR("Out of memory\n");
4031 bpp = surface->resource.format->byte_count * 3;
4038 fmt = surface->resource.format->glFormat;
4039 type = surface->resource.format->glType;
4040 bpp = surface->resource.format->byte_count;
4043 if (surface->flags & SFLAG_PBO)
4045 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4046 checkGLcall("glBindBufferARB");
4049 ERR("mem not null for pbo -- unexpected\n");
4054 /* Save old pixel store pack state */
4055 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4056 checkGLcall("glGetIntegerv");
4057 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4058 checkGLcall("glGetIntegerv");
4059 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4060 checkGLcall("glGetIntegerv");
4062 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4063 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4064 checkGLcall("glPixelStorei");
4065 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4066 checkGLcall("glPixelStorei");
4067 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4068 checkGLcall("glPixelStorei");
4070 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4071 local_rect.right - local_rect.left,
4072 local_rect.bottom - local_rect.top,
4074 checkGLcall("glReadPixels");
4076 /* Reset previous pixel store pack state */
4077 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4078 checkGLcall("glPixelStorei");
4079 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4080 checkGLcall("glPixelStorei");
4081 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4082 checkGLcall("glPixelStorei");
4084 if (surface->flags & SFLAG_PBO)
4086 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4087 checkGLcall("glBindBufferARB");
4089 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4090 * to get a pointer to it and perform the flipping in software. This is a lot
4091 * faster than calling glReadPixels for each line. In case we want more speed
4092 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4093 if (!srcIsUpsideDown)
4095 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4096 checkGLcall("glBindBufferARB");
4098 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4099 checkGLcall("glMapBufferARB");
4103 /* TODO: Merge this with the palettization loop below for P8 targets */
4104 if(!srcIsUpsideDown) {
4106 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4107 Flip the lines in software */
4108 len = (local_rect.right - local_rect.left) * bpp;
4109 off = local_rect.left * bpp;
4111 row = HeapAlloc(GetProcessHeap(), 0, len);
4113 ERR("Out of memory\n");
4114 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4115 HeapFree(GetProcessHeap(), 0, mem);
4120 top = mem + pitch * local_rect.top;
4121 bottom = mem + pitch * (local_rect.bottom - 1);
4122 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4123 memcpy(row, top + off, len);
4124 memcpy(top + off, bottom + off, len);
4125 memcpy(bottom + off, row, len);
4129 HeapFree(GetProcessHeap(), 0, row);
4131 /* Unmap the temp PBO buffer */
4132 if (surface->flags & SFLAG_PBO)
4134 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4135 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4140 context_release(context);
4142 /* For P8 textures we need to perform an inverse palette lookup. This is
4143 * done by searching for a palette index which matches the RGB value.
4144 * Note this isn't guaranteed to work when there are multiple entries for
4145 * the same color but we have no choice. In case of P8 render targets,
4146 * the index is stored in the alpha component so no conversion is needed. */
4147 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4149 const PALETTEENTRY *pal = NULL;
4150 DWORD width = pitch / 3;
4153 if (surface->palette)
4155 pal = surface->palette->palents;
4159 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4160 HeapFree(GetProcessHeap(), 0, mem);
4164 for(y = local_rect.top; y < local_rect.bottom; y++) {
4165 for(x = local_rect.left; x < local_rect.right; x++) {
4166 /* start lines pixels */
4167 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4168 const BYTE *green = blue + 1;
4169 const BYTE *red = green + 1;
4171 for(c = 0; c < 256; c++) {
4172 if(*red == pal[c].peRed &&
4173 *green == pal[c].peGreen &&
4174 *blue == pal[c].peBlue)
4176 *((BYTE *) dest + y * width + x) = c;
4182 HeapFree(GetProcessHeap(), 0, mem);
4186 /* Read the framebuffer contents into a texture */
4187 static void read_from_framebuffer_texture(struct wined3d_surface *surface, BOOL srgb)
4189 struct wined3d_device *device = surface->resource.device;
4190 struct wined3d_context *context;
4192 if (!surface_is_offscreen(surface))
4194 /* We would need to flip onscreen surfaces, but there's no efficient
4195 * way to do that here. It makes more sense for the caller to
4196 * explicitly go through sysmem. */
4197 ERR("Not supported for onscreen targets.\n");
4201 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
4202 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
4203 * states in the stateblock, and no driver was found yet that had bugs in that regard.
4205 context = context_acquire(device, surface);
4206 device_invalidate_state(device, STATE_FRAMEBUFFER);
4208 surface_prepare_texture(surface, context, srgb);
4209 surface_bind_and_dirtify(surface, context, srgb);
4211 TRACE("Reading back offscreen render target %p.\n", surface);
4215 glReadBuffer(device->offscreenBuffer);
4216 checkGLcall("glReadBuffer");
4218 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4219 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4220 checkGLcall("glCopyTexSubImage2D");
4224 context_release(context);
4227 /* Context activation is done by the caller. */
4228 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4229 struct wined3d_context *context, BOOL srgb)
4231 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4232 CONVERT_TYPES convert;
4233 struct wined3d_format format;
4235 if (surface->flags & alloc_flag) return;
4237 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4238 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4239 else surface->flags &= ~SFLAG_CONVERTED;
4241 surface_bind_and_dirtify(surface, context, srgb);
4242 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4243 surface->flags |= alloc_flag;
4246 /* Context activation is done by the caller. */
4247 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4249 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4251 struct wined3d_texture *texture = surface->container.u.texture;
4252 UINT sub_count = texture->level_count * texture->layer_count;
4255 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4257 for (i = 0; i < sub_count; ++i)
4259 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4260 surface_prepare_texture_internal(s, context, srgb);
4266 surface_prepare_texture_internal(surface, context, srgb);
4269 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4273 if (surface->rb_multisample)
4276 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4277 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4278 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4279 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4280 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4284 if (surface->rb_resolved)
4287 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4288 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4289 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4290 surface->pow2Width, surface->pow2Height);
4291 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4295 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4296 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4298 struct wined3d_device *device = surface->resource.device;
4299 UINT pitch = wined3d_surface_get_pitch(surface);
4300 const struct wined3d_gl_info *gl_info;
4301 struct wined3d_context *context;
4305 surface_get_rect(surface, rect, &local_rect);
4307 mem += local_rect.top * pitch + local_rect.left * bpp;
4308 w = local_rect.right - local_rect.left;
4309 h = local_rect.bottom - local_rect.top;
4311 /* Activate the correct context for the render target */
4312 context = context_acquire(device, surface);
4313 context_apply_blit_state(context, device);
4314 gl_info = context->gl_info;
4318 if (!surface_is_offscreen(surface))
4320 GLenum buffer = surface_get_gl_buffer(surface);
4321 TRACE("Unlocking %#x buffer.\n", buffer);
4322 context_set_draw_buffer(context, buffer);
4324 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4325 glPixelZoom(1.0f, -1.0f);
4329 /* Primary offscreen render target */
4330 TRACE("Offscreen render target.\n");
4331 context_set_draw_buffer(context, device->offscreenBuffer);
4333 glPixelZoom(1.0f, 1.0f);
4336 glRasterPos3i(local_rect.left, local_rect.top, 1);
4337 checkGLcall("glRasterPos3i");
4339 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4340 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4342 if (surface->flags & SFLAG_PBO)
4344 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4345 checkGLcall("glBindBufferARB");
4348 glDrawPixels(w, h, fmt, type, mem);
4349 checkGLcall("glDrawPixels");
4351 if (surface->flags & SFLAG_PBO)
4353 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4354 checkGLcall("glBindBufferARB");
4357 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4358 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4362 if (wined3d_settings.strict_draw_ordering
4363 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4364 && surface->container.u.swapchain->front_buffer == surface))
4367 context_release(context);
4370 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4371 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4373 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4374 const struct wined3d_device *device = surface->resource.device;
4375 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4376 BOOL blit_supported = FALSE;
4378 /* Copy the default values from the surface. Below we might perform fixups */
4379 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4380 *format = *surface->resource.format;
4381 *convert = NO_CONVERSION;
4383 /* Ok, now look if we have to do any conversion */
4384 switch (surface->resource.format->id)
4386 case WINED3DFMT_P8_UINT:
4387 /* Below the call to blit_supported is disabled for Wine 1.2
4388 * because the function isn't operating correctly yet. At the
4389 * moment 8-bit blits are handled in software and if certain GL
4390 * extensions are around, surface conversion is performed at
4391 * upload time. The blit_supported call recognizes it as a
4392 * destination fixup. This type of upload 'fixup' and 8-bit to
4393 * 8-bit blits need to be handled by the blit_shader.
4394 * TODO: get rid of this #if 0. */
4396 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4397 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4398 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4400 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4402 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4403 * texturing. Further also use conversion in case of color keying.
4404 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4405 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4406 * conflicts with this.
4408 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4409 || colorkey_active || !use_texturing)
4411 format->glFormat = GL_RGBA;
4412 format->glInternal = GL_RGBA;
4413 format->glType = GL_UNSIGNED_BYTE;
4414 format->conv_byte_count = 4;
4415 if (colorkey_active)
4416 *convert = CONVERT_PALETTED_CK;
4418 *convert = CONVERT_PALETTED;
4422 case WINED3DFMT_B2G3R3_UNORM:
4423 /* **********************
4424 GL_UNSIGNED_BYTE_3_3_2
4425 ********************** */
4426 if (colorkey_active) {
4427 /* This texture format will never be used.. So do not care about color keying
4428 up until the point in time it will be needed :-) */
4429 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4433 case WINED3DFMT_B5G6R5_UNORM:
4434 if (colorkey_active)
4436 *convert = CONVERT_CK_565;
4437 format->glFormat = GL_RGBA;
4438 format->glInternal = GL_RGB5_A1;
4439 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4440 format->conv_byte_count = 2;
4444 case WINED3DFMT_B5G5R5X1_UNORM:
4445 if (colorkey_active)
4447 *convert = CONVERT_CK_5551;
4448 format->glFormat = GL_BGRA;
4449 format->glInternal = GL_RGB5_A1;
4450 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4451 format->conv_byte_count = 2;
4455 case WINED3DFMT_B8G8R8_UNORM:
4456 if (colorkey_active)
4458 *convert = CONVERT_CK_RGB24;
4459 format->glFormat = GL_RGBA;
4460 format->glInternal = GL_RGBA8;
4461 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4462 format->conv_byte_count = 4;
4466 case WINED3DFMT_B8G8R8X8_UNORM:
4467 if (colorkey_active)
4469 *convert = CONVERT_RGB32_888;
4470 format->glFormat = GL_RGBA;
4471 format->glInternal = GL_RGBA8;
4472 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4473 format->conv_byte_count = 4;
4484 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4486 const struct wined3d_device *device = surface->resource.device;
4487 const struct wined3d_palette *pal = surface->palette;
4488 BOOL index_in_alpha = FALSE;
4491 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4492 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4493 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4494 * duplicate entries. Store the color key in the unused alpha component to speed the
4495 * download up and to make conversion unneeded. */
4496 index_in_alpha = primary_render_target_is_p8(device);
4500 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4503 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4504 * there's no palette at this time. */
4505 for (i = 0; i < 256; i++) table[i][3] = i;
4510 TRACE("Using surface palette %p\n", pal);
4511 /* Get the surface's palette */
4512 for (i = 0; i < 256; ++i)
4514 table[i][0] = pal->palents[i].peRed;
4515 table[i][1] = pal->palents[i].peGreen;
4516 table[i][2] = pal->palents[i].peBlue;
4518 /* When index_in_alpha is set the palette index is stored in the
4519 * alpha component. In case of a readback we can then read
4520 * GL_ALPHA. Color keying is handled in BltOverride using a
4521 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4522 * color key itself is passed to glAlphaFunc in other cases the
4523 * alpha component of pixels that should be masked away is set to 0. */
4528 else if (colorkey && (i >= surface->SrcBltCKey.dwColorSpaceLowValue)
4529 && (i <= surface->SrcBltCKey.dwColorSpaceHighValue))
4533 else if (pal->flags & WINEDDPCAPS_ALPHA)
4535 table[i][3] = pal->palents[i].peFlags;
4545 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4546 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4550 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4555 memcpy(dst, src, pitch * height);
4558 case CONVERT_PALETTED:
4559 case CONVERT_PALETTED_CK:
4564 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4566 for (y = 0; y < height; y++)
4568 source = src + pitch * y;
4569 dest = dst + outpitch * y;
4570 /* This is an 1 bpp format, using the width here is fine */
4571 for (x = 0; x < width; x++) {
4572 BYTE color = *source++;
4573 *dest++ = table[color][0];
4574 *dest++ = table[color][1];
4575 *dest++ = table[color][2];
4576 *dest++ = table[color][3];
4582 case CONVERT_CK_565:
4584 /* Converting the 565 format in 5551 packed to emulate color-keying.
4586 Note : in all these conversion, it would be best to average the averaging
4587 pixels to get the color of the pixel that will be color-keyed to
4588 prevent 'color bleeding'. This will be done later on if ever it is
4591 Note2: Nvidia documents say that their driver does not support alpha + color keying
4592 on the same surface and disables color keying in such a case
4598 TRACE("Color keyed 565\n");
4600 for (y = 0; y < height; y++) {
4601 Source = (const WORD *)(src + y * pitch);
4602 Dest = (WORD *) (dst + y * outpitch);
4603 for (x = 0; x < width; x++ ) {
4604 WORD color = *Source++;
4605 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4606 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4607 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4615 case CONVERT_CK_5551:
4617 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4621 TRACE("Color keyed 5551\n");
4622 for (y = 0; y < height; y++) {
4623 Source = (const WORD *)(src + y * pitch);
4624 Dest = (WORD *) (dst + y * outpitch);
4625 for (x = 0; x < width; x++ ) {
4626 WORD color = *Source++;
4628 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4629 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4632 *Dest &= ~(1 << 15);
4639 case CONVERT_CK_RGB24:
4641 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4643 for (y = 0; y < height; y++)
4645 source = src + pitch * y;
4646 dest = dst + outpitch * y;
4647 for (x = 0; x < width; x++) {
4648 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4649 DWORD dstcolor = color << 8;
4650 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4651 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4653 *(DWORD*)dest = dstcolor;
4661 case CONVERT_RGB32_888:
4663 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4665 for (y = 0; y < height; y++)
4667 source = src + pitch * y;
4668 dest = dst + outpitch * y;
4669 for (x = 0; x < width; x++) {
4670 DWORD color = 0xffffff & *(const DWORD*)source;
4671 DWORD dstcolor = color << 8;
4672 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4673 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4675 *(DWORD*)dest = dstcolor;
4684 ERR("Unsupported conversion type %#x.\n", convert);
4689 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4691 /* Flip the surface contents */
4696 front->hDC = back->hDC;
4700 /* Flip the DIBsection */
4703 BOOL hasDib = front->flags & SFLAG_DIBSECTION;
4704 tmp = front->dib.DIBsection;
4705 front->dib.DIBsection = back->dib.DIBsection;
4706 back->dib.DIBsection = tmp;
4708 if (back->flags & SFLAG_DIBSECTION) front->flags |= SFLAG_DIBSECTION;
4709 else front->flags &= ~SFLAG_DIBSECTION;
4710 if (hasDib) back->flags |= SFLAG_DIBSECTION;
4711 else back->flags &= ~SFLAG_DIBSECTION;
4714 /* Flip the surface data */
4718 tmp = front->dib.bitmap_data;
4719 front->dib.bitmap_data = back->dib.bitmap_data;
4720 back->dib.bitmap_data = tmp;
4722 tmp = front->resource.allocatedMemory;
4723 front->resource.allocatedMemory = back->resource.allocatedMemory;
4724 back->resource.allocatedMemory = tmp;
4726 tmp = front->resource.heapMemory;
4727 front->resource.heapMemory = back->resource.heapMemory;
4728 back->resource.heapMemory = tmp;
4733 GLuint tmp_pbo = front->pbo;
4734 front->pbo = back->pbo;
4735 back->pbo = tmp_pbo;
4738 /* client_memory should not be different, but just in case */
4741 tmp = front->dib.client_memory;
4742 front->dib.client_memory = back->dib.client_memory;
4743 back->dib.client_memory = tmp;
4746 /* Flip the opengl texture */
4750 tmp = back->texture_name;
4751 back->texture_name = front->texture_name;
4752 front->texture_name = tmp;
4754 tmp = back->texture_name_srgb;
4755 back->texture_name_srgb = front->texture_name_srgb;
4756 front->texture_name_srgb = tmp;
4758 tmp = back->rb_multisample;
4759 back->rb_multisample = front->rb_multisample;
4760 front->rb_multisample = tmp;
4762 tmp = back->rb_resolved;
4763 back->rb_resolved = front->rb_resolved;
4764 front->rb_resolved = tmp;
4766 resource_unload(&back->resource);
4767 resource_unload(&front->resource);
4771 DWORD tmp_flags = back->flags;
4772 back->flags = front->flags;
4773 front->flags = tmp_flags;
4777 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4778 * pixel copy calls. */
4779 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4780 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4782 struct wined3d_device *device = dst_surface->resource.device;
4785 struct wined3d_context *context;
4786 BOOL upsidedown = FALSE;
4787 RECT dst_rect = *dst_rect_in;
4789 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4790 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4792 if(dst_rect.top > dst_rect.bottom) {
4793 UINT tmp = dst_rect.bottom;
4794 dst_rect.bottom = dst_rect.top;
4799 context = context_acquire(device, src_surface);
4800 context_apply_blit_state(context, device);
4801 surface_internal_preload(dst_surface, SRGB_RGB);
4804 /* Bind the target texture */
4805 context_bind_texture(context, dst_surface->texture_target, dst_surface->texture_name);
4806 if (surface_is_offscreen(src_surface))
4808 TRACE("Reading from an offscreen target\n");
4809 upsidedown = !upsidedown;
4810 glReadBuffer(device->offscreenBuffer);
4814 glReadBuffer(surface_get_gl_buffer(src_surface));
4816 checkGLcall("glReadBuffer");
4818 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4819 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4821 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4823 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4825 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4826 ERR("Texture filtering not supported in direct blit\n");
4829 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4830 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4832 ERR("Texture filtering not supported in direct blit\n");
4836 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4837 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4839 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4841 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4842 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4843 src_rect->left, src_surface->resource.height - src_rect->bottom,
4844 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4848 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4849 /* I have to process this row by row to swap the image,
4850 * otherwise it would be upside down, so stretching in y direction
4851 * doesn't cost extra time
4853 * However, stretching in x direction can be avoided if not necessary
4855 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4856 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4858 /* Well, that stuff works, but it's very slow.
4859 * find a better way instead
4863 for (col = dst_rect.left; col < dst_rect.right; ++col)
4865 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4866 dst_rect.left + col /* x offset */, row /* y offset */,
4867 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4872 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4873 dst_rect.left /* x offset */, row /* y offset */,
4874 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4878 checkGLcall("glCopyTexSubImage2D");
4881 context_release(context);
4883 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4884 * path is never entered
4886 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4889 /* Uses the hardware to stretch and flip the image */
4890 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4891 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4893 struct wined3d_device *device = dst_surface->resource.device;
4894 struct wined3d_swapchain *src_swapchain = NULL;
4895 GLuint src, backup = 0;
4896 float left, right, top, bottom; /* Texture coordinates */
4897 UINT fbwidth = src_surface->resource.width;
4898 UINT fbheight = src_surface->resource.height;
4899 struct wined3d_context *context;
4900 GLenum drawBuffer = GL_BACK;
4901 GLenum texture_target;
4902 BOOL noBackBufferBackup;
4904 BOOL upsidedown = FALSE;
4905 RECT dst_rect = *dst_rect_in;
4907 TRACE("Using hwstretch blit\n");
4908 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4909 context = context_acquire(device, src_surface);
4910 context_apply_blit_state(context, device);
4911 surface_internal_preload(dst_surface, SRGB_RGB);
4913 src_offscreen = surface_is_offscreen(src_surface);
4914 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4915 if (!noBackBufferBackup && !src_surface->texture_name)
4917 /* Get it a description */
4918 surface_internal_preload(src_surface, SRGB_RGB);
4922 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4923 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4925 if (context->aux_buffers >= 2)
4927 /* Got more than one aux buffer? Use the 2nd aux buffer */
4928 drawBuffer = GL_AUX1;
4930 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4932 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4933 drawBuffer = GL_AUX0;
4936 if(noBackBufferBackup) {
4937 glGenTextures(1, &backup);
4938 checkGLcall("glGenTextures");
4939 context_bind_texture(context, GL_TEXTURE_2D, backup);
4940 texture_target = GL_TEXTURE_2D;
4942 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4943 * we are reading from the back buffer, the backup can be used as source texture
4945 texture_target = src_surface->texture_target;
4946 context_bind_texture(context, texture_target, src_surface->texture_name);
4947 glEnable(texture_target);
4948 checkGLcall("glEnable(texture_target)");
4950 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4951 src_surface->flags &= ~SFLAG_INTEXTURE;
4954 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4955 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4957 if(dst_rect.top > dst_rect.bottom) {
4958 UINT tmp = dst_rect.bottom;
4959 dst_rect.bottom = dst_rect.top;
4966 TRACE("Reading from an offscreen target\n");
4967 upsidedown = !upsidedown;
4968 glReadBuffer(device->offscreenBuffer);
4972 glReadBuffer(surface_get_gl_buffer(src_surface));
4975 /* TODO: Only back up the part that will be overwritten */
4976 glCopyTexSubImage2D(texture_target, 0,
4977 0, 0 /* read offsets */,
4982 checkGLcall("glCopyTexSubImage2D");
4984 /* No issue with overriding these - the sampler is dirty due to blit usage */
4985 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
4986 wined3d_gl_mag_filter(magLookup, Filter));
4987 checkGLcall("glTexParameteri");
4988 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
4989 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
4990 checkGLcall("glTexParameteri");
4992 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
4993 src_swapchain = src_surface->container.u.swapchain;
4994 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
4996 src = backup ? backup : src_surface->texture_name;
5000 glReadBuffer(GL_FRONT);
5001 checkGLcall("glReadBuffer(GL_FRONT)");
5003 glGenTextures(1, &src);
5004 checkGLcall("glGenTextures(1, &src)");
5005 context_bind_texture(context, GL_TEXTURE_2D, src);
5007 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5008 * out for power of 2 sizes
5010 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5011 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5012 checkGLcall("glTexImage2D");
5013 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5014 0, 0 /* read offsets */,
5019 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5020 checkGLcall("glTexParameteri");
5021 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5022 checkGLcall("glTexParameteri");
5024 glReadBuffer(GL_BACK);
5025 checkGLcall("glReadBuffer(GL_BACK)");
5027 if(texture_target != GL_TEXTURE_2D) {
5028 glDisable(texture_target);
5029 glEnable(GL_TEXTURE_2D);
5030 texture_target = GL_TEXTURE_2D;
5033 checkGLcall("glEnd and previous");
5035 left = src_rect->left;
5036 right = src_rect->right;
5040 top = src_surface->resource.height - src_rect->top;
5041 bottom = src_surface->resource.height - src_rect->bottom;
5045 top = src_surface->resource.height - src_rect->bottom;
5046 bottom = src_surface->resource.height - src_rect->top;
5049 if (src_surface->flags & SFLAG_NORMCOORD)
5051 left /= src_surface->pow2Width;
5052 right /= src_surface->pow2Width;
5053 top /= src_surface->pow2Height;
5054 bottom /= src_surface->pow2Height;
5057 /* draw the source texture stretched and upside down. The correct surface is bound already */
5058 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5059 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5061 context_set_draw_buffer(context, drawBuffer);
5062 glReadBuffer(drawBuffer);
5066 glTexCoord2f(left, bottom);
5070 glTexCoord2f(left, top);
5071 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5074 glTexCoord2f(right, top);
5075 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5078 glTexCoord2f(right, bottom);
5079 glVertex2i(dst_rect.right - dst_rect.left, 0);
5081 checkGLcall("glEnd and previous");
5083 if (texture_target != dst_surface->texture_target)
5085 glDisable(texture_target);
5086 glEnable(dst_surface->texture_target);
5087 texture_target = dst_surface->texture_target;
5090 /* Now read the stretched and upside down image into the destination texture */
5091 context_bind_texture(context, texture_target, dst_surface->texture_name);
5092 glCopyTexSubImage2D(texture_target,
5094 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5095 0, 0, /* We blitted the image to the origin */
5096 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5097 checkGLcall("glCopyTexSubImage2D");
5099 if(drawBuffer == GL_BACK) {
5100 /* Write the back buffer backup back */
5102 if(texture_target != GL_TEXTURE_2D) {
5103 glDisable(texture_target);
5104 glEnable(GL_TEXTURE_2D);
5105 texture_target = GL_TEXTURE_2D;
5107 context_bind_texture(context, GL_TEXTURE_2D, backup);
5111 if (texture_target != src_surface->texture_target)
5113 glDisable(texture_target);
5114 glEnable(src_surface->texture_target);
5115 texture_target = src_surface->texture_target;
5117 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5122 glTexCoord2f(0.0f, 0.0f);
5123 glVertex2i(0, fbheight);
5126 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5130 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5131 (float)fbheight / (float)src_surface->pow2Height);
5132 glVertex2i(fbwidth, 0);
5135 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5136 glVertex2i(fbwidth, fbheight);
5139 glDisable(texture_target);
5140 checkGLcall("glDisable(texture_target)");
5143 if (src != src_surface->texture_name && src != backup)
5145 glDeleteTextures(1, &src);
5146 checkGLcall("glDeleteTextures(1, &src)");
5149 glDeleteTextures(1, &backup);
5150 checkGLcall("glDeleteTextures(1, &backup)");
5155 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5157 context_release(context);
5159 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5160 * path is never entered
5162 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5165 /* Front buffer coordinates are always full screen coordinates, but our GL
5166 * drawable is limited to the window's client area. The sysmem and texture
5167 * copies do have the full screen size. Note that GL has a bottom-left
5168 * origin, while D3D has a top-left origin. */
5169 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5171 UINT drawable_height;
5173 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5174 && surface == surface->container.u.swapchain->front_buffer)
5176 POINT offset = {0, 0};
5179 ScreenToClient(window, &offset);
5180 OffsetRect(rect, offset.x, offset.y);
5182 GetClientRect(window, &windowsize);
5183 drawable_height = windowsize.bottom - windowsize.top;
5187 drawable_height = surface->resource.height;
5190 rect->top = drawable_height - rect->top;
5191 rect->bottom = drawable_height - rect->bottom;
5194 static void surface_blt_to_drawable(struct wined3d_device *device,
5195 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5196 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5197 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5199 struct wined3d_context *context;
5200 RECT src_rect, dst_rect;
5202 src_rect = *src_rect_in;
5203 dst_rect = *dst_rect_in;
5205 /* Make sure the surface is up-to-date. This should probably use
5206 * surface_load_location() and worry about the destination surface too,
5207 * unless we're overwriting it completely. */
5208 surface_internal_preload(src_surface, SRGB_RGB);
5210 /* Activate the destination context, set it up for blitting */
5211 context = context_acquire(device, dst_surface);
5212 context_apply_blit_state(context, device);
5214 if (!surface_is_offscreen(dst_surface))
5215 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5217 device->blitter->set_shader(device->blit_priv, context, src_surface);
5223 glEnable(GL_ALPHA_TEST);
5224 checkGLcall("glEnable(GL_ALPHA_TEST)");
5226 /* When the primary render target uses P8, the alpha component
5227 * contains the palette index. Which means that the colorkey is one of
5228 * the palette entries. In other cases pixels that should be masked
5229 * away have alpha set to 0. */
5230 if (primary_render_target_is_p8(device))
5231 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
5233 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5234 checkGLcall("glAlphaFunc");
5238 glDisable(GL_ALPHA_TEST);
5239 checkGLcall("glDisable(GL_ALPHA_TEST)");
5242 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5246 glDisable(GL_ALPHA_TEST);
5247 checkGLcall("glDisable(GL_ALPHA_TEST)");
5252 /* Leave the opengl state valid for blitting */
5253 device->blitter->unset_shader(context->gl_info);
5255 if (wined3d_settings.strict_draw_ordering
5256 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5257 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5258 wglFlush(); /* Flush to ensure ordering across contexts. */
5260 context_release(context);
5263 /* Do not call while under the GL lock. */
5264 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const WINED3DCOLORVALUE *color)
5266 struct wined3d_device *device = s->resource.device;
5267 const struct blit_shader *blitter;
5269 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5270 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5273 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5274 return WINED3DERR_INVALIDCALL;
5277 return blitter->color_fill(device, s, rect, color);
5280 /* Do not call while under the GL lock. */
5281 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5282 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5283 WINED3DTEXTUREFILTERTYPE Filter)
5285 struct wined3d_device *device = dst_surface->resource.device;
5286 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5287 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5289 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5290 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5291 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5293 /* Get the swapchain. One of the surfaces has to be a primary surface */
5294 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5296 WARN("Destination is in sysmem, rejecting gl blt\n");
5297 return WINED3DERR_INVALIDCALL;
5300 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5301 dstSwapchain = dst_surface->container.u.swapchain;
5305 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5307 WARN("Src is in sysmem, rejecting gl blt\n");
5308 return WINED3DERR_INVALIDCALL;
5311 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5312 srcSwapchain = src_surface->container.u.swapchain;
5315 /* Early sort out of cases where no render target is used */
5316 if (!dstSwapchain && !srcSwapchain
5317 && src_surface != device->fb.render_targets[0]
5318 && dst_surface != device->fb.render_targets[0])
5320 TRACE("No surface is render target, not using hardware blit.\n");
5321 return WINED3DERR_INVALIDCALL;
5324 /* No destination color keying supported */
5325 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5327 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5328 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5329 return WINED3DERR_INVALIDCALL;
5332 if (dstSwapchain && dstSwapchain == srcSwapchain)
5334 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5335 return WINED3DERR_INVALIDCALL;
5338 if (dstSwapchain && srcSwapchain)
5340 FIXME("Implement hardware blit between two different swapchains\n");
5341 return WINED3DERR_INVALIDCALL;
5346 /* Handled with regular texture -> swapchain blit */
5347 if (src_surface == device->fb.render_targets[0])
5348 TRACE("Blit from active render target to a swapchain\n");
5350 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5352 FIXME("Implement blit from a swapchain to the active render target\n");
5353 return WINED3DERR_INVALIDCALL;
5356 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5358 /* Blit from render target to texture */
5361 /* P8 read back is not implemented */
5362 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5363 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5365 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5366 return WINED3DERR_INVALIDCALL;
5369 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5371 TRACE("Color keying not supported by frame buffer to texture blit\n");
5372 return WINED3DERR_INVALIDCALL;
5373 /* Destination color key is checked above */
5376 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5381 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5382 * flip the image nor scale it.
5384 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5385 * -> If the app wants a image width an unscaled width, copy it line per line
5386 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5387 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5388 * back buffer. This is slower than reading line per line, thus not used for flipping
5389 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5390 * pixel by pixel. */
5391 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5392 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5394 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5395 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, Filter);
5397 TRACE("Using hardware stretching to flip / stretch the texture\n");
5398 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5401 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5403 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5404 dst_surface->resource.allocatedMemory = NULL;
5405 dst_surface->resource.heapMemory = NULL;
5409 dst_surface->flags &= ~SFLAG_INSYSMEM;
5414 else if (src_surface)
5416 /* Blit from offscreen surface to render target */
5417 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5418 WINEDDCOLORKEY oldBltCKey = src_surface->SrcBltCKey;
5420 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5422 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5423 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5424 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5426 FIXME("Unsupported blit operation falling back to software\n");
5427 return WINED3DERR_INVALIDCALL;
5430 /* Color keying: Check if we have to do a color keyed blt,
5431 * and if not check if a color key is activated.
5433 * Just modify the color keying parameters in the surface and restore them afterwards
5434 * The surface keeps track of the color key last used to load the opengl surface.
5435 * PreLoad will catch the change to the flags and color key and reload if necessary.
5437 if (flags & WINEDDBLT_KEYSRC)
5439 /* Use color key from surface */
5441 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5443 /* Use color key from DDBltFx */
5444 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5445 src_surface->SrcBltCKey = DDBltFx->ddckSrcColorkey;
5449 /* Do not use color key */
5450 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5453 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5454 src_surface, src_rect, dst_surface, dst_rect);
5456 /* Restore the color key parameters */
5457 src_surface->CKeyFlags = oldCKeyFlags;
5458 src_surface->SrcBltCKey = oldBltCKey;
5460 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5465 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5466 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5467 return WINED3DERR_INVALIDCALL;
5470 /* GL locking is done by the caller */
5471 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5472 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5474 struct wined3d_device *device = surface->resource.device;
5475 const struct wined3d_gl_info *gl_info = context->gl_info;
5476 GLint compare_mode = GL_NONE;
5477 struct blt_info info;
5478 GLint old_binding = 0;
5481 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5483 glDisable(GL_CULL_FACE);
5484 glDisable(GL_BLEND);
5485 glDisable(GL_ALPHA_TEST);
5486 glDisable(GL_SCISSOR_TEST);
5487 glDisable(GL_STENCIL_TEST);
5488 glEnable(GL_DEPTH_TEST);
5489 glDepthFunc(GL_ALWAYS);
5490 glDepthMask(GL_TRUE);
5491 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5492 glViewport(x, y, w, h);
5494 SetRect(&rect, 0, h, w, 0);
5495 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5496 context_active_texture(context, context->gl_info, 0);
5497 glGetIntegerv(info.binding, &old_binding);
5498 glBindTexture(info.bind_target, texture);
5499 if (gl_info->supported[ARB_SHADOW])
5501 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5502 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5505 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5506 gl_info, info.tex_type, &surface->ds_current_size);
5508 glBegin(GL_TRIANGLE_STRIP);
5509 glTexCoord3fv(info.coords[0]);
5510 glVertex2f(-1.0f, -1.0f);
5511 glTexCoord3fv(info.coords[1]);
5512 glVertex2f(1.0f, -1.0f);
5513 glTexCoord3fv(info.coords[2]);
5514 glVertex2f(-1.0f, 1.0f);
5515 glTexCoord3fv(info.coords[3]);
5516 glVertex2f(1.0f, 1.0f);
5519 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5520 glBindTexture(info.bind_target, old_binding);
5524 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5527 void surface_modify_ds_location(struct wined3d_surface *surface,
5528 DWORD location, UINT w, UINT h)
5530 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5532 if (location & ~SFLAG_DS_LOCATIONS)
5533 FIXME("Invalid location (%#x) specified.\n", location);
5535 surface->ds_current_size.cx = w;
5536 surface->ds_current_size.cy = h;
5537 surface->flags &= ~SFLAG_DS_LOCATIONS;
5538 surface->flags |= location;
5541 /* Context activation is done by the caller. */
5542 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5544 struct wined3d_device *device = surface->resource.device;
5547 TRACE("surface %p, new location %#x.\n", surface, location);
5549 /* TODO: Make this work for modes other than FBO */
5550 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5552 if (!(surface->flags & location))
5554 w = surface->ds_current_size.cx;
5555 h = surface->ds_current_size.cy;
5556 surface->ds_current_size.cx = 0;
5557 surface->ds_current_size.cy = 0;
5561 w = surface->resource.width;
5562 h = surface->resource.height;
5565 if (surface->ds_current_size.cx == surface->resource.width
5566 && surface->ds_current_size.cy == surface->resource.height)
5568 TRACE("Location (%#x) is already up to date.\n", location);
5572 if (surface->current_renderbuffer)
5574 FIXME("Not supported with fixed up depth stencil.\n");
5578 if (!(surface->flags & SFLAG_DS_LOCATIONS))
5580 /* This mostly happens when a depth / stencil is used without being
5581 * cleared first. In principle we could upload from sysmem, or
5582 * explicitly clear before first usage. For the moment there don't
5583 * appear to be a lot of applications depending on this, so a FIXME
5585 FIXME("No up to date depth stencil location.\n");
5586 surface->flags |= location;
5587 surface->ds_current_size.cx = surface->resource.width;
5588 surface->ds_current_size.cy = surface->resource.height;
5592 if (location == SFLAG_DS_OFFSCREEN)
5594 GLint old_binding = 0;
5597 /* The render target is allowed to be smaller than the depth/stencil
5598 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5599 * than the offscreen surface. Don't overwrite the offscreen surface
5600 * with undefined data. */
5601 w = min(w, context->swapchain->presentParms.BackBufferWidth);
5602 h = min(h, context->swapchain->presentParms.BackBufferHeight);
5604 TRACE("Copying onscreen depth buffer to depth texture.\n");
5608 if (!device->depth_blt_texture)
5610 glGenTextures(1, &device->depth_blt_texture);
5613 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5614 * directly on the FBO texture. That's because we need to flip. */
5615 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5616 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5617 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5619 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5620 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5624 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5625 bind_target = GL_TEXTURE_2D;
5627 glBindTexture(bind_target, device->depth_blt_texture);
5628 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5629 * internal format, because the internal format might include stencil
5630 * data. In principle we should copy stencil data as well, but unless
5631 * the driver supports stencil export it's hard to do, and doesn't
5632 * seem to be needed in practice. If the hardware doesn't support
5633 * writing stencil data, the glCopyTexImage2D() call might trigger
5634 * software fallbacks. */
5635 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5636 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5637 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5638 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5639 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5640 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5641 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5642 glBindTexture(bind_target, old_binding);
5644 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5645 NULL, surface, SFLAG_INTEXTURE);
5646 context_set_draw_buffer(context, GL_NONE);
5647 glReadBuffer(GL_NONE);
5649 /* Do the actual blit */
5650 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5651 checkGLcall("depth_blt");
5653 context_invalidate_state(context, STATE_FRAMEBUFFER);
5657 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5659 else if (location == SFLAG_DS_ONSCREEN)
5661 TRACE("Copying depth texture to onscreen depth buffer.\n");
5665 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5666 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5667 surface_depth_blt(surface, context, surface->texture_name,
5668 0, surface->pow2Height - h, w, h, surface->texture_target);
5669 checkGLcall("depth_blt");
5671 context_invalidate_state(context, STATE_FRAMEBUFFER);
5675 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5679 ERR("Invalid location (%#x) specified.\n", location);
5682 surface->flags |= location;
5683 surface->ds_current_size.cx = surface->resource.width;
5684 surface->ds_current_size.cy = surface->resource.height;
5687 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5689 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5690 struct wined3d_surface *overlay;
5692 TRACE("surface %p, location %s, persistent %#x.\n",
5693 surface, debug_surflocation(location), persistent);
5695 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5696 && (location & SFLAG_INDRAWABLE))
5697 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5699 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5700 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5701 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5705 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5706 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5708 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5710 TRACE("Passing to container.\n");
5711 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5714 surface->flags &= ~SFLAG_LOCATIONS;
5715 surface->flags |= location;
5717 /* Redraw emulated overlays, if any */
5718 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5720 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5722 overlay->surface_ops->surface_draw_overlay(overlay);
5728 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5730 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5732 TRACE("Passing to container\n");
5733 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5736 surface->flags &= ~location;
5739 if (!(surface->flags & SFLAG_LOCATIONS))
5741 ERR("Surface %p does not have any up to date location.\n", surface);
5745 static DWORD resource_access_from_location(DWORD location)
5749 case SFLAG_INSYSMEM:
5750 return WINED3D_RESOURCE_ACCESS_CPU;
5752 case SFLAG_INDRAWABLE:
5753 case SFLAG_INSRGBTEX:
5754 case SFLAG_INTEXTURE:
5755 case SFLAG_INRB_MULTISAMPLE:
5756 case SFLAG_INRB_RESOLVED:
5757 return WINED3D_RESOURCE_ACCESS_GPU;
5760 FIXME("Unhandled location %#x.\n", location);
5765 static void surface_load_sysmem(struct wined3d_surface *surface,
5766 const struct wined3d_gl_info *gl_info, const RECT *rect)
5768 surface_prepare_system_memory(surface);
5770 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5771 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5773 /* Download the surface to system memory. */
5774 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5776 struct wined3d_device *device = surface->resource.device;
5777 struct wined3d_context *context;
5779 /* TODO: Use already acquired context when possible. */
5780 context = context_acquire(device, NULL);
5782 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5783 surface_download_data(surface, gl_info);
5785 context_release(context);
5790 if (surface->flags & SFLAG_INDRAWABLE)
5792 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5793 wined3d_surface_get_pitch(surface));
5797 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5798 surface, surface->flags & SFLAG_LOCATIONS);
5801 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5802 const struct wined3d_gl_info *gl_info, const RECT *rect)
5804 struct wined3d_device *device = surface->resource.device;
5805 struct wined3d_format format;
5806 CONVERT_TYPES convert;
5810 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5812 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5813 return WINED3DERR_INVALIDCALL;
5816 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5817 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5819 if (surface->flags & SFLAG_INTEXTURE)
5823 surface_get_rect(surface, rect, &r);
5824 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5829 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5831 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5832 * path through sysmem. */
5833 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5836 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5838 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5839 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5841 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5843 struct wined3d_context *context;
5845 TRACE("Removing the pbo attached to surface %p.\n", surface);
5847 /* TODO: Use already acquired context when possible. */
5848 context = context_acquire(device, NULL);
5850 surface_remove_pbo(surface, gl_info);
5852 context_release(context);
5855 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5857 UINT height = surface->resource.height;
5858 UINT width = surface->resource.width;
5859 UINT src_pitch, dst_pitch;
5861 byte_count = format.conv_byte_count;
5862 src_pitch = wined3d_surface_get_pitch(surface);
5864 /* Stick to the alignment for the converted surface too, makes it
5865 * easier to load the surface. */
5866 dst_pitch = width * byte_count;
5867 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5869 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5871 ERR("Out of memory (%u).\n", dst_pitch * height);
5872 return E_OUTOFMEMORY;
5875 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5876 src_pitch, width, height, dst_pitch, convert, surface);
5878 surface->flags |= SFLAG_CONVERTED;
5882 surface->flags &= ~SFLAG_CONVERTED;
5883 mem = surface->resource.allocatedMemory;
5884 byte_count = format.byte_count;
5887 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5889 /* Don't delete PBO memory. */
5890 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5891 HeapFree(GetProcessHeap(), 0, mem);
5896 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5897 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5899 const DWORD attach_flags = WINED3DFMT_FLAG_FBO_ATTACHABLE | WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB;
5900 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5901 struct wined3d_device *device = surface->resource.device;
5902 struct wined3d_context *context;
5903 UINT width, src_pitch, dst_pitch;
5904 struct wined3d_bo_address data;
5905 struct wined3d_format format;
5906 POINT dst_point = {0, 0};
5907 CONVERT_TYPES convert;
5910 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
5911 && surface_is_offscreen(surface)
5912 && (surface->flags & SFLAG_INDRAWABLE))
5914 read_from_framebuffer_texture(surface, srgb);
5919 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
5920 && (surface->resource.format->flags & attach_flags) == attach_flags
5921 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5922 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5923 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5926 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
5927 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
5929 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
5930 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
5935 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
5936 && (surface->resource.format->flags & attach_flags) == attach_flags
5937 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5938 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5939 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5941 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
5942 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
5943 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5945 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, src_location,
5946 &rect, surface, dst_location, &rect);
5951 /* Upload from system memory */
5953 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
5954 TRUE /* We will use textures */, &format, &convert);
5958 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
5960 /* Performance warning... */
5961 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
5962 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5967 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
5969 /* Performance warning... */
5970 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
5971 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5975 if (!(surface->flags & SFLAG_INSYSMEM))
5977 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
5978 /* Lets hope we get it from somewhere... */
5979 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5982 /* TODO: Use already acquired context when possible. */
5983 context = context_acquire(device, NULL);
5985 surface_prepare_texture(surface, context, srgb);
5986 surface_bind_and_dirtify(surface, context, srgb);
5988 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
5990 surface->flags |= SFLAG_GLCKEY;
5991 surface->glCKey = surface->SrcBltCKey;
5993 else surface->flags &= ~SFLAG_GLCKEY;
5995 width = surface->resource.width;
5996 src_pitch = wined3d_surface_get_pitch(surface);
5998 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5999 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6001 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6003 TRACE("Removing the pbo attached to surface %p.\n", surface);
6004 surface_remove_pbo(surface, gl_info);
6009 /* This code is entered for texture formats which need a fixup. */
6010 UINT height = surface->resource.height;
6012 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6013 dst_pitch = width * format.conv_byte_count;
6014 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6016 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6018 ERR("Out of memory (%u).\n", dst_pitch * height);
6019 context_release(context);
6020 return E_OUTOFMEMORY;
6022 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6024 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6026 /* This code is only entered for color keying fixups */
6027 UINT height = surface->resource.height;
6029 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6030 dst_pitch = width * format.conv_byte_count;
6031 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6033 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6035 ERR("Out of memory (%u).\n", dst_pitch * height);
6036 context_release(context);
6037 return E_OUTOFMEMORY;
6039 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6040 width, height, dst_pitch, convert, surface);
6044 mem = surface->resource.allocatedMemory;
6047 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6049 surface_upload_data(surface, gl_info, &format, &src_rect, width, &dst_point, srgb, &data);
6051 context_release(context);
6053 /* Don't delete PBO memory. */
6054 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6055 HeapFree(GetProcessHeap(), 0, mem);
6060 static void surface_multisample_resolve(struct wined3d_surface *surface)
6062 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6064 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6065 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6067 surface_blt_fbo(surface->resource.device, WINED3DTEXF_POINT,
6068 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6071 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6073 struct wined3d_device *device = surface->resource.device;
6074 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6077 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6079 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6081 if (location == SFLAG_INTEXTURE)
6083 struct wined3d_context *context = context_acquire(device, NULL);
6084 surface_load_ds_location(surface, context, SFLAG_DS_OFFSCREEN);
6085 context_release(context);
6090 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6091 return WINED3DERR_INVALIDCALL;
6095 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6096 location = SFLAG_INTEXTURE;
6098 if (surface->flags & location)
6100 TRACE("Location already up to date.\n");
6104 if (WARN_ON(d3d_surface))
6106 DWORD required_access = resource_access_from_location(location);
6107 if ((surface->resource.access_flags & required_access) != required_access)
6108 WARN("Operation requires %#x access, but surface only has %#x.\n",
6109 required_access, surface->resource.access_flags);
6112 if (!(surface->flags & SFLAG_LOCATIONS))
6114 ERR("Surface %p does not have any up to date location.\n", surface);
6115 surface->flags |= SFLAG_LOST;
6116 return WINED3DERR_DEVICELOST;
6121 case SFLAG_INSYSMEM:
6122 surface_load_sysmem(surface, gl_info, rect);
6125 case SFLAG_INDRAWABLE:
6126 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6130 case SFLAG_INRB_RESOLVED:
6131 surface_multisample_resolve(surface);
6134 case SFLAG_INTEXTURE:
6135 case SFLAG_INSRGBTEX:
6136 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6141 ERR("Don't know how to handle location %#x.\n", location);
6147 surface->flags |= location;
6149 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6150 surface_evict_sysmem(surface);
6153 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6154 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6156 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6162 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6164 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6166 /* Not on a swapchain - must be offscreen */
6167 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6169 /* The front buffer is always onscreen */
6170 if (surface == swapchain->front_buffer) return FALSE;
6172 /* If the swapchain is rendered to an FBO, the backbuffer is
6173 * offscreen, otherwise onscreen */
6174 return swapchain->render_to_fbo;
6177 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6178 /* Context activation is done by the caller. */
6179 static void ffp_blit_free(struct wined3d_device *device) { }
6181 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6182 /* Context activation is done by the caller. */
6183 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6186 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6188 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6190 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6192 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6196 /* Context activation is done by the caller. */
6197 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, struct wined3d_surface *surface)
6199 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6201 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6202 * else the surface is converted in software at upload time in LoadLocation.
6204 if(fixup == COMPLEX_FIXUP_P8 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6205 ffp_blit_p8_upload_palette(surface, context->gl_info);
6208 glEnable(surface->texture_target);
6209 checkGLcall("glEnable(surface->texture_target)");
6214 /* Context activation is done by the caller. */
6215 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6218 glDisable(GL_TEXTURE_2D);
6219 checkGLcall("glDisable(GL_TEXTURE_2D)");
6220 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6222 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6223 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6225 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6227 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6228 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6233 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6234 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6235 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6237 enum complex_fixup src_fixup;
6241 case WINED3D_BLIT_OP_COLOR_BLIT:
6242 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
6245 src_fixup = get_complex_fixup(src_format->color_fixup);
6246 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6248 TRACE("Checking support for fixup:\n");
6249 dump_color_fixup_desc(src_format->color_fixup);
6252 if (!is_identity_fixup(dst_format->color_fixup))
6254 TRACE("Destination fixups are not supported\n");
6258 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6260 TRACE("P8 fixup supported\n");
6264 /* We only support identity conversions. */
6265 if (is_identity_fixup(src_format->color_fixup))
6271 TRACE("[FAILED]\n");
6274 case WINED3D_BLIT_OP_COLOR_FILL:
6275 if (dst_pool == WINED3DPOOL_SYSTEMMEM)
6278 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6280 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6283 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6285 TRACE("Color fill not supported\n");
6289 /* FIXME: We should reject color fills on formats with fixups,
6290 * but this would break P8 color fills for example. */
6294 case WINED3D_BLIT_OP_DEPTH_FILL:
6298 TRACE("Unsupported blit_op=%d\n", blit_op);
6303 /* Do not call while under the GL lock. */
6304 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6305 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
6307 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6308 struct wined3d_fb_state fb = {&dst_surface, NULL};
6310 return device_clear_render_targets(device, 1, &fb,
6311 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6314 /* Do not call while under the GL lock. */
6315 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6316 struct wined3d_surface *surface, const RECT *rect, float depth)
6318 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6319 struct wined3d_fb_state fb = {NULL, surface};
6321 return device_clear_render_targets(device, 0, &fb,
6322 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6325 const struct blit_shader ffp_blit = {
6331 ffp_blit_color_fill,
6332 ffp_blit_depth_fill,
6335 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6340 /* Context activation is done by the caller. */
6341 static void cpu_blit_free(struct wined3d_device *device)
6345 /* Context activation is done by the caller. */
6346 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, struct wined3d_surface *surface)
6351 /* Context activation is done by the caller. */
6352 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6356 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6357 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6358 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6360 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6368 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
6369 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
6370 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
6372 UINT row_block_count;
6373 const BYTE *src_row;
6380 row_block_count = (update_w + format->block_width - 1) / format->block_width;
6384 for (y = 0; y < update_h; y += format->block_height)
6386 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
6387 src_row += src_pitch;
6388 dst_row += dst_pitch;
6394 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
6396 src_row += (((update_h / format->block_height) - 1) * src_pitch);
6400 case WINED3DFMT_DXT1:
6401 for (y = 0; y < update_h; y += format->block_height)
6406 BYTE control_row[4];
6409 const struct block *s = (const struct block *)src_row;
6410 struct block *d = (struct block *)dst_row;
6412 for (x = 0; x < row_block_count; ++x)
6414 d[x].color[0] = s[x].color[0];
6415 d[x].color[1] = s[x].color[1];
6416 d[x].control_row[0] = s[x].control_row[3];
6417 d[x].control_row[1] = s[x].control_row[2];
6418 d[x].control_row[2] = s[x].control_row[1];
6419 d[x].control_row[3] = s[x].control_row[0];
6421 src_row -= src_pitch;
6422 dst_row += dst_pitch;
6426 case WINED3DFMT_DXT3:
6427 for (y = 0; y < update_h; y += format->block_height)
6433 BYTE control_row[4];
6436 const struct block *s = (const struct block *)src_row;
6437 struct block *d = (struct block *)dst_row;
6439 for (x = 0; x < row_block_count; ++x)
6441 d[x].alpha_row[0] = s[x].alpha_row[3];
6442 d[x].alpha_row[1] = s[x].alpha_row[2];
6443 d[x].alpha_row[2] = s[x].alpha_row[1];
6444 d[x].alpha_row[3] = s[x].alpha_row[0];
6445 d[x].color[0] = s[x].color[0];
6446 d[x].color[1] = s[x].color[1];
6447 d[x].control_row[0] = s[x].control_row[3];
6448 d[x].control_row[1] = s[x].control_row[2];
6449 d[x].control_row[2] = s[x].control_row[1];
6450 d[x].control_row[3] = s[x].control_row[0];
6452 src_row -= src_pitch;
6453 dst_row += dst_pitch;
6458 FIXME("Compressed flip not implemented for format %s.\n",
6459 debug_d3dformat(format->id));
6464 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
6465 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
6470 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6471 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6472 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6474 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6475 const struct wined3d_format *src_format, *dst_format;
6476 struct wined3d_surface *orig_src = src_surface;
6477 WINED3DLOCKED_RECT dlock, slock;
6478 HRESULT hr = WINED3D_OK;
6484 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6485 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6486 flags, fx, debug_d3dtexturefiltertype(filter));
6496 full_rect.right = dst_surface->resource.width;
6497 full_rect.bottom = dst_surface->resource.height;
6498 IntersectRect(&xdst, &full_rect, dst_rect);
6502 BOOL clip_horiz, clip_vert;
6505 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6506 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6508 if (clip_vert || clip_horiz)
6510 /* Now check if this is a special case or not... */
6511 if ((flags & WINEDDBLT_DDFX)
6512 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6513 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6515 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6523 xsrc.left -= xdst.left;
6526 if (xdst.right > dst_surface->resource.width)
6528 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6529 xdst.right = (int)dst_surface->resource.width;
6537 xsrc.top -= xdst.top;
6540 if (xdst.bottom > dst_surface->resource.height)
6542 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6543 xdst.bottom = (int)dst_surface->resource.height;
6547 /* And check if after clipping something is still to be done... */
6548 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6549 || (xdst.left >= (int)dst_surface->resource.width)
6550 || (xdst.top >= (int)dst_surface->resource.height)
6551 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6552 || (xsrc.left >= (int)src_surface->resource.width)
6553 || (xsrc.top >= (int)src_surface->resource.height))
6555 TRACE("Nothing to be done after clipping.\n");
6561 if (src_surface == dst_surface)
6563 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6565 src_format = dst_surface->resource.format;
6566 dst_format = src_format;
6570 dst_format = dst_surface->resource.format;
6573 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6575 src_surface = surface_convert_format(src_surface, dst_format->id);
6578 /* The conv function writes a FIXME */
6579 WARN("Cannot convert source surface format to dest format.\n");
6583 wined3d_surface_map(src_surface, &slock, NULL, WINED3DLOCK_READONLY);
6584 src_format = src_surface->resource.format;
6588 src_format = dst_format;
6591 wined3d_surface_map(dst_surface, &dlock, &xdst, 0);
6593 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6596 bpp = dst_surface->resource.format->byte_count;
6597 srcheight = xsrc.bottom - xsrc.top;
6598 srcwidth = xsrc.right - xsrc.left;
6599 dstheight = xdst.bottom - xdst.top;
6600 dstwidth = xdst.right - xdst.left;
6601 width = (xdst.right - xdst.left) * bpp;
6603 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_COMPRESSED)
6605 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6607 if (src_surface == dst_surface)
6609 FIXME("Only plain blits supported on compressed surfaces.\n");
6614 if (srcheight != dstheight || srcwidth != dstwidth)
6616 WARN("Stretching not supported on compressed surfaces.\n");
6617 hr = WINED3DERR_INVALIDCALL;
6621 if (srcwidth & (src_format->block_width - 1) || srcheight & (src_format->block_height - 1))
6623 WARN("Rectangle not block-aligned.\n");
6624 hr = WINED3DERR_INVALIDCALL;
6628 hr = surface_cpu_blt_compressed(slock.pBits, dlock.pBits,
6629 slock.Pitch, dlock.Pitch, dstwidth, dstheight,
6630 src_format, flags, fx);
6634 if (dst_rect && src_surface != dst_surface)
6637 dbuf = (BYTE*)dlock.pBits+(xdst.top*dlock.Pitch)+(xdst.left*bpp);
6639 /* First, all the 'source-less' blits */
6640 if (flags & WINEDDBLT_COLORFILL)
6642 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dlock.Pitch, fx->u5.dwFillColor);
6643 flags &= ~WINEDDBLT_COLORFILL;
6646 if (flags & WINEDDBLT_DEPTHFILL)
6648 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6650 if (flags & WINEDDBLT_ROP)
6652 /* Catch some degenerate cases here. */
6656 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,0);
6658 case 0xAA0029: /* No-op */
6661 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,~0);
6663 case SRCCOPY: /* Well, we do that below? */
6666 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6669 flags &= ~WINEDDBLT_ROP;
6671 if (flags & WINEDDBLT_DDROPS)
6673 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6675 /* Now the 'with source' blits. */
6679 int sx, xinc, sy, yinc;
6681 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6684 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6685 && (srcwidth != dstwidth || srcheight != dstheight))
6687 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6688 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6691 sbase = (BYTE*)slock.pBits+(xsrc.top*slock.Pitch)+xsrc.left*bpp;
6692 xinc = (srcwidth << 16) / dstwidth;
6693 yinc = (srcheight << 16) / dstheight;
6697 /* No effects, we can cheat here. */
6698 if (dstwidth == srcwidth)
6700 if (dstheight == srcheight)
6702 /* No stretching in either direction. This needs to be as
6703 * fast as possible. */
6706 /* Check for overlapping surfaces. */
6707 if (src_surface != dst_surface || xdst.top < xsrc.top
6708 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6710 /* No overlap, or dst above src, so copy from top downwards. */
6711 for (y = 0; y < dstheight; ++y)
6713 memcpy(dbuf, sbuf, width);
6714 sbuf += slock.Pitch;
6715 dbuf += dlock.Pitch;
6718 else if (xdst.top > xsrc.top)
6720 /* Copy from bottom upwards. */
6721 sbuf += (slock.Pitch*dstheight);
6722 dbuf += (dlock.Pitch*dstheight);
6723 for (y = 0; y < dstheight; ++y)
6725 sbuf -= slock.Pitch;
6726 dbuf -= dlock.Pitch;
6727 memcpy(dbuf, sbuf, width);
6732 /* Src and dst overlapping on the same line, use memmove. */
6733 for (y = 0; y < dstheight; ++y)
6735 memmove(dbuf, sbuf, width);
6736 sbuf += slock.Pitch;
6737 dbuf += dlock.Pitch;
6743 /* Stretching in y direction only. */
6744 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6746 sbuf = sbase + (sy >> 16) * slock.Pitch;
6747 memcpy(dbuf, sbuf, width);
6748 dbuf += dlock.Pitch;
6754 /* Stretching in X direction. */
6756 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6758 sbuf = sbase + (sy >> 16) * slock.Pitch;
6760 if ((sy >> 16) == (last_sy >> 16))
6762 /* This source row is the same as last source row -
6763 * Copy the already stretched row. */
6764 memcpy(dbuf, dbuf - dlock.Pitch, width);
6768 #define STRETCH_ROW(type) \
6770 const type *s = (const type *)sbuf; \
6771 type *d = (type *)dbuf; \
6772 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6773 d[x] = s[sx >> 16]; \
6791 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6795 s = sbuf + 3 * (sx >> 16);
6796 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6797 d[0] = (pixel ) & 0xff;
6798 d[1] = (pixel >> 8) & 0xff;
6799 d[2] = (pixel >> 16) & 0xff;
6805 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6806 hr = WINED3DERR_NOTAVAILABLE;
6811 dbuf += dlock.Pitch;
6818 LONG dstyinc = dlock.Pitch, dstxinc = bpp;
6819 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6820 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6821 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6823 /* The color keying flags are checked for correctness in ddraw */
6824 if (flags & WINEDDBLT_KEYSRC)
6826 keylow = src_surface->SrcBltCKey.dwColorSpaceLowValue;
6827 keyhigh = src_surface->SrcBltCKey.dwColorSpaceHighValue;
6829 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6831 keylow = fx->ddckSrcColorkey.dwColorSpaceLowValue;
6832 keyhigh = fx->ddckSrcColorkey.dwColorSpaceHighValue;
6835 if (flags & WINEDDBLT_KEYDEST)
6837 /* Destination color keys are taken from the source surface! */
6838 destkeylow = src_surface->DestBltCKey.dwColorSpaceLowValue;
6839 destkeyhigh = src_surface->DestBltCKey.dwColorSpaceHighValue;
6841 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6843 destkeylow = fx->ddckDestColorkey.dwColorSpaceLowValue;
6844 destkeyhigh = fx->ddckDestColorkey.dwColorSpaceHighValue;
6853 keymask = src_format->red_mask
6854 | src_format->green_mask
6855 | src_format->blue_mask;
6857 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6860 if (flags & WINEDDBLT_DDFX)
6862 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6865 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6866 dBottomLeft = dTopLeft + ((dstheight - 1) * dlock.Pitch);
6867 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6869 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6871 /* I don't think we need to do anything about this flag */
6872 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6874 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6877 dTopRight = dTopLeft;
6880 dBottomRight = dBottomLeft;
6882 dstxinc = dstxinc * -1;
6884 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6887 dTopLeft = dBottomLeft;
6890 dTopRight = dBottomRight;
6892 dstyinc = dstyinc * -1;
6894 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6896 /* I don't think we need to do anything about this flag */
6897 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6899 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6902 dBottomRight = dTopLeft;
6905 dBottomLeft = dTopRight;
6907 dstxinc = dstxinc * -1;
6908 dstyinc = dstyinc * -1;
6910 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6913 dTopLeft = dBottomLeft;
6914 dBottomLeft = dBottomRight;
6915 dBottomRight = dTopRight;
6920 dstxinc = dstxinc * -1;
6922 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6925 dTopLeft = dTopRight;
6926 dTopRight = dBottomRight;
6927 dBottomRight = dBottomLeft;
6932 dstyinc = dstyinc * -1;
6934 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6936 /* I don't think we need to do anything about this flag */
6937 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6940 flags &= ~(WINEDDBLT_DDFX);
6943 #define COPY_COLORKEY_FX(type) \
6946 type *d = (type *)dbuf, *dx, tmp; \
6947 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6949 s = (const type *)(sbase + (sy >> 16) * slock.Pitch); \
6951 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6953 tmp = s[sx >> 16]; \
6954 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6955 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6959 dx = (type *)(((BYTE *)dx) + dstxinc); \
6961 d = (type *)(((BYTE *)d) + dstyinc); \
6968 COPY_COLORKEY_FX(BYTE);
6971 COPY_COLORKEY_FX(WORD);
6974 COPY_COLORKEY_FX(DWORD);
6979 BYTE *d = dbuf, *dx;
6980 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6982 sbuf = sbase + (sy >> 16) * slock.Pitch;
6984 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
6986 DWORD pixel, dpixel = 0;
6987 s = sbuf + 3 * (sx>>16);
6988 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6989 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
6990 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
6991 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
6993 dx[0] = (pixel ) & 0xff;
6994 dx[1] = (pixel >> 8) & 0xff;
6995 dx[2] = (pixel >> 16) & 0xff;
7004 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7005 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7006 hr = WINED3DERR_NOTAVAILABLE;
7008 #undef COPY_COLORKEY_FX
7014 if (flags && FIXME_ON(d3d_surface))
7016 FIXME("\tUnsupported flags: %#x.\n", flags);
7020 wined3d_surface_unmap(dst_surface);
7021 if (src_surface && src_surface != dst_surface)
7022 wined3d_surface_unmap(src_surface);
7023 /* Release the converted surface, if any. */
7024 if (src_surface && src_surface != orig_src)
7025 wined3d_surface_decref(src_surface);
7030 /* Do not call while under the GL lock. */
7031 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7032 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
7034 static const RECT src_rect;
7037 memset(&BltFx, 0, sizeof(BltFx));
7038 BltFx.dwSize = sizeof(BltFx);
7039 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7040 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7041 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7044 /* Do not call while under the GL lock. */
7045 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7046 struct wined3d_surface *surface, const RECT *rect, float depth)
7048 FIXME("Depth filling not implemented by cpu_blit.\n");
7049 return WINED3DERR_INVALIDCALL;
7052 const struct blit_shader cpu_blit = {
7058 cpu_blit_color_fill,
7059 cpu_blit_depth_fill,
7062 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7063 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
7064 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7065 WINED3DPOOL pool, void *parent, const struct wined3d_parent_ops *parent_ops)
7067 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7068 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7069 unsigned int resource_size;
7072 if (multisample_quality > 0)
7074 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7075 multisample_quality = 0;
7078 /* Quick lockable sanity check.
7079 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7080 * this function is too deep to need to care about things like this.
7081 * Levels need to be checked too, since they all affect what can be done. */
7084 case WINED3DPOOL_SCRATCH:
7087 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7088 "which are mutually exclusive, setting lockable to TRUE.\n");
7093 case WINED3DPOOL_SYSTEMMEM:
7095 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7098 case WINED3DPOOL_MANAGED:
7099 if (usage & WINED3DUSAGE_DYNAMIC)
7100 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7103 case WINED3DPOOL_DEFAULT:
7104 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7105 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7109 FIXME("Unknown pool %#x.\n", pool);
7113 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7114 FIXME("Trying to create a render target that isn't in the default pool.\n");
7116 /* FIXME: Check that the format is supported by the device. */
7118 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7120 return WINED3DERR_INVALIDCALL;
7122 surface->surface_type = surface_type;
7124 switch (surface_type)
7126 case SURFACE_OPENGL:
7127 surface->surface_ops = &surface_ops;
7131 surface->surface_ops = &gdi_surface_ops;
7135 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7136 return WINED3DERR_INVALIDCALL;
7139 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7140 multisample_type, multisample_quality, usage, pool, width, height, 1,
7141 resource_size, parent, parent_ops, &surface_resource_ops);
7144 WARN("Failed to initialize resource, returning %#x.\n", hr);
7148 /* "Standalone" surface. */
7149 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7151 surface->texture_level = level;
7152 list_init(&surface->overlays);
7155 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7157 surface->flags |= SFLAG_DISCARD;
7158 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7159 surface->flags |= SFLAG_LOCKABLE;
7160 /* I'm not sure if this qualifies as a hack or as an optimization. It
7161 * seems reasonable to assume that lockable render targets will get
7162 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7163 * creation. However, the other reason we want to do this is that several
7164 * ddraw applications access surface memory while the surface isn't
7165 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7166 * future locks prevents these from crashing. */
7167 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7168 surface->flags |= SFLAG_DYNLOCK;
7170 /* Mark the texture as dirty so that it gets loaded first time around. */
7171 surface_add_dirty_rect(surface, NULL);
7172 list_init(&surface->renderbuffers);
7174 TRACE("surface %p, memory %p, size %u\n",
7175 surface, surface->resource.allocatedMemory, surface->resource.size);
7177 /* Call the private setup routine */
7178 hr = surface->surface_ops->surface_private_setup(surface);
7181 ERR("Private setup failed, returning %#x\n", hr);
7182 surface_cleanup(surface);
7189 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7190 enum wined3d_format_id format_id, BOOL lockable, BOOL discard, UINT level, DWORD usage, WINED3DPOOL pool,
7191 WINED3DMULTISAMPLE_TYPE multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7192 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7194 struct wined3d_surface *object;
7197 TRACE("device %p, width %u, height %u, format %s, lockable %#x, discard %#x, level %u\n",
7198 device, width, height, debug_d3dformat(format_id), lockable, discard, level);
7199 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7200 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7201 TRACE("surface_type %#x, parent %p, parent_ops %p.\n", surface_type, parent, parent_ops);
7203 if (surface_type == SURFACE_OPENGL && !device->adapter)
7205 ERR("OpenGL surfaces are not available without OpenGL.\n");
7206 return WINED3DERR_NOTAVAILABLE;
7209 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7212 ERR("Failed to allocate surface memory.\n");
7213 return WINED3DERR_OUTOFVIDEOMEMORY;
7216 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level, lockable,
7217 discard, multisample_type, multisample_quality, device, usage, format_id, pool, parent, parent_ops);
7220 WARN("Failed to initialize surface, returning %#x.\n", hr);
7221 HeapFree(GetProcessHeap(), 0, object);
7225 TRACE("Created surface %p.\n", object);