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 surface_cpu_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
40 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD trans);
41 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
42 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *fx,
43 WINED3DTEXTUREFILTERTYPE filter);
45 static void surface_cleanup(struct wined3d_surface *surface)
47 TRACE("surface %p.\n", surface);
49 if (surface->texture_name || (surface->flags & SFLAG_PBO) || !list_empty(&surface->renderbuffers))
51 struct wined3d_renderbuffer_entry *entry, *entry2;
52 const struct wined3d_gl_info *gl_info;
53 struct wined3d_context *context;
55 context = context_acquire(surface->resource.device, NULL);
56 gl_info = context->gl_info;
60 if (surface->texture_name)
62 TRACE("Deleting texture %u.\n", surface->texture_name);
63 glDeleteTextures(1, &surface->texture_name);
66 if (surface->flags & SFLAG_PBO)
68 TRACE("Deleting PBO %u.\n", surface->pbo);
69 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
72 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
74 TRACE("Deleting renderbuffer %u.\n", entry->id);
75 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
76 HeapFree(GetProcessHeap(), 0, entry);
81 context_release(context);
84 if (surface->flags & SFLAG_DIBSECTION)
87 SelectObject(surface->hDC, surface->dib.holdbitmap);
88 DeleteDC(surface->hDC);
89 /* Release the DIB section. */
90 DeleteObject(surface->dib.DIBsection);
91 surface->dib.bitmap_data = NULL;
92 surface->resource.allocatedMemory = NULL;
95 if (surface->flags & SFLAG_USERPTR)
96 wined3d_surface_set_mem(surface, NULL);
97 if (surface->overlay_dest)
98 list_remove(&surface->overlay_entry);
100 HeapFree(GetProcessHeap(), 0, surface->palette9);
102 resource_cleanup(&surface->resource);
105 void surface_set_container(struct wined3d_surface *surface, enum wined3d_container_type type, void *container)
107 TRACE("surface %p, container %p.\n", surface, container);
109 if (!container && type != WINED3D_CONTAINER_NONE)
110 ERR("Setting NULL container of type %#x.\n", type);
112 if (type == WINED3D_CONTAINER_SWAPCHAIN)
114 surface->get_drawable_size = get_drawable_size_swapchain;
118 switch (wined3d_settings.offscreen_rendering_mode)
121 surface->get_drawable_size = get_drawable_size_fbo;
125 surface->get_drawable_size = get_drawable_size_backbuffer;
129 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
134 surface->container.type = type;
135 surface->container.u.base = container;
142 enum tex_types tex_type;
143 GLfloat coords[4][3];
154 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
156 f->l = ((r->left * 2.0f) / w) - 1.0f;
157 f->t = ((r->top * 2.0f) / h) - 1.0f;
158 f->r = ((r->right * 2.0f) / w) - 1.0f;
159 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
162 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
164 GLfloat (*coords)[3] = info->coords;
170 FIXME("Unsupported texture target %#x\n", target);
171 /* Fall back to GL_TEXTURE_2D */
173 info->binding = GL_TEXTURE_BINDING_2D;
174 info->bind_target = GL_TEXTURE_2D;
175 info->tex_type = tex_2d;
176 coords[0][0] = (float)rect->left / w;
177 coords[0][1] = (float)rect->top / h;
180 coords[1][0] = (float)rect->right / w;
181 coords[1][1] = (float)rect->top / h;
184 coords[2][0] = (float)rect->left / w;
185 coords[2][1] = (float)rect->bottom / h;
188 coords[3][0] = (float)rect->right / w;
189 coords[3][1] = (float)rect->bottom / h;
193 case GL_TEXTURE_RECTANGLE_ARB:
194 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
195 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
196 info->tex_type = tex_rect;
197 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
198 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
199 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
200 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
203 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
204 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
205 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
206 info->tex_type = tex_cube;
207 cube_coords_float(rect, w, h, &f);
209 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
210 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
211 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
212 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
215 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
216 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
217 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
218 info->tex_type = tex_cube;
219 cube_coords_float(rect, w, h, &f);
221 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
222 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
223 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
224 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
227 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
228 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
229 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
230 info->tex_type = tex_cube;
231 cube_coords_float(rect, w, h, &f);
233 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
234 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
235 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
236 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
239 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
240 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
241 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
242 info->tex_type = tex_cube;
243 cube_coords_float(rect, w, h, &f);
245 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
246 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
247 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
248 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
251 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
252 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
253 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
254 info->tex_type = tex_cube;
255 cube_coords_float(rect, w, h, &f);
257 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
258 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
259 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
260 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
263 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
264 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
265 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
266 info->tex_type = tex_cube;
267 cube_coords_float(rect, w, h, &f);
269 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
270 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
271 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
272 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
277 static inline void surface_get_rect(struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
280 *rect_out = *rect_in;
285 rect_out->right = surface->resource.width;
286 rect_out->bottom = surface->resource.height;
290 /* GL locking and context activation is done by the caller */
291 void draw_textured_quad(struct wined3d_surface *src_surface, const RECT *src_rect,
292 const RECT *dst_rect, WINED3DTEXTUREFILTERTYPE Filter)
294 struct blt_info info;
296 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
298 glEnable(info.bind_target);
299 checkGLcall("glEnable(bind_target)");
301 /* Bind the texture */
302 glBindTexture(info.bind_target, src_surface->texture_name);
303 checkGLcall("glBindTexture");
305 /* Filtering for StretchRect */
306 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
307 wined3d_gl_mag_filter(magLookup, Filter));
308 checkGLcall("glTexParameteri");
309 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
310 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
311 checkGLcall("glTexParameteri");
312 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
313 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
314 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
315 checkGLcall("glTexEnvi");
318 glBegin(GL_TRIANGLE_STRIP);
319 glTexCoord3fv(info.coords[0]);
320 glVertex2i(dst_rect->left, dst_rect->top);
322 glTexCoord3fv(info.coords[1]);
323 glVertex2i(dst_rect->right, dst_rect->top);
325 glTexCoord3fv(info.coords[2]);
326 glVertex2i(dst_rect->left, dst_rect->bottom);
328 glTexCoord3fv(info.coords[3]);
329 glVertex2i(dst_rect->right, dst_rect->bottom);
332 /* Unbind the texture */
333 glBindTexture(info.bind_target, 0);
334 checkGLcall("glBindTexture(info->bind_target, 0)");
336 /* We changed the filtering settings on the texture. Inform the
337 * container about this to get the filters reset properly next draw. */
338 if (src_surface->container.type == WINED3D_CONTAINER_TEXTURE)
340 struct wined3d_texture *texture = src_surface->container.u.texture;
341 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
342 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
343 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
347 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
349 const struct wined3d_format *format = surface->resource.format;
357 TRACE("surface %p.\n", surface);
359 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
361 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
362 return WINED3DERR_INVALIDCALL;
365 switch (format->byte_count)
369 /* Allocate extra space to store the RGB bit masks. */
370 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
374 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
378 /* Allocate extra space for a palette. */
379 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
380 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
385 return E_OUTOFMEMORY;
387 /* Some applications access the surface in via DWORDs, and do not take
388 * the necessary care at the end of the surface. So we need at least
389 * 4 extra bytes at the end of the surface. Check against the page size,
390 * if the last page used for the surface has at least 4 spare bytes we're
391 * safe, otherwise add an extra line to the DIB section. */
392 GetSystemInfo(&sysInfo);
393 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
396 TRACE("Adding an extra line to the DIB section.\n");
399 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
400 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
401 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
402 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
403 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
404 * wined3d_surface_get_pitch(surface);
405 b_info->bmiHeader.biPlanes = 1;
406 b_info->bmiHeader.biBitCount = format->byte_count * 8;
408 b_info->bmiHeader.biXPelsPerMeter = 0;
409 b_info->bmiHeader.biYPelsPerMeter = 0;
410 b_info->bmiHeader.biClrUsed = 0;
411 b_info->bmiHeader.biClrImportant = 0;
413 /* Get the bit masks */
414 masks = (DWORD *)b_info->bmiColors;
415 switch (surface->resource.format->id)
417 case WINED3DFMT_B8G8R8_UNORM:
418 usage = DIB_RGB_COLORS;
419 b_info->bmiHeader.biCompression = BI_RGB;
422 case WINED3DFMT_B5G5R5X1_UNORM:
423 case WINED3DFMT_B5G5R5A1_UNORM:
424 case WINED3DFMT_B4G4R4A4_UNORM:
425 case WINED3DFMT_B4G4R4X4_UNORM:
426 case WINED3DFMT_B2G3R3_UNORM:
427 case WINED3DFMT_B2G3R3A8_UNORM:
428 case WINED3DFMT_R10G10B10A2_UNORM:
429 case WINED3DFMT_R8G8B8A8_UNORM:
430 case WINED3DFMT_R8G8B8X8_UNORM:
431 case WINED3DFMT_B10G10R10A2_UNORM:
432 case WINED3DFMT_B5G6R5_UNORM:
433 case WINED3DFMT_R16G16B16A16_UNORM:
435 b_info->bmiHeader.biCompression = BI_BITFIELDS;
436 masks[0] = format->red_mask;
437 masks[1] = format->green_mask;
438 masks[2] = format->blue_mask;
442 /* Don't know palette */
443 b_info->bmiHeader.biCompression = BI_RGB;
448 if (!(dc = GetDC(0)))
450 HeapFree(GetProcessHeap(), 0, b_info);
451 return HRESULT_FROM_WIN32(GetLastError());
454 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
455 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
456 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
457 surface->dib.DIBsection = CreateDIBSection(dc, b_info, usage, &surface->dib.bitmap_data, 0, 0);
460 if (!surface->dib.DIBsection)
462 ERR("Failed to create DIB section.\n");
463 HeapFree(GetProcessHeap(), 0, b_info);
464 return HRESULT_FROM_WIN32(GetLastError());
467 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
468 /* Copy the existing surface to the dib section. */
469 if (surface->resource.allocatedMemory)
471 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
472 surface->resource.height * wined3d_surface_get_pitch(surface));
476 /* This is to make maps read the GL texture although memory is allocated. */
477 surface->flags &= ~SFLAG_INSYSMEM;
479 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
481 HeapFree(GetProcessHeap(), 0, b_info);
483 /* Now allocate a DC. */
484 surface->hDC = CreateCompatibleDC(0);
485 surface->dib.holdbitmap = SelectObject(surface->hDC, surface->dib.DIBsection);
486 TRACE("Using wined3d palette %p.\n", surface->palette);
487 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
489 surface->flags |= SFLAG_DIBSECTION;
491 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
492 surface->resource.heapMemory = NULL;
497 static void surface_prepare_system_memory(struct wined3d_surface *surface)
499 struct wined3d_device *device = surface->resource.device;
500 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
502 TRACE("surface %p.\n", surface);
504 /* Performance optimization: Count how often a surface is locked, if it is
505 * locked regularly do not throw away the system memory copy. This avoids
506 * the need to download the surface from OpenGL all the time. The surface
507 * is still downloaded if the OpenGL texture is changed. */
508 if (!(surface->flags & SFLAG_DYNLOCK))
510 if (++surface->lockCount > MAXLOCKCOUNT)
512 TRACE("Surface is locked regularly, not freeing the system memory copy any more.\n");
513 surface->flags |= SFLAG_DYNLOCK;
517 /* Create a PBO for dynamically locked surfaces but don't do it for
518 * converted or NPOT surfaces. Also don't create a PBO for systemmem
520 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (surface->flags & SFLAG_DYNLOCK)
521 && !(surface->flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
522 && (surface->resource.pool != WINED3DPOOL_SYSTEMMEM))
524 struct wined3d_context *context;
527 context = context_acquire(device, NULL);
530 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
531 error = glGetError();
532 if (!surface->pbo || error != GL_NO_ERROR)
533 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
535 TRACE("Binding PBO %u.\n", surface->pbo);
537 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
538 checkGLcall("glBindBufferARB");
540 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
541 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
542 checkGLcall("glBufferDataARB");
544 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
545 checkGLcall("glBindBufferARB");
547 /* We don't need the system memory anymore and we can't even use it for PBOs. */
548 if (!(surface->flags & SFLAG_CLIENT))
550 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
551 surface->resource.heapMemory = NULL;
553 surface->resource.allocatedMemory = NULL;
554 surface->flags |= SFLAG_PBO;
556 context_release(context);
558 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
560 /* Whatever surface we have, make sure that there is memory allocated
561 * for the downloaded copy, or a PBO to map. */
562 if (!surface->resource.heapMemory)
563 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
565 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
566 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
568 if (surface->flags & SFLAG_INSYSMEM)
569 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
573 static void surface_evict_sysmem(struct wined3d_surface *surface)
575 if (surface->flags & SFLAG_DONOTFREE)
578 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
579 surface->resource.allocatedMemory = NULL;
580 surface->resource.heapMemory = NULL;
581 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
584 /* Context activation is done by the caller. */
585 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
586 const struct wined3d_gl_info *gl_info, BOOL srgb)
588 struct wined3d_device *device = surface->resource.device;
589 DWORD active_sampler;
590 GLint active_texture;
592 /* We don't need a specific texture unit, but after binding the texture
593 * the current unit is dirty. Read the unit back instead of switching to
594 * 0, this avoids messing around with the state manager's GL states. The
595 * current texture unit should always be a valid one.
597 * To be more specific, this is tricky because we can implicitly be
598 * called from sampler() in state.c. This means we can't touch anything
599 * other than whatever happens to be the currently active texture, or we
600 * would risk marking already applied sampler states dirty again.
602 * TODO: Track the current active texture per GL context instead of using
606 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
608 active_sampler = device->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
610 if (active_sampler != WINED3D_UNMAPPED_STAGE)
611 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
612 surface_bind(surface, gl_info, srgb);
615 static void surface_force_reload(struct wined3d_surface *surface)
617 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
620 static void surface_release_client_storage(struct wined3d_surface *surface)
622 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
625 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
626 if (surface->texture_name)
628 surface_bind_and_dirtify(surface, context->gl_info, FALSE);
629 glTexImage2D(surface->texture_target, surface->texture_level,
630 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
632 if (surface->texture_name_srgb)
634 surface_bind_and_dirtify(surface, context->gl_info, TRUE);
635 glTexImage2D(surface->texture_target, surface->texture_level,
636 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
638 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
641 context_release(context);
643 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
644 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
645 surface_force_reload(surface);
648 static HRESULT surface_private_setup(struct wined3d_surface *surface)
650 /* TODO: Check against the maximum texture sizes supported by the video card. */
651 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
652 unsigned int pow2Width, pow2Height;
654 TRACE("surface %p.\n", surface);
656 surface->texture_name = 0;
657 surface->texture_target = GL_TEXTURE_2D;
659 /* Non-power2 support */
660 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
662 pow2Width = surface->resource.width;
663 pow2Height = surface->resource.height;
667 /* Find the nearest pow2 match */
668 pow2Width = pow2Height = 1;
669 while (pow2Width < surface->resource.width)
671 while (pow2Height < surface->resource.height)
674 surface->pow2Width = pow2Width;
675 surface->pow2Height = pow2Height;
677 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
679 /* TODO: Add support for non power two compressed textures. */
680 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
682 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
683 surface, surface->resource.width, surface->resource.height);
684 return WINED3DERR_NOTAVAILABLE;
688 if (pow2Width != surface->resource.width
689 || pow2Height != surface->resource.height)
691 surface->flags |= SFLAG_NONPOW2;
694 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
695 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
697 /* One of three options:
698 * 1: Do the same as we do with NPOT and scale the texture, (any
699 * texture ops would require the texture to be scaled which is
701 * 2: Set the texture to the maximum size (bad idea).
702 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
703 * 4: Create the surface, but allow it to be used only for DirectDraw
704 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
705 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
706 * the render target. */
707 if (surface->resource.pool == WINED3DPOOL_DEFAULT || surface->resource.pool == WINED3DPOOL_MANAGED)
709 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
710 return WINED3DERR_NOTAVAILABLE;
713 /* We should never use this surface in combination with OpenGL! */
714 TRACE("Creating an oversized surface: %ux%u.\n",
715 surface->pow2Width, surface->pow2Height);
719 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
720 * and EXT_PALETTED_TEXTURE is used in combination with texture
721 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
722 * EXT_PALETTED_TEXTURE doesn't work in combination with
723 * ARB_TEXTURE_RECTANGLE. */
724 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
725 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
726 && gl_info->supported[EXT_PALETTED_TEXTURE]
727 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
729 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
730 surface->pow2Width = surface->resource.width;
731 surface->pow2Height = surface->resource.height;
732 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
736 switch (wined3d_settings.offscreen_rendering_mode)
739 surface->get_drawable_size = get_drawable_size_fbo;
743 surface->get_drawable_size = get_drawable_size_backbuffer;
747 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
748 return WINED3DERR_INVALIDCALL;
751 surface->flags |= SFLAG_INSYSMEM;
756 static void surface_realize_palette(struct wined3d_surface *surface)
758 struct wined3d_palette *palette = surface->palette;
760 TRACE("surface %p.\n", surface);
762 if (!palette) return;
764 if (surface->resource.format->id == WINED3DFMT_P8_UINT
765 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
767 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
769 /* Make sure the texture is up to date. This call doesn't do
770 * anything if the texture is already up to date. */
771 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
773 /* We want to force a palette refresh, so mark the drawable as not being up to date */
774 if (!surface_is_offscreen(surface))
775 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
779 if (!(surface->flags & SFLAG_INSYSMEM))
781 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
782 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
784 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
788 if (surface->flags & SFLAG_DIBSECTION)
793 TRACE("Updating the DC's palette.\n");
795 for (i = 0; i < 256; ++i)
797 col[i].rgbRed = palette->palents[i].peRed;
798 col[i].rgbGreen = palette->palents[i].peGreen;
799 col[i].rgbBlue = palette->palents[i].peBlue;
800 col[i].rgbReserved = 0;
802 SetDIBColorTable(surface->hDC, 0, 256, col);
805 /* Propagate the changes to the drawable when we have a palette. */
806 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
807 surface_load_location(surface, SFLAG_INDRAWABLE, NULL);
810 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
814 /* If there's no destination surface there is nothing to do. */
815 if (!surface->overlay_dest)
818 /* Blt calls ModifyLocation on the dest surface, which in turn calls
819 * DrawOverlay to update the overlay. Prevent an endless recursion. */
820 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
823 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
824 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
825 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3DTEXF_LINEAR);
826 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
831 static void surface_preload(struct wined3d_surface *surface)
833 TRACE("surface %p.\n", surface);
835 surface_internal_preload(surface, SRGB_ANY);
838 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
840 struct wined3d_device *device = surface->resource.device;
841 const RECT *pass_rect = rect;
843 TRACE("surface %p, rect %s, flags %#x.\n",
844 surface, wine_dbgstr_rect(rect), flags);
846 if (flags & WINED3DLOCK_DISCARD)
848 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
849 surface_prepare_system_memory(surface);
850 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
854 /* surface_load_location() does not check if the rectangle specifies
855 * the full surface. Most callers don't need that, so do it here. */
856 if (rect && !rect->top && !rect->left
857 && rect->right == surface->resource.width
858 && rect->bottom == surface->resource.height)
861 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
862 && ((surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
863 || surface == device->fb.render_targets[0])))
864 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
867 if (surface->flags & SFLAG_PBO)
869 const struct wined3d_gl_info *gl_info;
870 struct wined3d_context *context;
872 context = context_acquire(device, NULL);
873 gl_info = context->gl_info;
876 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
877 checkGLcall("glBindBufferARB");
879 /* This shouldn't happen but could occur if some other function
880 * didn't handle the PBO properly. */
881 if (surface->resource.allocatedMemory)
882 ERR("The surface already has PBO memory allocated.\n");
884 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
885 checkGLcall("glMapBufferARB");
887 /* Make sure the PBO isn't set anymore in order not to break non-PBO
889 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
890 checkGLcall("glBindBufferARB");
893 context_release(context);
896 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
899 surface_add_dirty_rect(surface, NULL);
906 b.Right = rect->right;
907 b.Bottom = rect->bottom;
910 surface_add_dirty_rect(surface, &b);
915 static void surface_unmap(struct wined3d_surface *surface)
917 struct wined3d_device *device = surface->resource.device;
920 TRACE("surface %p.\n", surface);
922 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
924 if (surface->flags & SFLAG_PBO)
926 const struct wined3d_gl_info *gl_info;
927 struct wined3d_context *context;
929 TRACE("Freeing PBO memory.\n");
931 context = context_acquire(device, NULL);
932 gl_info = context->gl_info;
935 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
936 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
937 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
938 checkGLcall("glUnmapBufferARB");
940 context_release(context);
942 surface->resource.allocatedMemory = NULL;
945 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
947 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
949 TRACE("Not dirtified, nothing to do.\n");
953 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
954 || (device->fb.render_targets && surface == device->fb.render_targets[0]))
956 if (wined3d_settings.rendertargetlock_mode == RTL_DISABLE)
958 static BOOL warned = FALSE;
961 ERR("The application tries to write to the render target, but render target locking is disabled.\n");
967 if (!surface->dirtyRect.left && !surface->dirtyRect.top
968 && surface->dirtyRect.right == surface->resource.width
969 && surface->dirtyRect.bottom == surface->resource.height)
975 /* TODO: Proper partial rectangle tracking. */
977 surface->flags |= SFLAG_INSYSMEM;
980 surface_load_location(surface, SFLAG_INDRAWABLE, fullsurface ? NULL : &surface->dirtyRect);
982 /* Partial rectangle tracking is not commonly implemented, it is only
983 * done for render targets. INSYSMEM was set before to tell
984 * surface_load_location() where to read the rectangle from.
985 * Indrawable is set because all modifications from the partial
986 * sysmem copy are written back to the drawable, thus the surface is
987 * merged again in the drawable. The sysmem copy is not fully up to
988 * date because only a subrectangle was read in Map(). */
991 surface_modify_location(surface, SFLAG_INDRAWABLE, TRUE);
992 surface_evict_sysmem(surface);
995 surface->dirtyRect.left = surface->resource.width;
996 surface->dirtyRect.top = surface->resource.height;
997 surface->dirtyRect.right = 0;
998 surface->dirtyRect.bottom = 0;
1000 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
1002 FIXME("Depth / stencil buffer locking is not implemented.\n");
1006 /* Overlays have to be redrawn manually after changes with the GL implementation */
1007 if (surface->overlay_dest)
1008 surface->surface_ops->surface_draw_overlay(surface);
1011 static HRESULT surface_getdc(struct wined3d_surface *surface)
1013 WINED3DLOCKED_RECT lock;
1016 TRACE("surface %p.\n", surface);
1018 /* Create a DIB section if there isn't a dc yet. */
1021 if (surface->flags & SFLAG_CLIENT)
1023 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1024 surface_release_client_storage(surface);
1026 hr = surface_create_dib_section(surface);
1028 return WINED3DERR_INVALIDCALL;
1030 /* Use the DIB section from now on if we are not using a PBO. */
1031 if (!(surface->flags & SFLAG_PBO))
1032 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1035 /* Map the surface. */
1036 hr = wined3d_surface_map(surface, &lock, NULL, 0);
1038 ERR("Map failed, hr %#x.\n", hr);
1040 /* Sync the DIB with the PBO. This can't be done earlier because Map()
1041 * activates the allocatedMemory. */
1042 if (surface->flags & SFLAG_PBO)
1043 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->dib.bitmap_size);
1048 static HRESULT surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override)
1050 TRACE("surface %p, override %p.\n", surface, override);
1052 /* Flipping is only supported on render targets and overlays. */
1053 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
1055 WARN("Tried to flip a non-render target, non-overlay surface.\n");
1056 return WINEDDERR_NOTFLIPPABLE;
1059 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1061 flip_surface(surface, override);
1063 /* Update the overlay if it is visible */
1064 if (surface->overlay_dest)
1065 return surface->surface_ops->surface_draw_overlay(surface);
1073 static BOOL surface_is_full_rect(struct wined3d_surface *surface, const RECT *r)
1075 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1077 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1082 static void wined3d_surface_depth_blt_fbo(struct wined3d_device *device, struct wined3d_surface *src_surface,
1083 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1085 const struct wined3d_gl_info *gl_info;
1086 struct wined3d_context *context;
1087 DWORD src_mask, dst_mask;
1090 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1091 device, src_surface, wine_dbgstr_rect(src_rect),
1092 dst_surface, wine_dbgstr_rect(dst_rect));
1094 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1095 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1097 if (src_mask != dst_mask)
1099 ERR("Incompatible formats %s and %s.\n",
1100 debug_d3dformat(src_surface->resource.format->id),
1101 debug_d3dformat(dst_surface->resource.format->id));
1107 ERR("Not a depth / stencil format: %s.\n",
1108 debug_d3dformat(src_surface->resource.format->id));
1113 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1114 gl_mask |= GL_DEPTH_BUFFER_BIT;
1115 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1116 gl_mask |= GL_STENCIL_BUFFER_BIT;
1118 /* Make sure the locations are up-to-date. Loading the destination
1119 * surface isn't required if the entire surface is overwritten. */
1120 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1121 if (!surface_is_full_rect(dst_surface, dst_rect))
1122 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1124 context = context_acquire(device, NULL);
1125 if (!context->valid)
1127 context_release(context);
1128 WARN("Invalid context, skipping blit.\n");
1132 gl_info = context->gl_info;
1136 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1137 glReadBuffer(GL_NONE);
1138 checkGLcall("glReadBuffer()");
1139 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1141 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1142 context_set_draw_buffer(context, GL_NONE);
1143 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1145 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1147 glDepthMask(GL_TRUE);
1148 device_invalidate_state(device, STATE_RENDER(WINED3DRS_ZWRITEENABLE));
1150 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1152 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1154 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1155 device_invalidate_state(device, STATE_RENDER(WINED3DRS_TWOSIDEDSTENCILMODE));
1158 device_invalidate_state(device, STATE_RENDER(WINED3DRS_STENCILWRITEMASK));
1161 glDisable(GL_SCISSOR_TEST);
1162 device_invalidate_state(device, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1164 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1165 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1166 checkGLcall("glBlitFramebuffer()");
1170 if (wined3d_settings.strict_draw_ordering)
1171 wglFlush(); /* Flush to ensure ordering across contexts. */
1173 context_release(context);
1176 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1177 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
1178 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
1180 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1183 /* Source and/or destination need to be on the GL side */
1184 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
1189 case WINED3D_BLIT_OP_COLOR_BLIT:
1190 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1192 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1196 case WINED3D_BLIT_OP_DEPTH_BLIT:
1197 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1199 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1207 if (!(src_format->id == dst_format->id
1208 || (is_identity_fixup(src_format->color_fixup)
1209 && is_identity_fixup(dst_format->color_fixup))))
1215 static BOOL surface_convert_depth_to_float(struct wined3d_surface *surface, DWORD depth, float *float_depth)
1217 const struct wined3d_format *format = surface->resource.format;
1221 case WINED3DFMT_S1_UINT_D15_UNORM:
1222 *float_depth = depth / (float)0x00007fff;
1225 case WINED3DFMT_D16_UNORM:
1226 *float_depth = depth / (float)0x0000ffff;
1229 case WINED3DFMT_D24_UNORM_S8_UINT:
1230 case WINED3DFMT_X8D24_UNORM:
1231 *float_depth = depth / (float)0x00ffffff;
1234 case WINED3DFMT_D32_UNORM:
1235 *float_depth = depth / (float)0xffffffff;
1239 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1246 /* Do not call while under the GL lock. */
1247 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1249 const struct wined3d_resource *resource = &surface->resource;
1250 struct wined3d_device *device = resource->device;
1251 const struct blit_shader *blitter;
1253 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1254 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1257 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1258 return WINED3DERR_INVALIDCALL;
1261 return blitter->depth_fill(device, surface, rect, depth);
1264 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1265 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1267 struct wined3d_device *device = src_surface->resource.device;
1269 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1270 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1271 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1272 return WINED3DERR_INVALIDCALL;
1274 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1276 surface_modify_ds_location(dst_surface, SFLAG_DS_OFFSCREEN,
1277 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1278 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
1283 /* Do not call while under the GL lock. */
1284 static HRESULT surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1285 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1286 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1288 struct wined3d_device *device = dst_surface->resource.device;
1289 DWORD src_ds_flags, dst_ds_flags;
1291 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1292 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1293 flags, fx, debug_d3dtexturefiltertype(filter));
1294 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1296 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1298 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
1299 return WINEDDERR_SURFACEBUSY;
1302 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1304 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1308 if (src_ds_flags || dst_ds_flags)
1310 if (flags & WINEDDBLT_DEPTHFILL)
1315 TRACE("Depth fill.\n");
1317 surface_get_rect(dst_surface, dst_rect_in, &rect);
1319 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1320 return WINED3DERR_INVALIDCALL;
1322 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &rect, depth)))
1327 RECT src_rect, dst_rect;
1329 /* Accessing depth / stencil surfaces is supposed to fail while in
1330 * a scene, except for fills, which seem to work. */
1331 if (device->inScene)
1333 WARN("Rejecting depth / stencil access while in scene.\n");
1334 return WINED3DERR_INVALIDCALL;
1337 if (src_ds_flags != dst_ds_flags)
1339 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1340 return WINED3DERR_INVALIDCALL;
1343 if (src_rect_in && (src_rect_in->top || src_rect_in->left
1344 || src_rect_in->bottom != src_surface->resource.height
1345 || src_rect_in->right != src_surface->resource.width))
1347 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1348 wine_dbgstr_rect(src_rect_in));
1349 return WINED3DERR_INVALIDCALL;
1352 if (dst_rect_in && (dst_rect_in->top || dst_rect_in->left
1353 || dst_rect_in->bottom != dst_surface->resource.height
1354 || dst_rect_in->right != dst_surface->resource.width))
1356 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1357 wine_dbgstr_rect(src_rect_in));
1358 return WINED3DERR_INVALIDCALL;
1361 if (src_surface->resource.height != dst_surface->resource.height
1362 || src_surface->resource.width != dst_surface->resource.width)
1364 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1365 return WINED3DERR_INVALIDCALL;
1368 surface_get_rect(src_surface, src_rect_in, &src_rect);
1369 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1371 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1376 /* Special cases for render targets. */
1377 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1378 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1380 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, dst_rect_in,
1381 src_surface, src_rect_in, flags, fx, filter)))
1385 /* For the rest call the X11 surface implementation. For render targets
1386 * this should be implemented OpenGL accelerated in BltOverride, other
1387 * blits are rather rare. */
1388 return surface_cpu_blt(dst_surface, dst_rect_in, src_surface, src_rect_in, flags, fx, filter);
1391 /* Do not call while under the GL lock. */
1392 static HRESULT surface_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
1393 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD trans)
1395 struct wined3d_device *device = dst_surface->resource.device;
1397 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect %s, flags %#x.\n",
1398 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect_in), trans);
1400 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface->flags & SFLAG_LOCKED))
1402 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1403 return WINEDDERR_SURFACEBUSY;
1406 if (device->inScene && (dst_surface == device->fb.depth_stencil || src_surface == device->fb.depth_stencil))
1408 WARN("Attempt to access the depth / stencil surface while in a scene.\n");
1409 return WINED3DERR_INVALIDCALL;
1412 /* Special cases for RenderTargets */
1413 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1414 || (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
1417 RECT src_rect, dst_rect;
1420 surface_get_rect(src_surface, src_rect_in, &src_rect);
1422 dst_rect.left = dst_x;
1423 dst_rect.top = dst_y;
1424 dst_rect.right = dst_x + src_rect.right - src_rect.left;
1425 dst_rect.bottom = dst_y + src_rect.bottom - src_rect.top;
1427 /* Convert BltFast flags into Blt ones because BltOverride is called
1428 * from Blt as well. */
1429 if (trans & WINEDDBLTFAST_SRCCOLORKEY)
1430 flags |= WINEDDBLT_KEYSRC;
1431 if (trans & WINEDDBLTFAST_DESTCOLORKEY)
1432 flags |= WINEDDBLT_KEYDEST;
1433 if (trans & WINEDDBLTFAST_WAIT)
1434 flags |= WINEDDBLT_WAIT;
1435 if (trans & WINEDDBLTFAST_DONOTWAIT)
1436 flags |= WINEDDBLT_DONOTWAIT;
1438 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface,
1439 &dst_rect, src_surface, &src_rect, flags, NULL, WINED3DTEXF_POINT)))
1443 return surface_cpu_bltfast(dst_surface, dst_x, dst_y, src_surface, src_rect_in, trans);
1446 static HRESULT surface_set_mem(struct wined3d_surface *surface, void *mem)
1448 TRACE("surface %p, mem %p.\n", surface, mem);
1450 if (mem && mem != surface->resource.allocatedMemory)
1452 void *release = NULL;
1454 /* Do I have to copy the old surface content? */
1455 if (surface->flags & SFLAG_DIBSECTION)
1457 SelectObject(surface->hDC, surface->dib.holdbitmap);
1458 DeleteDC(surface->hDC);
1459 /* Release the DIB section. */
1460 DeleteObject(surface->dib.DIBsection);
1461 surface->dib.bitmap_data = NULL;
1462 surface->resource.allocatedMemory = NULL;
1463 surface->hDC = NULL;
1464 surface->flags &= ~SFLAG_DIBSECTION;
1466 else if (!(surface->flags & SFLAG_USERPTR))
1468 release = surface->resource.heapMemory;
1469 surface->resource.heapMemory = NULL;
1471 surface->resource.allocatedMemory = mem;
1472 surface->flags |= SFLAG_USERPTR;
1474 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
1475 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1477 /* For client textures OpenGL has to be notified. */
1478 if (surface->flags & SFLAG_CLIENT)
1479 surface_release_client_storage(surface);
1481 /* Now free the old memory if any. */
1482 HeapFree(GetProcessHeap(), 0, release);
1484 else if (surface->flags & SFLAG_USERPTR)
1486 /* Map and GetDC will re-create the dib section and allocated memory. */
1487 surface->resource.allocatedMemory = NULL;
1488 /* HeapMemory should be NULL already. */
1489 if (surface->resource.heapMemory)
1490 ERR("User pointer surface has heap memory allocated.\n");
1491 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
1493 if (surface->flags & SFLAG_CLIENT)
1494 surface_release_client_storage(surface);
1496 surface_prepare_system_memory(surface);
1497 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1503 /* Context activation is done by the caller. */
1504 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1506 if (!surface->resource.heapMemory)
1508 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1509 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1510 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1514 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1515 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1516 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1517 surface->resource.size, surface->resource.allocatedMemory));
1518 checkGLcall("glGetBufferSubDataARB");
1519 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1520 checkGLcall("glDeleteBuffersARB");
1524 surface->flags &= ~SFLAG_PBO;
1527 /* Do not call while under the GL lock. */
1528 static void surface_unload(struct wined3d_resource *resource)
1530 struct wined3d_surface *surface = surface_from_resource(resource);
1531 struct wined3d_renderbuffer_entry *entry, *entry2;
1532 struct wined3d_device *device = resource->device;
1533 const struct wined3d_gl_info *gl_info;
1534 struct wined3d_context *context;
1536 TRACE("surface %p.\n", surface);
1538 if (resource->pool == WINED3DPOOL_DEFAULT)
1540 /* Default pool resources are supposed to be destroyed before Reset is called.
1541 * Implicit resources stay however. So this means we have an implicit render target
1542 * or depth stencil. The content may be destroyed, but we still have to tear down
1543 * opengl resources, so we cannot leave early.
1545 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1546 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1547 * or the depth stencil into an FBO the texture or render buffer will be removed
1548 * and all flags get lost
1550 surface_init_sysmem(surface);
1554 /* Load the surface into system memory */
1555 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1556 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
1558 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1559 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1560 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1562 context = context_acquire(device, NULL);
1563 gl_info = context->gl_info;
1565 /* Destroy PBOs, but load them into real sysmem before */
1566 if (surface->flags & SFLAG_PBO)
1567 surface_remove_pbo(surface, gl_info);
1569 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1570 * all application-created targets the application has to release the surface
1571 * before calling _Reset
1573 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1576 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1578 list_remove(&entry->entry);
1579 HeapFree(GetProcessHeap(), 0, entry);
1581 list_init(&surface->renderbuffers);
1582 surface->current_renderbuffer = NULL;
1584 /* If we're in a texture, the texture name belongs to the texture.
1585 * Otherwise, destroy it. */
1586 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1589 glDeleteTextures(1, &surface->texture_name);
1590 surface->texture_name = 0;
1591 glDeleteTextures(1, &surface->texture_name_srgb);
1592 surface->texture_name_srgb = 0;
1596 context_release(context);
1598 resource_unload(resource);
1601 static const struct wined3d_resource_ops surface_resource_ops =
1606 static const struct wined3d_surface_ops surface_ops =
1608 surface_private_setup,
1610 surface_realize_palette,
1611 surface_draw_overlay,
1622 /*****************************************************************************
1623 * Initializes the GDI surface, aka creates the DIB section we render to
1624 * The DIB section creation is done by calling GetDC, which will create the
1625 * section and releasing the dc to allow the app to use it. The dib section
1626 * will stay until the surface is released
1628 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1629 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1630 * avoid confusion in the shared surface code.
1633 * WINED3D_OK on success
1634 * The return values of called methods on failure
1636 *****************************************************************************/
1637 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1641 TRACE("surface %p.\n", surface);
1643 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1645 ERR("Overlays not yet supported by GDI surfaces.\n");
1646 return WINED3DERR_INVALIDCALL;
1649 /* Sysmem textures have memory already allocated - release it,
1650 * this avoids an unnecessary memcpy. */
1651 hr = surface_create_dib_section(surface);
1654 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1655 surface->resource.heapMemory = NULL;
1656 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1659 /* We don't mind the nonpow2 stuff in GDI. */
1660 surface->pow2Width = surface->resource.width;
1661 surface->pow2Height = surface->resource.height;
1666 static void surface_gdi_cleanup(struct wined3d_surface *surface)
1668 TRACE("surface %p.\n", surface);
1670 if (surface->flags & SFLAG_DIBSECTION)
1672 /* Release the DC. */
1673 SelectObject(surface->hDC, surface->dib.holdbitmap);
1674 DeleteDC(surface->hDC);
1675 /* Release the DIB section. */
1676 DeleteObject(surface->dib.DIBsection);
1677 surface->dib.bitmap_data = NULL;
1678 surface->resource.allocatedMemory = NULL;
1681 if (surface->flags & SFLAG_USERPTR)
1682 wined3d_surface_set_mem(surface, NULL);
1683 if (surface->overlay_dest)
1684 list_remove(&surface->overlay_entry);
1686 HeapFree(GetProcessHeap(), 0, surface->palette9);
1688 resource_cleanup(&surface->resource);
1691 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1693 struct wined3d_palette *palette = surface->palette;
1695 TRACE("surface %p.\n", surface);
1697 if (!palette) return;
1699 if (surface->flags & SFLAG_DIBSECTION)
1704 TRACE("Updating the DC's palette.\n");
1706 for (i = 0; i < 256; ++i)
1708 col[i].rgbRed = palette->palents[i].peRed;
1709 col[i].rgbGreen = palette->palents[i].peGreen;
1710 col[i].rgbBlue = palette->palents[i].peBlue;
1711 col[i].rgbReserved = 0;
1713 SetDIBColorTable(surface->hDC, 0, 256, col);
1716 /* Update the image because of the palette change. Some games like e.g.
1717 * Red Alert call SetEntries a lot to implement fading. */
1718 /* Tell the swapchain to update the screen. */
1719 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1721 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1722 if (surface == swapchain->front_buffer)
1724 x11_copy_to_screen(swapchain, NULL);
1729 static HRESULT gdi_surface_draw_overlay(struct wined3d_surface *surface)
1731 FIXME("GDI surfaces can't draw overlays yet.\n");
1735 static void gdi_surface_preload(struct wined3d_surface *surface)
1737 TRACE("surface %p.\n", surface);
1739 ERR("Preloading GDI surfaces is not supported.\n");
1742 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
1744 TRACE("surface %p, rect %s, flags %#x.\n",
1745 surface, wine_dbgstr_rect(rect), flags);
1747 if (!surface->resource.allocatedMemory)
1749 /* This happens on gdi surfaces if the application set a user pointer
1750 * and resets it. Recreate the DIB section. */
1751 surface_create_dib_section(surface);
1752 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1756 static void gdi_surface_unmap(struct wined3d_surface *surface)
1758 TRACE("surface %p.\n", surface);
1760 /* Tell the swapchain to update the screen. */
1761 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1763 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1764 if (surface == swapchain->front_buffer)
1766 x11_copy_to_screen(swapchain, &surface->lockedRect);
1770 memset(&surface->lockedRect, 0, sizeof(RECT));
1773 static HRESULT gdi_surface_getdc(struct wined3d_surface *surface)
1775 WINED3DLOCKED_RECT lock;
1778 TRACE("surface %p.\n", surface);
1780 /* Should have a DIB section already. */
1781 if (!(surface->flags & SFLAG_DIBSECTION))
1783 WARN("DC not supported on this surface\n");
1784 return WINED3DERR_INVALIDCALL;
1787 /* Map the surface. */
1788 hr = wined3d_surface_map(surface, &lock, NULL, 0);
1790 ERR("Map failed, hr %#x.\n", hr);
1795 static HRESULT gdi_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override)
1797 TRACE("surface %p, override %p.\n", surface, override);
1802 static HRESULT gdi_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
1803 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
1804 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1806 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1807 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
1808 flags, fx, debug_d3dtexturefiltertype(filter));
1810 return surface_cpu_blt(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter);
1813 static HRESULT gdi_surface_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
1814 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD trans)
1816 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect %s, flags %#x.\n",
1817 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect), trans);
1819 return surface_cpu_bltfast(dst_surface, dst_x, dst_y, src_surface, src_rect, trans);
1822 static HRESULT gdi_surface_set_mem(struct wined3d_surface *surface, void *mem)
1824 TRACE("surface %p, mem %p.\n", surface, mem);
1826 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
1827 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1829 ERR("Not supported on render targets.\n");
1830 return WINED3DERR_INVALIDCALL;
1833 if (mem && mem != surface->resource.allocatedMemory)
1835 void *release = NULL;
1837 /* Do I have to copy the old surface content? */
1838 if (surface->flags & SFLAG_DIBSECTION)
1840 SelectObject(surface->hDC, surface->dib.holdbitmap);
1841 DeleteDC(surface->hDC);
1842 /* Release the DIB section. */
1843 DeleteObject(surface->dib.DIBsection);
1844 surface->dib.bitmap_data = NULL;
1845 surface->resource.allocatedMemory = NULL;
1846 surface->hDC = NULL;
1847 surface->flags &= ~SFLAG_DIBSECTION;
1849 else if (!(surface->flags & SFLAG_USERPTR))
1851 release = surface->resource.allocatedMemory;
1853 surface->resource.allocatedMemory = mem;
1854 surface->flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
1856 /* Now free the old memory, if any. */
1857 HeapFree(GetProcessHeap(), 0, release);
1859 else if (surface->flags & SFLAG_USERPTR)
1861 /* Map() and GetDC() will re-create the dib section and allocated memory. */
1862 surface->resource.allocatedMemory = NULL;
1863 surface->flags &= ~SFLAG_USERPTR;
1869 static const struct wined3d_surface_ops gdi_surface_ops =
1871 gdi_surface_private_setup,
1872 surface_gdi_cleanup,
1873 gdi_surface_realize_palette,
1874 gdi_surface_draw_overlay,
1875 gdi_surface_preload,
1881 gdi_surface_bltfast,
1882 gdi_surface_set_mem,
1885 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
1890 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
1894 name = &surface->texture_name_srgb;
1895 flag = SFLAG_INSRGBTEX;
1899 name = &surface->texture_name;
1900 flag = SFLAG_INTEXTURE;
1903 if (!*name && new_name)
1905 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
1906 * surface has no texture name yet. See if we can get rid of this. */
1907 if (surface->flags & flag)
1908 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
1909 surface_modify_location(surface, flag, FALSE);
1913 surface_force_reload(surface);
1916 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
1918 TRACE("surface %p, target %#x.\n", surface, target);
1920 if (surface->texture_target != target)
1922 if (target == GL_TEXTURE_RECTANGLE_ARB)
1924 surface->flags &= ~SFLAG_NORMCOORD;
1926 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
1928 surface->flags |= SFLAG_NORMCOORD;
1931 surface->texture_target = target;
1932 surface_force_reload(surface);
1935 /* Context activation is done by the caller. */
1936 void surface_bind(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
1938 TRACE("surface %p, gl_info %p, srgb %#x.\n", surface, gl_info, srgb);
1940 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
1942 struct wined3d_texture *texture = surface->container.u.texture;
1944 TRACE("Passing to container (%p).\n", texture);
1945 texture->texture_ops->texture_bind(texture, gl_info, srgb);
1949 if (surface->texture_level)
1951 ERR("Standalone surface %p is non-zero texture level %u.\n",
1952 surface, surface->texture_level);
1956 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
1960 if (!surface->texture_name)
1962 glGenTextures(1, &surface->texture_name);
1963 checkGLcall("glGenTextures");
1965 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
1967 glBindTexture(surface->texture_target, surface->texture_name);
1968 checkGLcall("glBindTexture");
1969 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1970 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1971 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
1972 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1973 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1974 checkGLcall("glTexParameteri");
1978 glBindTexture(surface->texture_target, surface->texture_name);
1979 checkGLcall("glBindTexture");
1986 /* This function checks if the primary render target uses the 8bit paletted format. */
1987 static BOOL primary_render_target_is_p8(struct wined3d_device *device)
1989 if (device->fb.render_targets && device->fb.render_targets[0])
1991 struct wined3d_surface *render_target = device->fb.render_targets[0];
1992 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1993 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1999 /* This call just downloads data, the caller is responsible for binding the
2000 * correct texture. */
2001 /* Context activation is done by the caller. */
2002 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2004 const struct wined3d_format *format = surface->resource.format;
2006 /* Only support read back of converted P8 surfaces. */
2007 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2009 FIXME("Readback conversion not supported for format %s.\n", debug_d3dformat(format->id));
2015 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2017 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2018 surface, surface->texture_level, format->glFormat, format->glType,
2019 surface->resource.allocatedMemory);
2021 if (surface->flags & SFLAG_PBO)
2023 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2024 checkGLcall("glBindBufferARB");
2025 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2026 checkGLcall("glGetCompressedTexImageARB");
2027 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2028 checkGLcall("glBindBufferARB");
2032 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2033 surface->texture_level, surface->resource.allocatedMemory));
2034 checkGLcall("glGetCompressedTexImageARB");
2042 GLenum gl_format = format->glFormat;
2043 GLenum gl_type = format->glType;
2047 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2048 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2050 gl_format = GL_ALPHA;
2051 gl_type = GL_UNSIGNED_BYTE;
2054 if (surface->flags & SFLAG_NONPOW2)
2056 unsigned char alignment = surface->resource.device->surface_alignment;
2057 src_pitch = format->byte_count * surface->pow2Width;
2058 dst_pitch = wined3d_surface_get_pitch(surface);
2059 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2060 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2064 mem = surface->resource.allocatedMemory;
2067 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2068 surface, surface->texture_level, gl_format, gl_type, mem);
2070 if (surface->flags & SFLAG_PBO)
2072 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2073 checkGLcall("glBindBufferARB");
2075 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2076 checkGLcall("glGetTexImage");
2078 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2079 checkGLcall("glBindBufferARB");
2083 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2084 checkGLcall("glGetTexImage");
2088 if (surface->flags & SFLAG_NONPOW2)
2090 const BYTE *src_data;
2094 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2095 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2096 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2098 * We're doing this...
2100 * instead of boxing the texture :
2101 * |<-texture width ->| -->pow2width| /\
2102 * |111111111111111111| | |
2103 * |222 Texture 222222| boxed empty | texture height
2104 * |3333 Data 33333333| | |
2105 * |444444444444444444| | \/
2106 * ----------------------------------- |
2107 * | boxed empty | boxed empty | pow2height
2109 * -----------------------------------
2112 * we're repacking the data to the expected texture width
2114 * |<-texture width ->| -->pow2width| /\
2115 * |111111111111111111222222222222222| |
2116 * |222333333333333333333444444444444| texture height
2120 * | empty | pow2height
2122 * -----------------------------------
2126 * |<-texture width ->| /\
2127 * |111111111111111111|
2128 * |222222222222222222|texture height
2129 * |333333333333333333|
2130 * |444444444444444444| \/
2131 * --------------------
2133 * this also means that any references to allocatedMemory should work with the data as if were a
2134 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2136 * internally the texture is still stored in a boxed format so any references to textureName will
2137 * get a boxed texture with width pow2width and not a texture of width resource.width.
2139 * Performance should not be an issue, because applications normally do not lock the surfaces when
2140 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2141 * and doesn't have to be re-read. */
2143 dst_data = surface->resource.allocatedMemory;
2144 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2145 for (y = 1; y < surface->resource.height; ++y)
2147 /* skip the first row */
2148 src_data += src_pitch;
2149 dst_data += dst_pitch;
2150 memcpy(dst_data, src_data, dst_pitch);
2153 HeapFree(GetProcessHeap(), 0, mem);
2157 /* Surface has now been downloaded */
2158 surface->flags |= SFLAG_INSYSMEM;
2161 /* This call just uploads data, the caller is responsible for binding the
2162 * correct texture. */
2163 /* Context activation is done by the caller. */
2164 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2165 const struct wined3d_format *format, BOOL srgb, const GLvoid *data)
2167 GLsizei width = surface->resource.width;
2168 GLsizei height = surface->resource.height;
2173 internal = format->glGammaInternal;
2175 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2177 internal = format->rtInternal;
2181 internal = format->glInternal;
2184 TRACE("surface %p, internal %#x, width %d, height %d, format %#x, type %#x, data %p.\n",
2185 surface, internal, width, height, format->glFormat, format->glType, data);
2186 TRACE("target %#x, level %u, resource size %u.\n",
2187 surface->texture_target, surface->texture_level, surface->resource.size);
2189 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2193 if (surface->flags & SFLAG_PBO)
2195 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
2196 checkGLcall("glBindBufferARB");
2198 TRACE("(%p) pbo: %#x, data: %p.\n", surface, surface->pbo, data);
2202 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2204 TRACE("Calling glCompressedTexSubImage2DARB.\n");
2206 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2207 0, 0, width, height, internal, surface->resource.size, data));
2208 checkGLcall("glCompressedTexSubImage2DARB");
2212 TRACE("Calling glTexSubImage2D.\n");
2214 glTexSubImage2D(surface->texture_target, surface->texture_level,
2215 0, 0, width, height, format->glFormat, format->glType, data);
2216 checkGLcall("glTexSubImage2D");
2219 if (surface->flags & SFLAG_PBO)
2221 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2222 checkGLcall("glBindBufferARB");
2227 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2229 struct wined3d_device *device = surface->resource.device;
2232 for (i = 0; i < device->context_count; ++i)
2234 context_surface_update(device->contexts[i], surface);
2239 /* This call just allocates the texture, the caller is responsible for binding
2240 * the correct texture. */
2241 /* Context activation is done by the caller. */
2242 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2243 const struct wined3d_format *format, BOOL srgb)
2245 BOOL enable_client_storage = FALSE;
2246 GLsizei width = surface->pow2Width;
2247 GLsizei height = surface->pow2Height;
2248 const BYTE *mem = NULL;
2253 internal = format->glGammaInternal;
2255 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2257 internal = format->rtInternal;
2261 internal = format->glInternal;
2264 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2266 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",
2267 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2268 internal, width, height, format->glFormat, format->glType);
2272 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2274 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2275 || !surface->resource.allocatedMemory)
2277 /* In some cases we want to disable client storage.
2278 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2279 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2280 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2281 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2283 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2284 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2285 surface->flags &= ~SFLAG_CLIENT;
2286 enable_client_storage = TRUE;
2290 surface->flags |= SFLAG_CLIENT;
2292 /* Point OpenGL to our allocated texture memory. Do not use
2293 * resource.allocatedMemory here because it might point into a
2294 * PBO. Instead use heapMemory, but get the alignment right. */
2295 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2296 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2300 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2302 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2303 internal, width, height, 0, surface->resource.size, mem));
2304 checkGLcall("glCompressedTexImage2DARB");
2308 glTexImage2D(surface->texture_target, surface->texture_level,
2309 internal, width, height, 0, format->glFormat, format->glType, mem);
2310 checkGLcall("glTexImage2D");
2313 if(enable_client_storage) {
2314 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2315 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2320 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2321 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2322 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2323 /* GL locking is done by the caller */
2324 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, struct wined3d_surface *rt)
2326 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2327 struct wined3d_renderbuffer_entry *entry;
2328 GLuint renderbuffer = 0;
2329 unsigned int src_width, src_height;
2330 unsigned int width, height;
2332 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2334 width = rt->pow2Width;
2335 height = rt->pow2Height;
2339 width = surface->pow2Width;
2340 height = surface->pow2Height;
2343 src_width = surface->pow2Width;
2344 src_height = surface->pow2Height;
2346 /* A depth stencil smaller than the render target is not valid */
2347 if (width > src_width || height > src_height) return;
2349 /* Remove any renderbuffer set if the sizes match */
2350 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2351 || (width == src_width && height == src_height))
2353 surface->current_renderbuffer = NULL;
2357 /* Look if we've already got a renderbuffer of the correct dimensions */
2358 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2360 if (entry->width == width && entry->height == height)
2362 renderbuffer = entry->id;
2363 surface->current_renderbuffer = entry;
2370 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2371 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2372 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2373 surface->resource.format->glInternal, width, height);
2375 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2376 entry->width = width;
2377 entry->height = height;
2378 entry->id = renderbuffer;
2379 list_add_head(&surface->renderbuffers, &entry->entry);
2381 surface->current_renderbuffer = entry;
2384 checkGLcall("set_compatible_renderbuffer");
2387 GLenum surface_get_gl_buffer(struct wined3d_surface *surface)
2389 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2391 TRACE("surface %p.\n", surface);
2393 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2395 ERR("Surface %p is not on a swapchain.\n", surface);
2399 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2401 if (swapchain->render_to_fbo)
2403 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2404 return GL_COLOR_ATTACHMENT0;
2406 TRACE("Returning GL_BACK\n");
2409 else if (surface == swapchain->front_buffer)
2411 TRACE("Returning GL_FRONT\n");
2415 FIXME("Higher back buffer, returning GL_BACK\n");
2419 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2420 void surface_add_dirty_rect(struct wined3d_surface *surface, const WINED3DBOX *dirty_rect)
2422 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2424 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2425 /* No partial locking for textures yet. */
2426 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2428 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2431 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->Left);
2432 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->Top);
2433 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->Right);
2434 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->Bottom);
2438 surface->dirtyRect.left = 0;
2439 surface->dirtyRect.top = 0;
2440 surface->dirtyRect.right = surface->resource.width;
2441 surface->dirtyRect.bottom = surface->resource.height;
2444 /* if the container is a texture then mark it dirty. */
2445 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2447 TRACE("Passing to container.\n");
2448 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2452 static BOOL surface_convert_color_to_float(struct wined3d_surface *surface,
2453 DWORD color, WINED3DCOLORVALUE *float_color)
2455 const struct wined3d_format *format = surface->resource.format;
2456 struct wined3d_device *device = surface->resource.device;
2460 case WINED3DFMT_P8_UINT:
2461 if (surface->palette)
2463 float_color->r = surface->palette->palents[color].peRed / 255.0f;
2464 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
2465 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
2469 float_color->r = 0.0f;
2470 float_color->g = 0.0f;
2471 float_color->b = 0.0f;
2473 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
2476 case WINED3DFMT_B5G6R5_UNORM:
2477 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
2478 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
2479 float_color->b = (color & 0x1f) / 31.0f;
2480 float_color->a = 1.0f;
2483 case WINED3DFMT_B8G8R8_UNORM:
2484 case WINED3DFMT_B8G8R8X8_UNORM:
2485 float_color->r = D3DCOLOR_R(color);
2486 float_color->g = D3DCOLOR_G(color);
2487 float_color->b = D3DCOLOR_B(color);
2488 float_color->a = 1.0f;
2491 case WINED3DFMT_B8G8R8A8_UNORM:
2492 float_color->r = D3DCOLOR_R(color);
2493 float_color->g = D3DCOLOR_G(color);
2494 float_color->b = D3DCOLOR_B(color);
2495 float_color->a = D3DCOLOR_A(color);
2499 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
2506 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2508 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2510 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2512 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2514 ERR("Not supported on scratch surfaces.\n");
2515 return WINED3DERR_INVALIDCALL;
2518 if (!(surface->flags & flag))
2520 TRACE("Reloading because surface is dirty\n");
2522 /* Reload if either the texture and sysmem have different ideas about the
2523 * color key, or the actual key values changed. */
2524 else if (!(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2525 || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2526 && (surface->glCKey.dwColorSpaceLowValue != surface->SrcBltCKey.dwColorSpaceLowValue
2527 || surface->glCKey.dwColorSpaceHighValue != surface->SrcBltCKey.dwColorSpaceHighValue)))
2529 TRACE("Reloading because of color keying\n");
2530 /* To perform the color key conversion we need a sysmem copy of
2531 * the surface. Make sure we have it. */
2533 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2534 /* Make sure the texture is reloaded because of the color key change,
2535 * this kills performance though :( */
2536 /* TODO: This is not necessarily needed with hw palettized texture support. */
2537 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2541 TRACE("surface is already in texture\n");
2545 /* No partial locking for textures yet. */
2546 surface_load_location(surface, flag, NULL);
2547 surface_evict_sysmem(surface);
2552 /* See also float_16_to_32() in wined3d_private.h */
2553 static inline unsigned short float_32_to_16(const float *in)
2556 float tmp = fabsf(*in);
2557 unsigned int mantissa;
2560 /* Deal with special numbers */
2566 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2568 if (tmp < powf(2, 10))
2574 } while (tmp < powf(2, 10));
2576 else if (tmp >= powf(2, 11))
2582 } while (tmp >= powf(2, 11));
2585 mantissa = (unsigned int)tmp;
2586 if (tmp - mantissa >= 0.5f)
2587 ++mantissa; /* Round to nearest, away from zero. */
2589 exp += 10; /* Normalize the mantissa. */
2590 exp += 15; /* Exponent is encoded with excess 15. */
2592 if (exp > 30) /* too big */
2594 ret = 0x7c00; /* INF */
2598 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2601 mantissa = mantissa >> 1;
2604 ret = mantissa & 0x3ff;
2608 ret = (exp << 10) | (mantissa & 0x3ff);
2611 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2615 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2619 TRACE("Surface %p, container %p of type %#x.\n",
2620 surface, surface->container.u.base, surface->container.type);
2622 switch (surface->container.type)
2624 case WINED3D_CONTAINER_TEXTURE:
2625 return wined3d_texture_incref(surface->container.u.texture);
2627 case WINED3D_CONTAINER_SWAPCHAIN:
2628 return wined3d_swapchain_incref(surface->container.u.swapchain);
2631 ERR("Unhandled container type %#x.\n", surface->container.type);
2632 case WINED3D_CONTAINER_NONE:
2636 refcount = InterlockedIncrement(&surface->resource.ref);
2637 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2642 /* Do not call while under the GL lock. */
2643 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2647 TRACE("Surface %p, container %p of type %#x.\n",
2648 surface, surface->container.u.base, surface->container.type);
2650 switch (surface->container.type)
2652 case WINED3D_CONTAINER_TEXTURE:
2653 return wined3d_texture_decref(surface->container.u.texture);
2655 case WINED3D_CONTAINER_SWAPCHAIN:
2656 return wined3d_swapchain_decref(surface->container.u.swapchain);
2659 ERR("Unhandled container type %#x.\n", surface->container.type);
2660 case WINED3D_CONTAINER_NONE:
2664 refcount = InterlockedDecrement(&surface->resource.ref);
2665 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2669 surface->surface_ops->surface_cleanup(surface);
2670 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2672 TRACE("Destroyed surface %p.\n", surface);
2673 HeapFree(GetProcessHeap(), 0, surface);
2679 HRESULT CDECL wined3d_surface_set_private_data(struct wined3d_surface *surface,
2680 REFGUID riid, const void *data, DWORD data_size, DWORD flags)
2682 return resource_set_private_data(&surface->resource, riid, data, data_size, flags);
2685 HRESULT CDECL wined3d_surface_get_private_data(const struct wined3d_surface *surface,
2686 REFGUID guid, void *data, DWORD *data_size)
2688 return resource_get_private_data(&surface->resource, guid, data, data_size);
2691 HRESULT CDECL wined3d_surface_free_private_data(struct wined3d_surface *surface, REFGUID refguid)
2693 return resource_free_private_data(&surface->resource, refguid);
2696 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2698 return resource_set_priority(&surface->resource, priority);
2701 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2703 return resource_get_priority(&surface->resource);
2706 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2708 TRACE("surface %p.\n", surface);
2710 surface->surface_ops->surface_preload(surface);
2713 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2715 TRACE("surface %p.\n", surface);
2717 return surface->resource.parent;
2720 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2722 TRACE("surface %p.\n", surface);
2724 return &surface->resource;
2727 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2729 TRACE("surface %p, flags %#x.\n", surface, flags);
2733 case WINEDDGBS_CANBLT:
2734 case WINEDDGBS_ISBLTDONE:
2738 return WINED3DERR_INVALIDCALL;
2742 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2744 TRACE("surface %p, flags %#x.\n", surface, flags);
2746 /* XXX: DDERR_INVALIDSURFACETYPE */
2750 case WINEDDGFS_CANFLIP:
2751 case WINEDDGFS_ISFLIPDONE:
2755 return WINED3DERR_INVALIDCALL;
2759 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2761 TRACE("surface %p.\n", surface);
2763 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2764 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2767 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2769 TRACE("surface %p.\n", surface);
2771 /* So far we don't lose anything :) */
2772 surface->flags &= ~SFLAG_LOST;
2776 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2778 TRACE("surface %p, palette %p.\n", surface, palette);
2780 if (surface->palette == palette)
2782 TRACE("Nop palette change.\n");
2786 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2787 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2789 surface->palette = palette;
2793 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2794 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
2796 surface->surface_ops->surface_realize_palette(surface);
2802 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
2803 DWORD flags, const WINEDDCOLORKEY *color_key)
2805 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
2807 if (flags & WINEDDCKEY_COLORSPACE)
2809 FIXME(" colorkey value not supported (%08x) !\n", flags);
2810 return WINED3DERR_INVALIDCALL;
2813 /* Dirtify the surface, but only if a key was changed. */
2816 switch (flags & ~WINEDDCKEY_COLORSPACE)
2818 case WINEDDCKEY_DESTBLT:
2819 surface->DestBltCKey = *color_key;
2820 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
2823 case WINEDDCKEY_DESTOVERLAY:
2824 surface->DestOverlayCKey = *color_key;
2825 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
2828 case WINEDDCKEY_SRCOVERLAY:
2829 surface->SrcOverlayCKey = *color_key;
2830 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
2833 case WINEDDCKEY_SRCBLT:
2834 surface->SrcBltCKey = *color_key;
2835 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
2841 switch (flags & ~WINEDDCKEY_COLORSPACE)
2843 case WINEDDCKEY_DESTBLT:
2844 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
2847 case WINEDDCKEY_DESTOVERLAY:
2848 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
2851 case WINEDDCKEY_SRCOVERLAY:
2852 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
2855 case WINEDDCKEY_SRCBLT:
2856 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
2864 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
2866 TRACE("surface %p.\n", surface);
2868 return surface->palette;
2871 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2873 const struct wined3d_format *format = surface->resource.format;
2876 TRACE("surface %p.\n", surface);
2878 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
2880 /* Since compressed formats are block based, pitch means the amount of
2881 * bytes to the next row of block rather than the next row of pixels. */
2882 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
2883 pitch = row_block_count * format->block_byte_count;
2887 unsigned char alignment = surface->resource.device->surface_alignment;
2888 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
2889 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2892 TRACE("Returning %u.\n", pitch);
2897 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
2899 TRACE("surface %p, mem %p.\n", surface, mem);
2901 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
2903 WARN("Surface is locked or the DC is in use.\n");
2904 return WINED3DERR_INVALIDCALL;
2907 return surface->surface_ops->surface_set_mem(surface, mem);
2910 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2914 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2916 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2918 WARN("Not an overlay surface.\n");
2919 return WINEDDERR_NOTAOVERLAYSURFACE;
2922 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2923 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2924 surface->overlay_destrect.left = x;
2925 surface->overlay_destrect.top = y;
2926 surface->overlay_destrect.right = x + w;
2927 surface->overlay_destrect.bottom = y + h;
2929 surface->surface_ops->surface_draw_overlay(surface);
2934 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2936 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2938 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2940 TRACE("Not an overlay surface.\n");
2941 return WINEDDERR_NOTAOVERLAYSURFACE;
2944 if (!surface->overlay_dest)
2946 TRACE("Overlay not visible.\n");
2949 return WINEDDERR_OVERLAYNOTVISIBLE;
2952 *x = surface->overlay_destrect.left;
2953 *y = surface->overlay_destrect.top;
2955 TRACE("Returning position %d, %d.\n", *x, *y);
2960 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2961 DWORD flags, struct wined3d_surface *ref)
2963 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2965 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2967 TRACE("Not an overlay surface.\n");
2968 return WINEDDERR_NOTAOVERLAYSURFACE;
2974 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2975 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2977 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2978 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2980 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2982 WARN("Not an overlay surface.\n");
2983 return WINEDDERR_NOTAOVERLAYSURFACE;
2985 else if (!dst_surface)
2987 WARN("Dest surface is NULL.\n");
2988 return WINED3DERR_INVALIDCALL;
2993 surface->overlay_srcrect = *src_rect;
2997 surface->overlay_srcrect.left = 0;
2998 surface->overlay_srcrect.top = 0;
2999 surface->overlay_srcrect.right = surface->resource.width;
3000 surface->overlay_srcrect.bottom = surface->resource.height;
3005 surface->overlay_destrect = *dst_rect;
3009 surface->overlay_destrect.left = 0;
3010 surface->overlay_destrect.top = 0;
3011 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3012 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3015 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3017 list_remove(&surface->overlay_entry);
3020 if (flags & WINEDDOVER_SHOW)
3022 if (surface->overlay_dest != dst_surface)
3024 surface->overlay_dest = dst_surface;
3025 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3028 else if (flags & WINEDDOVER_HIDE)
3030 /* tests show that the rectangles are erased on hide */
3031 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3032 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3033 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3034 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3035 surface->overlay_dest = NULL;
3038 surface->surface_ops->surface_draw_overlay(surface);
3043 HRESULT CDECL wined3d_surface_set_clipper(struct wined3d_surface *surface, struct wined3d_clipper *clipper)
3045 TRACE("surface %p, clipper %p.\n", surface, clipper);
3047 surface->clipper = clipper;
3052 struct wined3d_clipper * CDECL wined3d_surface_get_clipper(const struct wined3d_surface *surface)
3054 TRACE("surface %p.\n", surface);
3056 return surface->clipper;
3059 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3061 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3063 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3065 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3067 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3068 return WINED3DERR_INVALIDCALL;
3071 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3072 surface->pow2Width, surface->pow2Height);
3073 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3074 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
3075 surface->resource.format = format;
3077 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3078 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3079 format->glFormat, format->glInternal, format->glType);
3084 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3085 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3087 unsigned short *dst_s;
3091 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3093 for (y = 0; y < h; ++y)
3095 src_f = (const float *)(src + y * pitch_in);
3096 dst_s = (unsigned short *) (dst + y * pitch_out);
3097 for (x = 0; x < w; ++x)
3099 dst_s[x] = float_32_to_16(src_f + x);
3104 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3105 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3107 static const unsigned char convert_5to8[] =
3109 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3110 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3111 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3112 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3114 static const unsigned char convert_6to8[] =
3116 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3117 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3118 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3119 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3120 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3121 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3122 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3123 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3127 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3129 for (y = 0; y < h; ++y)
3131 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3132 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3133 for (x = 0; x < w; ++x)
3135 WORD pixel = src_line[x];
3136 dst_line[x] = 0xff000000
3137 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3138 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3139 | convert_5to8[(pixel & 0x001f)];
3144 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3145 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3149 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3151 for (y = 0; y < h; ++y)
3153 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3154 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3156 for (x = 0; x < w; ++x)
3158 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3163 static inline BYTE cliptobyte(int x)
3165 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3168 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3169 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3171 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3174 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3176 for (y = 0; y < h; ++y)
3178 const BYTE *src_line = src + y * pitch_in;
3179 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3180 for (x = 0; x < w; ++x)
3182 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3183 * C = Y - 16; D = U - 128; E = V - 128;
3184 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3185 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3186 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3187 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3188 * U and V are shared between the pixels. */
3189 if (!(x & 1)) /* For every even pixel, read new U and V. */
3191 d = (int) src_line[1] - 128;
3192 e = (int) src_line[3] - 128;
3194 g2 = - 100 * d - 208 * e + 128;
3197 c2 = 298 * ((int) src_line[0] - 16);
3198 dst_line[x] = 0xff000000
3199 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3200 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3201 | cliptobyte((c2 + b2) >> 8); /* blue */
3202 /* Scale RGB values to 0..255 range,
3203 * then clip them if still not in range (may be negative),
3204 * then shift them within DWORD if necessary. */
3210 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3211 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3214 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3216 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3218 for (y = 0; y < h; ++y)
3220 const BYTE *src_line = src + y * pitch_in;
3221 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3222 for (x = 0; x < w; ++x)
3224 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3225 * C = Y - 16; D = U - 128; E = V - 128;
3226 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3227 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3228 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3229 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3230 * U and V are shared between the pixels. */
3231 if (!(x & 1)) /* For every even pixel, read new U and V. */
3233 d = (int) src_line[1] - 128;
3234 e = (int) src_line[3] - 128;
3236 g2 = - 100 * d - 208 * e + 128;
3239 c2 = 298 * ((int) src_line[0] - 16);
3240 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3241 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3242 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3243 /* Scale RGB values to 0..255 range,
3244 * then clip them if still not in range (may be negative),
3245 * then shift them within DWORD if necessary. */
3251 struct d3dfmt_convertor_desc
3253 enum wined3d_format_id from, to;
3254 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3257 static const struct d3dfmt_convertor_desc convertors[] =
3259 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3260 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3261 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3262 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3263 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3266 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3267 enum wined3d_format_id to)
3271 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3273 if (convertors[i].from == from && convertors[i].to == to)
3274 return &convertors[i];
3280 /*****************************************************************************
3281 * surface_convert_format
3283 * Creates a duplicate of a surface in a different format. Is used by Blt to
3284 * blit between surfaces with different formats.
3287 * source: Source surface
3288 * fmt: Requested destination format
3290 *****************************************************************************/
3291 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3293 const struct d3dfmt_convertor_desc *conv;
3294 WINED3DLOCKED_RECT lock_src, lock_dst;
3295 struct wined3d_surface *ret = NULL;
3298 conv = find_convertor(source->resource.format->id, to_fmt);
3301 FIXME("Cannot find a conversion function from format %s to %s.\n",
3302 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3306 wined3d_surface_create(source->resource.device, source->resource.width,
3307 source->resource.height, to_fmt, TRUE /* lockable */, TRUE /* discard */, 0 /* level */,
3308 0 /* usage */, WINED3DPOOL_SCRATCH, WINED3DMULTISAMPLE_NONE /* TODO: Multisampled conversion */,
3309 0 /* MultiSampleQuality */, source->surface_type, NULL /* parent */, &wined3d_null_parent_ops, &ret);
3312 ERR("Failed to create a destination surface for conversion.\n");
3316 memset(&lock_src, 0, sizeof(lock_src));
3317 memset(&lock_dst, 0, sizeof(lock_dst));
3319 hr = wined3d_surface_map(source, &lock_src, NULL, WINED3DLOCK_READONLY);
3322 ERR("Failed to lock the source surface.\n");
3323 wined3d_surface_decref(ret);
3326 hr = wined3d_surface_map(ret, &lock_dst, NULL, WINED3DLOCK_READONLY);
3329 ERR("Failed to lock the destination surface.\n");
3330 wined3d_surface_unmap(source);
3331 wined3d_surface_decref(ret);
3335 conv->convert(lock_src.pBits, lock_dst.pBits, lock_src.Pitch, lock_dst.Pitch,
3336 source->resource.width, source->resource.height);
3338 wined3d_surface_unmap(ret);
3339 wined3d_surface_unmap(source);
3344 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3345 unsigned int bpp, UINT pitch, DWORD color)
3352 #define COLORFILL_ROW(type) \
3354 type *d = (type *)buf; \
3355 for (x = 0; x < width; ++x) \
3356 d[x] = (type)color; \
3362 COLORFILL_ROW(BYTE);
3366 COLORFILL_ROW(WORD);
3372 for (x = 0; x < width; ++x, d += 3)
3374 d[0] = (color ) & 0xFF;
3375 d[1] = (color >> 8) & 0xFF;
3376 d[2] = (color >> 16) & 0xFF;
3381 COLORFILL_ROW(DWORD);
3385 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3386 return WINED3DERR_NOTAVAILABLE;
3389 #undef COLORFILL_ROW
3391 /* Now copy first row. */
3393 for (y = 1; y < height; ++y)
3396 memcpy(buf, first, width * bpp);
3402 /* Do not call while under the GL lock. */
3403 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3404 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
3405 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
3407 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
3408 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3409 flags, fx, debug_d3dtexturefiltertype(filter));
3411 return dst_surface->surface_ops->surface_blt(dst_surface,
3412 dst_rect, src_surface, src_rect, flags, fx, filter);
3415 /* Do not call while under the GL lock. */
3416 HRESULT CDECL wined3d_surface_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
3417 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD trans)
3419 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect %s, trans %#x.\n",
3420 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect), trans);
3422 return dst_surface->surface_ops->surface_bltfast(dst_surface,
3423 dst_x, dst_y, src_surface, src_rect, trans);
3426 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3428 TRACE("surface %p.\n", surface);
3430 if (!(surface->flags & SFLAG_LOCKED))
3432 WARN("Trying to unmap unmapped surface.\n");
3433 return WINEDDERR_NOTLOCKED;
3435 surface->flags &= ~SFLAG_LOCKED;
3437 surface->surface_ops->surface_unmap(surface);
3442 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3443 WINED3DLOCKED_RECT *locked_rect, const RECT *rect, DWORD flags)
3445 TRACE("surface %p, locked_rect %p, rect %s, flags %#x.\n",
3446 surface, locked_rect, wine_dbgstr_rect(rect), flags);
3448 if (surface->flags & SFLAG_LOCKED)
3450 WARN("Surface is already mapped.\n");
3451 return WINED3DERR_INVALIDCALL;
3453 surface->flags |= SFLAG_LOCKED;
3455 if (!(surface->flags & SFLAG_LOCKABLE))
3456 WARN("Trying to lock unlockable surface.\n");
3458 surface->surface_ops->surface_map(surface, rect, flags);
3460 locked_rect->Pitch = wined3d_surface_get_pitch(surface);
3464 locked_rect->pBits = surface->resource.allocatedMemory;
3465 surface->lockedRect.left = 0;
3466 surface->lockedRect.top = 0;
3467 surface->lockedRect.right = surface->resource.width;
3468 surface->lockedRect.bottom = surface->resource.height;
3472 const struct wined3d_format *format = surface->resource.format;
3474 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3476 /* Compressed textures are block based, so calculate the offset of
3477 * the block that contains the top-left pixel of the locked rectangle. */
3478 locked_rect->pBits = surface->resource.allocatedMemory
3479 + ((rect->top / format->block_height) * locked_rect->Pitch)
3480 + ((rect->left / format->block_width) * format->block_byte_count);
3484 locked_rect->pBits = surface->resource.allocatedMemory
3485 + (locked_rect->Pitch * rect->top)
3486 + (rect->left * format->byte_count);
3488 surface->lockedRect.left = rect->left;
3489 surface->lockedRect.top = rect->top;
3490 surface->lockedRect.right = rect->right;
3491 surface->lockedRect.bottom = rect->bottom;
3494 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3495 TRACE("Returning memory %p, pitch %u.\n", locked_rect->pBits, locked_rect->Pitch);
3500 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3504 TRACE("surface %p, dc %p.\n", surface, dc);
3506 if (surface->flags & SFLAG_USERPTR)
3508 ERR("Not supported on surfaces with application-provided memory.\n");
3509 return WINEDDERR_NODC;
3512 /* Give more detailed info for ddraw. */
3513 if (surface->flags & SFLAG_DCINUSE)
3514 return WINEDDERR_DCALREADYCREATED;
3516 /* Can't GetDC if the surface is locked. */
3517 if (surface->flags & SFLAG_LOCKED)
3518 return WINED3DERR_INVALIDCALL;
3520 hr = surface->surface_ops->surface_getdc(surface);
3524 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3525 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3527 /* GetDC on palettized formats is unsupported in D3D9, and the method
3528 * is missing in D3D8, so this should only be used for DX <=7
3529 * surfaces (with non-device palettes). */
3530 const PALETTEENTRY *pal = NULL;
3532 if (surface->palette)
3534 pal = surface->palette->palents;
3538 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3539 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3541 if (dds_primary && dds_primary->palette)
3542 pal = dds_primary->palette->palents;
3550 for (i = 0; i < 256; ++i)
3552 col[i].rgbRed = pal[i].peRed;
3553 col[i].rgbGreen = pal[i].peGreen;
3554 col[i].rgbBlue = pal[i].peBlue;
3555 col[i].rgbReserved = 0;
3557 SetDIBColorTable(surface->hDC, 0, 256, col);
3561 surface->flags |= SFLAG_DCINUSE;
3564 TRACE("Returning dc %p.\n", *dc);
3569 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3571 TRACE("surface %p, dc %p.\n", surface, dc);
3573 if (!(surface->flags & SFLAG_DCINUSE))
3574 return WINEDDERR_NODC;
3576 if (surface->hDC != dc)
3578 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3580 return WINEDDERR_NODC;
3583 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3585 /* Copy the contents of the DIB over to the PBO. */
3586 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->dib.bitmap_size);
3589 /* We locked first, so unlock now. */
3590 wined3d_surface_unmap(surface);
3592 surface->flags &= ~SFLAG_DCINUSE;
3597 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3599 struct wined3d_swapchain *swapchain;
3602 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3604 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
3606 ERR("Flipped surface is not on a swapchain.\n");
3607 return WINEDDERR_NOTFLIPPABLE;
3609 swapchain = surface->container.u.swapchain;
3611 hr = surface->surface_ops->surface_flip(surface, override);
3615 /* Just overwrite the swapchain presentation interval. This is ok because
3616 * only ddraw apps can call Flip, and only d3d8 and d3d9 applications
3617 * specify the presentation interval. */
3618 if (!(flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)))
3619 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
3620 else if (flags & WINEDDFLIP_NOVSYNC)
3621 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3622 else if (flags & WINEDDFLIP_INTERVAL2)
3623 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
3624 else if (flags & WINEDDFLIP_INTERVAL3)
3625 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
3627 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3629 return wined3d_swapchain_present(swapchain, NULL, NULL, swapchain->win_handle, NULL, 0);
3632 /* Do not call while under the GL lock. */
3633 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3635 struct wined3d_device *device = surface->resource.device;
3637 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3639 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3641 struct wined3d_texture *texture = surface->container.u.texture;
3643 TRACE("Passing to container (%p).\n", texture);
3644 texture->texture_ops->texture_preload(texture, srgb);
3648 struct wined3d_context *context = NULL;
3650 TRACE("(%p) : About to load surface\n", surface);
3652 if (!device->isInDraw) context = context_acquire(device, NULL);
3654 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3655 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3657 if (palette9_changed(surface))
3659 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
3660 /* TODO: This is not necessarily needed with hw palettized texture support */
3661 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3662 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
3663 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
3667 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3669 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3671 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3675 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3679 if (context) context_release(context);
3683 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3685 if (!surface->resource.allocatedMemory)
3687 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3688 surface->resource.size + RESOURCE_ALIGNMENT);
3689 if (!surface->resource.heapMemory)
3691 ERR("Out of memory\n");
3694 surface->resource.allocatedMemory =
3695 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
3699 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
3702 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3707 /* Read the framebuffer back into the surface */
3708 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
3710 struct wined3d_device *device = surface->resource.device;
3711 const struct wined3d_gl_info *gl_info;
3712 struct wined3d_context *context;
3716 BYTE *row, *top, *bottom;
3720 BOOL srcIsUpsideDown;
3725 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
3726 static BOOL warned = FALSE;
3728 ERR("The application tries to lock the render target, but render target locking is disabled\n");
3734 context = context_acquire(device, surface);
3735 context_apply_blit_state(context, device);
3736 gl_info = context->gl_info;
3740 /* Select the correct read buffer, and give some debug output.
3741 * There is no need to keep track of the current read buffer or reset it, every part of the code
3742 * that reads sets the read buffer as desired.
3744 if (surface_is_offscreen(surface))
3746 /* Mapping the primary render target which is not on a swapchain.
3747 * Read from the back buffer. */
3748 TRACE("Mapping offscreen render target.\n");
3749 glReadBuffer(device->offscreenBuffer);
3750 srcIsUpsideDown = TRUE;
3754 /* Onscreen surfaces are always part of a swapchain */
3755 GLenum buffer = surface_get_gl_buffer(surface);
3756 TRACE("Mapping %#x buffer.\n", buffer);
3757 glReadBuffer(buffer);
3758 checkGLcall("glReadBuffer");
3759 srcIsUpsideDown = FALSE;
3762 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
3765 local_rect.left = 0;
3767 local_rect.right = surface->resource.width;
3768 local_rect.bottom = surface->resource.height;
3774 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
3776 switch (surface->resource.format->id)
3778 case WINED3DFMT_P8_UINT:
3780 if (primary_render_target_is_p8(device))
3782 /* In case of P8 render targets the index is stored in the alpha component */
3784 type = GL_UNSIGNED_BYTE;
3786 bpp = surface->resource.format->byte_count;
3790 /* GL can't return palettized data, so read ARGB pixels into a
3791 * separate block of memory and convert them into palettized format
3792 * in software. Slow, but if the app means to use palettized render
3793 * targets and locks it...
3795 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
3796 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
3797 * for the color channels when palettizing the colors.
3800 type = GL_UNSIGNED_BYTE;
3802 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
3805 ERR("Out of memory\n");
3809 bpp = surface->resource.format->byte_count * 3;
3816 fmt = surface->resource.format->glFormat;
3817 type = surface->resource.format->glType;
3818 bpp = surface->resource.format->byte_count;
3821 if (surface->flags & SFLAG_PBO)
3823 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
3824 checkGLcall("glBindBufferARB");
3827 ERR("mem not null for pbo -- unexpected\n");
3832 /* Save old pixel store pack state */
3833 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
3834 checkGLcall("glGetIntegerv");
3835 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
3836 checkGLcall("glGetIntegerv");
3837 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
3838 checkGLcall("glGetIntegerv");
3840 /* Setup pixel store pack state -- to glReadPixels into the correct place */
3841 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
3842 checkGLcall("glPixelStorei");
3843 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
3844 checkGLcall("glPixelStorei");
3845 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
3846 checkGLcall("glPixelStorei");
3848 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
3849 local_rect.right - local_rect.left,
3850 local_rect.bottom - local_rect.top,
3852 checkGLcall("glReadPixels");
3854 /* Reset previous pixel store pack state */
3855 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
3856 checkGLcall("glPixelStorei");
3857 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
3858 checkGLcall("glPixelStorei");
3859 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
3860 checkGLcall("glPixelStorei");
3862 if (surface->flags & SFLAG_PBO)
3864 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
3865 checkGLcall("glBindBufferARB");
3867 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
3868 * to get a pointer to it and perform the flipping in software. This is a lot
3869 * faster than calling glReadPixels for each line. In case we want more speed
3870 * we should rerender it flipped in a FBO and read the data back from the FBO. */
3871 if (!srcIsUpsideDown)
3873 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
3874 checkGLcall("glBindBufferARB");
3876 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3877 checkGLcall("glMapBufferARB");
3881 /* TODO: Merge this with the palettization loop below for P8 targets */
3882 if(!srcIsUpsideDown) {
3884 /* glReadPixels returns the image upside down, and there is no way to prevent this.
3885 Flip the lines in software */
3886 len = (local_rect.right - local_rect.left) * bpp;
3887 off = local_rect.left * bpp;
3889 row = HeapAlloc(GetProcessHeap(), 0, len);
3891 ERR("Out of memory\n");
3892 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
3893 HeapFree(GetProcessHeap(), 0, mem);
3898 top = mem + pitch * local_rect.top;
3899 bottom = mem + pitch * (local_rect.bottom - 1);
3900 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
3901 memcpy(row, top + off, len);
3902 memcpy(top + off, bottom + off, len);
3903 memcpy(bottom + off, row, len);
3907 HeapFree(GetProcessHeap(), 0, row);
3909 /* Unmap the temp PBO buffer */
3910 if (surface->flags & SFLAG_PBO)
3912 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
3913 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
3918 context_release(context);
3920 /* For P8 textures we need to perform an inverse palette lookup. This is
3921 * done by searching for a palette index which matches the RGB value.
3922 * Note this isn't guaranteed to work when there are multiple entries for
3923 * the same color but we have no choice. In case of P8 render targets,
3924 * the index is stored in the alpha component so no conversion is needed. */
3925 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
3927 const PALETTEENTRY *pal = NULL;
3928 DWORD width = pitch / 3;
3931 if (surface->palette)
3933 pal = surface->palette->palents;
3937 ERR("Palette is missing, cannot perform inverse palette lookup\n");
3938 HeapFree(GetProcessHeap(), 0, mem);
3942 for(y = local_rect.top; y < local_rect.bottom; y++) {
3943 for(x = local_rect.left; x < local_rect.right; x++) {
3944 /* start lines pixels */
3945 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
3946 const BYTE *green = blue + 1;
3947 const BYTE *red = green + 1;
3949 for(c = 0; c < 256; c++) {
3950 if(*red == pal[c].peRed &&
3951 *green == pal[c].peGreen &&
3952 *blue == pal[c].peBlue)
3954 *((BYTE *) dest + y * width + x) = c;
3960 HeapFree(GetProcessHeap(), 0, mem);
3964 /* Read the framebuffer contents into a texture */
3965 static void read_from_framebuffer_texture(struct wined3d_surface *surface, BOOL srgb)
3967 struct wined3d_device *device = surface->resource.device;
3968 const struct wined3d_gl_info *gl_info;
3969 struct wined3d_context *context;
3971 if (!surface_is_offscreen(surface))
3973 /* We would need to flip onscreen surfaces, but there's no efficient
3974 * way to do that here. It makes more sense for the caller to
3975 * explicitly go through sysmem. */
3976 ERR("Not supported for onscreen targets.\n");
3980 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
3981 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
3982 * states in the stateblock, and no driver was found yet that had bugs in that regard.
3984 context = context_acquire(device, surface);
3985 gl_info = context->gl_info;
3987 surface_prepare_texture(surface, gl_info, srgb);
3988 surface_bind_and_dirtify(surface, gl_info, srgb);
3990 TRACE("Reading back offscreen render target %p.\n", surface);
3994 glReadBuffer(device->offscreenBuffer);
3995 checkGLcall("glReadBuffer");
3997 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
3998 0, 0, 0, 0, surface->resource.width, surface->resource.height);
3999 checkGLcall("glCopyTexSubImage2D");
4003 context_release(context);
4006 /* Context activation is done by the caller. */
4007 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4008 const struct wined3d_gl_info *gl_info, BOOL srgb)
4010 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4011 CONVERT_TYPES convert;
4012 struct wined3d_format format;
4014 if (surface->flags & alloc_flag) return;
4016 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4017 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4018 else surface->flags &= ~SFLAG_CONVERTED;
4020 surface_bind_and_dirtify(surface, gl_info, srgb);
4021 surface_allocate_surface(surface, gl_info, &format, srgb);
4022 surface->flags |= alloc_flag;
4025 /* Context activation is done by the caller. */
4026 void surface_prepare_texture(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
4028 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4030 struct wined3d_texture *texture = surface->container.u.texture;
4031 UINT sub_count = texture->level_count * texture->layer_count;
4034 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4036 for (i = 0; i < sub_count; ++i)
4038 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4039 surface_prepare_texture_internal(s, gl_info, srgb);
4045 surface_prepare_texture_internal(surface, gl_info, srgb);
4048 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4049 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4051 struct wined3d_device *device = surface->resource.device;
4052 UINT pitch = wined3d_surface_get_pitch(surface);
4053 const struct wined3d_gl_info *gl_info;
4054 struct wined3d_context *context;
4058 surface_get_rect(surface, rect, &local_rect);
4060 mem += local_rect.top * pitch + local_rect.left * bpp;
4061 w = local_rect.right - local_rect.left;
4062 h = local_rect.bottom - local_rect.top;
4064 /* Activate the correct context for the render target */
4065 context = context_acquire(device, surface);
4066 context_apply_blit_state(context, device);
4067 gl_info = context->gl_info;
4071 if (!surface_is_offscreen(surface))
4073 GLenum buffer = surface_get_gl_buffer(surface);
4074 TRACE("Unlocking %#x buffer.\n", buffer);
4075 context_set_draw_buffer(context, buffer);
4077 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4078 glPixelZoom(1.0f, -1.0f);
4082 /* Primary offscreen render target */
4083 TRACE("Offscreen render target.\n");
4084 context_set_draw_buffer(context, device->offscreenBuffer);
4086 glPixelZoom(1.0f, 1.0f);
4089 glRasterPos3i(local_rect.left, local_rect.top, 1);
4090 checkGLcall("glRasterPos3i");
4092 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4093 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4095 if (surface->flags & SFLAG_PBO)
4097 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4098 checkGLcall("glBindBufferARB");
4101 glDrawPixels(w, h, fmt, type, mem);
4102 checkGLcall("glDrawPixels");
4104 if (surface->flags & SFLAG_PBO)
4106 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4107 checkGLcall("glBindBufferARB");
4110 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4111 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4115 if (wined3d_settings.strict_draw_ordering
4116 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4117 && surface->container.u.swapchain->front_buffer == surface))
4120 context_release(context);
4123 HRESULT d3dfmt_get_conv(struct wined3d_surface *surface, BOOL need_alpha_ck,
4124 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4126 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4127 struct wined3d_device *device = surface->resource.device;
4128 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4129 BOOL blit_supported = FALSE;
4131 /* Copy the default values from the surface. Below we might perform fixups */
4132 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4133 *format = *surface->resource.format;
4134 *convert = NO_CONVERSION;
4136 /* Ok, now look if we have to do any conversion */
4137 switch (surface->resource.format->id)
4139 case WINED3DFMT_P8_UINT:
4140 /* Below the call to blit_supported is disabled for Wine 1.2
4141 * because the function isn't operating correctly yet. At the
4142 * moment 8-bit blits are handled in software and if certain GL
4143 * extensions are around, surface conversion is performed at
4144 * upload time. The blit_supported call recognizes it as a
4145 * destination fixup. This type of upload 'fixup' and 8-bit to
4146 * 8-bit blits need to be handled by the blit_shader.
4147 * TODO: get rid of this #if 0. */
4149 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4150 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4151 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4153 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4155 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4156 * texturing. Further also use conversion in case of color keying.
4157 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4158 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4159 * conflicts with this.
4161 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4162 || colorkey_active || !use_texturing)
4164 format->glFormat = GL_RGBA;
4165 format->glInternal = GL_RGBA;
4166 format->glType = GL_UNSIGNED_BYTE;
4167 format->conv_byte_count = 4;
4168 if (colorkey_active)
4169 *convert = CONVERT_PALETTED_CK;
4171 *convert = CONVERT_PALETTED;
4175 case WINED3DFMT_B2G3R3_UNORM:
4176 /* **********************
4177 GL_UNSIGNED_BYTE_3_3_2
4178 ********************** */
4179 if (colorkey_active) {
4180 /* This texture format will never be used.. So do not care about color keying
4181 up until the point in time it will be needed :-) */
4182 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4186 case WINED3DFMT_B5G6R5_UNORM:
4187 if (colorkey_active)
4189 *convert = CONVERT_CK_565;
4190 format->glFormat = GL_RGBA;
4191 format->glInternal = GL_RGB5_A1;
4192 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4193 format->conv_byte_count = 2;
4197 case WINED3DFMT_B5G5R5X1_UNORM:
4198 if (colorkey_active)
4200 *convert = CONVERT_CK_5551;
4201 format->glFormat = GL_BGRA;
4202 format->glInternal = GL_RGB5_A1;
4203 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4204 format->conv_byte_count = 2;
4208 case WINED3DFMT_B8G8R8_UNORM:
4209 if (colorkey_active)
4211 *convert = CONVERT_CK_RGB24;
4212 format->glFormat = GL_RGBA;
4213 format->glInternal = GL_RGBA8;
4214 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4215 format->conv_byte_count = 4;
4219 case WINED3DFMT_B8G8R8X8_UNORM:
4220 if (colorkey_active)
4222 *convert = CONVERT_RGB32_888;
4223 format->glFormat = GL_RGBA;
4224 format->glInternal = GL_RGBA8;
4225 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4226 format->conv_byte_count = 4;
4237 void d3dfmt_p8_init_palette(struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4239 struct wined3d_device *device = surface->resource.device;
4240 struct wined3d_palette *pal = surface->palette;
4241 BOOL index_in_alpha = FALSE;
4244 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4245 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4246 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4247 * duplicate entries. Store the color key in the unused alpha component to speed the
4248 * download up and to make conversion unneeded. */
4249 index_in_alpha = primary_render_target_is_p8(device);
4253 UINT dxVersion = device->wined3d->dxVersion;
4255 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
4258 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4261 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4262 * there's no palette at this time. */
4263 for (i = 0; i < 256; i++) table[i][3] = i;
4268 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
4269 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
4270 * capability flag is present (wine does advertise this capability) */
4271 for (i = 0; i < 256; ++i)
4273 table[i][0] = device->palettes[device->currentPalette][i].peRed;
4274 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
4275 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
4276 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
4282 TRACE("Using surface palette %p\n", pal);
4283 /* Get the surface's palette */
4284 for (i = 0; i < 256; ++i)
4286 table[i][0] = pal->palents[i].peRed;
4287 table[i][1] = pal->palents[i].peGreen;
4288 table[i][2] = pal->palents[i].peBlue;
4290 /* When index_in_alpha is set the palette index is stored in the
4291 * alpha component. In case of a readback we can then read
4292 * GL_ALPHA. Color keying is handled in BltOverride using a
4293 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4294 * color key itself is passed to glAlphaFunc in other cases the
4295 * alpha component of pixels that should be masked away is set to 0. */
4300 else if (colorkey && (i >= surface->SrcBltCKey.dwColorSpaceLowValue)
4301 && (i <= surface->SrcBltCKey.dwColorSpaceHighValue))
4305 else if (pal->flags & WINEDDPCAPS_ALPHA)
4307 table[i][3] = pal->palents[i].peFlags;
4317 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4318 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4322 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4327 memcpy(dst, src, pitch * height);
4330 case CONVERT_PALETTED:
4331 case CONVERT_PALETTED_CK:
4336 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4338 for (y = 0; y < height; y++)
4340 source = src + pitch * y;
4341 dest = dst + outpitch * y;
4342 /* This is an 1 bpp format, using the width here is fine */
4343 for (x = 0; x < width; x++) {
4344 BYTE color = *source++;
4345 *dest++ = table[color][0];
4346 *dest++ = table[color][1];
4347 *dest++ = table[color][2];
4348 *dest++ = table[color][3];
4354 case CONVERT_CK_565:
4356 /* Converting the 565 format in 5551 packed to emulate color-keying.
4358 Note : in all these conversion, it would be best to average the averaging
4359 pixels to get the color of the pixel that will be color-keyed to
4360 prevent 'color bleeding'. This will be done later on if ever it is
4363 Note2: Nvidia documents say that their driver does not support alpha + color keying
4364 on the same surface and disables color keying in such a case
4370 TRACE("Color keyed 565\n");
4372 for (y = 0; y < height; y++) {
4373 Source = (const WORD *)(src + y * pitch);
4374 Dest = (WORD *) (dst + y * outpitch);
4375 for (x = 0; x < width; x++ ) {
4376 WORD color = *Source++;
4377 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4378 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4379 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4387 case CONVERT_CK_5551:
4389 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4393 TRACE("Color keyed 5551\n");
4394 for (y = 0; y < height; y++) {
4395 Source = (const WORD *)(src + y * pitch);
4396 Dest = (WORD *) (dst + y * outpitch);
4397 for (x = 0; x < width; x++ ) {
4398 WORD color = *Source++;
4400 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4401 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4404 *Dest &= ~(1 << 15);
4411 case CONVERT_CK_RGB24:
4413 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4415 for (y = 0; y < height; y++)
4417 source = src + pitch * y;
4418 dest = dst + outpitch * y;
4419 for (x = 0; x < width; x++) {
4420 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4421 DWORD dstcolor = color << 8;
4422 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4423 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4425 *(DWORD*)dest = dstcolor;
4433 case CONVERT_RGB32_888:
4435 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4437 for (y = 0; y < height; y++)
4439 source = src + pitch * y;
4440 dest = dst + outpitch * y;
4441 for (x = 0; x < width; x++) {
4442 DWORD color = 0xffffff & *(const DWORD*)source;
4443 DWORD dstcolor = color << 8;
4444 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4445 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4447 *(DWORD*)dest = dstcolor;
4456 ERR("Unsupported conversion type %#x.\n", convert);
4461 BOOL palette9_changed(struct wined3d_surface *surface)
4463 struct wined3d_device *device = surface->resource.device;
4465 if (surface->palette || (surface->resource.format->id != WINED3DFMT_P8_UINT
4466 && surface->resource.format->id != WINED3DFMT_P8_UINT_A8_UNORM))
4468 /* If a ddraw-style palette is attached assume no d3d9 palette change.
4469 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
4474 if (surface->palette9)
4476 if (!memcmp(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
4483 surface->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
4485 memcpy(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
4490 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4492 /* Flip the surface contents */
4497 front->hDC = back->hDC;
4501 /* Flip the DIBsection */
4504 BOOL hasDib = front->flags & SFLAG_DIBSECTION;
4505 tmp = front->dib.DIBsection;
4506 front->dib.DIBsection = back->dib.DIBsection;
4507 back->dib.DIBsection = tmp;
4509 if (back->flags & SFLAG_DIBSECTION) front->flags |= SFLAG_DIBSECTION;
4510 else front->flags &= ~SFLAG_DIBSECTION;
4511 if (hasDib) back->flags |= SFLAG_DIBSECTION;
4512 else back->flags &= ~SFLAG_DIBSECTION;
4515 /* Flip the surface data */
4519 tmp = front->dib.bitmap_data;
4520 front->dib.bitmap_data = back->dib.bitmap_data;
4521 back->dib.bitmap_data = tmp;
4523 tmp = front->resource.allocatedMemory;
4524 front->resource.allocatedMemory = back->resource.allocatedMemory;
4525 back->resource.allocatedMemory = tmp;
4527 tmp = front->resource.heapMemory;
4528 front->resource.heapMemory = back->resource.heapMemory;
4529 back->resource.heapMemory = tmp;
4534 GLuint tmp_pbo = front->pbo;
4535 front->pbo = back->pbo;
4536 back->pbo = tmp_pbo;
4539 /* client_memory should not be different, but just in case */
4542 tmp = front->dib.client_memory;
4543 front->dib.client_memory = back->dib.client_memory;
4544 back->dib.client_memory = tmp;
4547 /* Flip the opengl texture */
4551 tmp = back->texture_name;
4552 back->texture_name = front->texture_name;
4553 front->texture_name = tmp;
4555 tmp = back->texture_name_srgb;
4556 back->texture_name_srgb = front->texture_name_srgb;
4557 front->texture_name_srgb = tmp;
4559 resource_unload(&back->resource);
4560 resource_unload(&front->resource);
4564 DWORD tmp_flags = back->flags;
4565 back->flags = front->flags;
4566 front->flags = tmp_flags;
4570 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4571 * pixel copy calls. */
4572 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4573 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4575 struct wined3d_device *device = dst_surface->resource.device;
4578 struct wined3d_context *context;
4579 BOOL upsidedown = FALSE;
4580 RECT dst_rect = *dst_rect_in;
4582 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4583 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4585 if(dst_rect.top > dst_rect.bottom) {
4586 UINT tmp = dst_rect.bottom;
4587 dst_rect.bottom = dst_rect.top;
4592 context = context_acquire(device, src_surface);
4593 context_apply_blit_state(context, device);
4594 surface_internal_preload(dst_surface, SRGB_RGB);
4597 /* Bind the target texture */
4598 glBindTexture(dst_surface->texture_target, dst_surface->texture_name);
4599 checkGLcall("glBindTexture");
4600 if (surface_is_offscreen(src_surface))
4602 TRACE("Reading from an offscreen target\n");
4603 upsidedown = !upsidedown;
4604 glReadBuffer(device->offscreenBuffer);
4608 glReadBuffer(surface_get_gl_buffer(src_surface));
4610 checkGLcall("glReadBuffer");
4612 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4613 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4615 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4617 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4619 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4620 ERR("Texture filtering not supported in direct blit\n");
4623 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4624 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4626 ERR("Texture filtering not supported in direct blit\n");
4630 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4631 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4633 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4635 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4636 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4637 src_rect->left, src_surface->resource.height - src_rect->bottom,
4638 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4642 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4643 /* I have to process this row by row to swap the image,
4644 * otherwise it would be upside down, so stretching in y direction
4645 * doesn't cost extra time
4647 * However, stretching in x direction can be avoided if not necessary
4649 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4650 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4652 /* Well, that stuff works, but it's very slow.
4653 * find a better way instead
4657 for (col = dst_rect.left; col < dst_rect.right; ++col)
4659 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4660 dst_rect.left + col /* x offset */, row /* y offset */,
4661 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4666 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4667 dst_rect.left /* x offset */, row /* y offset */,
4668 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4672 checkGLcall("glCopyTexSubImage2D");
4675 context_release(context);
4677 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4678 * path is never entered
4680 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4683 /* Uses the hardware to stretch and flip the image */
4684 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4685 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4687 struct wined3d_device *device = dst_surface->resource.device;
4688 struct wined3d_swapchain *src_swapchain = NULL;
4689 GLuint src, backup = 0;
4690 float left, right, top, bottom; /* Texture coordinates */
4691 UINT fbwidth = src_surface->resource.width;
4692 UINT fbheight = src_surface->resource.height;
4693 struct wined3d_context *context;
4694 GLenum drawBuffer = GL_BACK;
4695 GLenum texture_target;
4696 BOOL noBackBufferBackup;
4698 BOOL upsidedown = FALSE;
4699 RECT dst_rect = *dst_rect_in;
4701 TRACE("Using hwstretch blit\n");
4702 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4703 context = context_acquire(device, src_surface);
4704 context_apply_blit_state(context, device);
4705 surface_internal_preload(dst_surface, SRGB_RGB);
4707 src_offscreen = surface_is_offscreen(src_surface);
4708 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4709 if (!noBackBufferBackup && !src_surface->texture_name)
4711 /* Get it a description */
4712 surface_internal_preload(src_surface, SRGB_RGB);
4716 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4717 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4719 if (context->aux_buffers >= 2)
4721 /* Got more than one aux buffer? Use the 2nd aux buffer */
4722 drawBuffer = GL_AUX1;
4724 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4726 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4727 drawBuffer = GL_AUX0;
4730 if(noBackBufferBackup) {
4731 glGenTextures(1, &backup);
4732 checkGLcall("glGenTextures");
4733 glBindTexture(GL_TEXTURE_2D, backup);
4734 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
4735 texture_target = GL_TEXTURE_2D;
4737 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4738 * we are reading from the back buffer, the backup can be used as source texture
4740 texture_target = src_surface->texture_target;
4741 glBindTexture(texture_target, src_surface->texture_name);
4742 checkGLcall("glBindTexture(texture_target, src_surface->texture_name)");
4743 glEnable(texture_target);
4744 checkGLcall("glEnable(texture_target)");
4746 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4747 src_surface->flags &= ~SFLAG_INTEXTURE;
4750 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4751 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4753 if(dst_rect.top > dst_rect.bottom) {
4754 UINT tmp = dst_rect.bottom;
4755 dst_rect.bottom = dst_rect.top;
4762 TRACE("Reading from an offscreen target\n");
4763 upsidedown = !upsidedown;
4764 glReadBuffer(device->offscreenBuffer);
4768 glReadBuffer(surface_get_gl_buffer(src_surface));
4771 /* TODO: Only back up the part that will be overwritten */
4772 glCopyTexSubImage2D(texture_target, 0,
4773 0, 0 /* read offsets */,
4778 checkGLcall("glCopyTexSubImage2D");
4780 /* No issue with overriding these - the sampler is dirty due to blit usage */
4781 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
4782 wined3d_gl_mag_filter(magLookup, Filter));
4783 checkGLcall("glTexParameteri");
4784 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
4785 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
4786 checkGLcall("glTexParameteri");
4788 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
4789 src_swapchain = src_surface->container.u.swapchain;
4790 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
4792 src = backup ? backup : src_surface->texture_name;
4796 glReadBuffer(GL_FRONT);
4797 checkGLcall("glReadBuffer(GL_FRONT)");
4799 glGenTextures(1, &src);
4800 checkGLcall("glGenTextures(1, &src)");
4801 glBindTexture(GL_TEXTURE_2D, src);
4802 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
4804 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
4805 * out for power of 2 sizes
4807 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
4808 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
4809 checkGLcall("glTexImage2D");
4810 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
4811 0, 0 /* read offsets */,
4816 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4817 checkGLcall("glTexParameteri");
4818 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4819 checkGLcall("glTexParameteri");
4821 glReadBuffer(GL_BACK);
4822 checkGLcall("glReadBuffer(GL_BACK)");
4824 if(texture_target != GL_TEXTURE_2D) {
4825 glDisable(texture_target);
4826 glEnable(GL_TEXTURE_2D);
4827 texture_target = GL_TEXTURE_2D;
4830 checkGLcall("glEnd and previous");
4832 left = src_rect->left;
4833 right = src_rect->right;
4837 top = src_surface->resource.height - src_rect->top;
4838 bottom = src_surface->resource.height - src_rect->bottom;
4842 top = src_surface->resource.height - src_rect->bottom;
4843 bottom = src_surface->resource.height - src_rect->top;
4846 if (src_surface->flags & SFLAG_NORMCOORD)
4848 left /= src_surface->pow2Width;
4849 right /= src_surface->pow2Width;
4850 top /= src_surface->pow2Height;
4851 bottom /= src_surface->pow2Height;
4854 /* draw the source texture stretched and upside down. The correct surface is bound already */
4855 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
4856 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
4858 context_set_draw_buffer(context, drawBuffer);
4859 glReadBuffer(drawBuffer);
4863 glTexCoord2f(left, bottom);
4867 glTexCoord2f(left, top);
4868 glVertex2i(0, dst_rect.bottom - dst_rect.top);
4871 glTexCoord2f(right, top);
4872 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4875 glTexCoord2f(right, bottom);
4876 glVertex2i(dst_rect.right - dst_rect.left, 0);
4878 checkGLcall("glEnd and previous");
4880 if (texture_target != dst_surface->texture_target)
4882 glDisable(texture_target);
4883 glEnable(dst_surface->texture_target);
4884 texture_target = dst_surface->texture_target;
4887 /* Now read the stretched and upside down image into the destination texture */
4888 glBindTexture(texture_target, dst_surface->texture_name);
4889 checkGLcall("glBindTexture");
4890 glCopyTexSubImage2D(texture_target,
4892 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
4893 0, 0, /* We blitted the image to the origin */
4894 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4895 checkGLcall("glCopyTexSubImage2D");
4897 if(drawBuffer == GL_BACK) {
4898 /* Write the back buffer backup back */
4900 if(texture_target != GL_TEXTURE_2D) {
4901 glDisable(texture_target);
4902 glEnable(GL_TEXTURE_2D);
4903 texture_target = GL_TEXTURE_2D;
4905 glBindTexture(GL_TEXTURE_2D, backup);
4906 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
4910 if (texture_target != src_surface->texture_target)
4912 glDisable(texture_target);
4913 glEnable(src_surface->texture_target);
4914 texture_target = src_surface->texture_target;
4916 glBindTexture(src_surface->texture_target, src_surface->texture_name);
4917 checkGLcall("glBindTexture(src_surface->texture_target, src_surface->texture_name)");
4922 glTexCoord2f(0.0f, 0.0f);
4923 glVertex2i(0, fbheight);
4926 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
4930 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
4931 (float)fbheight / (float)src_surface->pow2Height);
4932 glVertex2i(fbwidth, 0);
4935 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
4936 glVertex2i(fbwidth, fbheight);
4939 glDisable(texture_target);
4940 checkGLcall("glDisable(texture_target)");
4943 if (src != src_surface->texture_name && src != backup)
4945 glDeleteTextures(1, &src);
4946 checkGLcall("glDeleteTextures(1, &src)");
4949 glDeleteTextures(1, &backup);
4950 checkGLcall("glDeleteTextures(1, &backup)");
4955 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
4957 context_release(context);
4959 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4960 * path is never entered
4962 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4965 /* Front buffer coordinates are always full screen coordinates, but our GL
4966 * drawable is limited to the window's client area. The sysmem and texture
4967 * copies do have the full screen size. Note that GL has a bottom-left
4968 * origin, while D3D has a top-left origin. */
4969 void surface_translate_drawable_coords(struct wined3d_surface *surface, HWND window, RECT *rect)
4971 UINT drawable_height;
4973 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4974 && surface == surface->container.u.swapchain->front_buffer)
4976 POINT offset = {0, 0};
4979 ScreenToClient(window, &offset);
4980 OffsetRect(rect, offset.x, offset.y);
4982 GetClientRect(window, &windowsize);
4983 drawable_height = windowsize.bottom - windowsize.top;
4987 drawable_height = surface->resource.height;
4990 rect->top = drawable_height - rect->top;
4991 rect->bottom = drawable_height - rect->bottom;
4994 /* blit between surface locations. onscreen on different swapchains is not supported.
4995 * depth / stencil is not supported. */
4996 static void surface_blt_fbo(struct wined3d_device *device, const WINED3DTEXTUREFILTERTYPE filter,
4997 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
4998 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
5000 const struct wined3d_gl_info *gl_info;
5001 struct wined3d_context *context;
5002 RECT src_rect, dst_rect;
5005 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
5006 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
5007 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
5008 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
5009 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
5011 src_rect = *src_rect_in;
5012 dst_rect = *dst_rect_in;
5016 case WINED3DTEXF_LINEAR:
5017 gl_filter = GL_LINEAR;
5021 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
5022 case WINED3DTEXF_NONE:
5023 case WINED3DTEXF_POINT:
5024 gl_filter = GL_NEAREST;
5028 if (src_location == SFLAG_INDRAWABLE && surface_is_offscreen(src_surface))
5029 src_location = SFLAG_INTEXTURE;
5030 if (dst_location == SFLAG_INDRAWABLE && surface_is_offscreen(dst_surface))
5031 dst_location = SFLAG_INTEXTURE;
5033 /* Make sure the locations are up-to-date. Loading the destination
5034 * surface isn't required if the entire surface is overwritten. (And is
5035 * in fact harmful if we're being called by surface_load_location() with
5036 * the purpose of loading the destination surface.) */
5037 surface_load_location(src_surface, src_location, NULL);
5038 if (!surface_is_full_rect(dst_surface, &dst_rect))
5039 surface_load_location(dst_surface, dst_location, NULL);
5041 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
5042 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
5043 else context = context_acquire(device, NULL);
5045 if (!context->valid)
5047 context_release(context);
5048 WARN("Invalid context, skipping blit.\n");
5052 gl_info = context->gl_info;
5054 if (src_location == SFLAG_INDRAWABLE)
5056 GLenum buffer = surface_get_gl_buffer(src_surface);
5058 TRACE("Source surface %p is onscreen.\n", src_surface);
5060 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
5063 context_bind_fbo(context, GL_READ_FRAMEBUFFER, NULL);
5064 glReadBuffer(buffer);
5065 checkGLcall("glReadBuffer()");
5069 TRACE("Source surface %p is offscreen.\n", src_surface);
5071 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
5072 glReadBuffer(GL_COLOR_ATTACHMENT0);
5073 checkGLcall("glReadBuffer()");
5075 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
5078 if (dst_location == SFLAG_INDRAWABLE)
5080 GLenum buffer = surface_get_gl_buffer(dst_surface);
5082 TRACE("Destination surface %p is onscreen.\n", dst_surface);
5084 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5087 context_bind_fbo(context, GL_DRAW_FRAMEBUFFER, NULL);
5088 context_set_draw_buffer(context, buffer);
5092 TRACE("Destination surface %p is offscreen.\n", dst_surface);
5095 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
5096 context_set_draw_buffer(context, GL_COLOR_ATTACHMENT0);
5098 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
5100 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
5101 device_invalidate_state(device, STATE_RENDER(WINED3DRS_COLORWRITEENABLE));
5102 device_invalidate_state(device, STATE_RENDER(WINED3DRS_COLORWRITEENABLE1));
5103 device_invalidate_state(device, STATE_RENDER(WINED3DRS_COLORWRITEENABLE2));
5104 device_invalidate_state(device, STATE_RENDER(WINED3DRS_COLORWRITEENABLE3));
5106 glDisable(GL_SCISSOR_TEST);
5107 device_invalidate_state(device, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
5109 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
5110 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
5111 checkGLcall("glBlitFramebuffer()");
5115 if (wined3d_settings.strict_draw_ordering
5116 || (dst_location == SFLAG_INDRAWABLE
5117 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
5120 context_release(context);
5123 static void surface_blt_to_drawable(struct wined3d_device *device,
5124 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5125 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5126 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5128 struct wined3d_context *context;
5129 RECT src_rect, dst_rect;
5131 src_rect = *src_rect_in;
5132 dst_rect = *dst_rect_in;
5134 /* Make sure the surface is up-to-date. This should probably use
5135 * surface_load_location() and worry about the destination surface too,
5136 * unless we're overwriting it completely. */
5137 surface_internal_preload(src_surface, SRGB_RGB);
5139 /* Activate the destination context, set it up for blitting */
5140 context = context_acquire(device, dst_surface);
5141 context_apply_blit_state(context, device);
5143 if (!surface_is_offscreen(dst_surface))
5144 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5146 device->blitter->set_shader(device->blit_priv, context->gl_info, src_surface);
5152 glEnable(GL_ALPHA_TEST);
5153 checkGLcall("glEnable(GL_ALPHA_TEST)");
5155 /* When the primary render target uses P8, the alpha component
5156 * contains the palette index. Which means that the colorkey is one of
5157 * the palette entries. In other cases pixels that should be masked
5158 * away have alpha set to 0. */
5159 if (primary_render_target_is_p8(device))
5160 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
5162 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5163 checkGLcall("glAlphaFunc");
5167 glDisable(GL_ALPHA_TEST);
5168 checkGLcall("glDisable(GL_ALPHA_TEST)");
5171 draw_textured_quad(src_surface, &src_rect, &dst_rect, filter);
5175 glDisable(GL_ALPHA_TEST);
5176 checkGLcall("glDisable(GL_ALPHA_TEST)");
5181 /* Leave the opengl state valid for blitting */
5182 device->blitter->unset_shader(context->gl_info);
5184 if (wined3d_settings.strict_draw_ordering
5185 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5186 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5187 wglFlush(); /* Flush to ensure ordering across contexts. */
5189 context_release(context);
5192 /* Do not call while under the GL lock. */
5193 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const WINED3DCOLORVALUE *color)
5195 struct wined3d_device *device = s->resource.device;
5196 const struct blit_shader *blitter;
5198 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5199 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5202 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5203 return WINED3DERR_INVALIDCALL;
5206 return blitter->color_fill(device, s, rect, color);
5209 /* Do not call while under the GL lock. */
5210 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *DestRect,
5211 struct wined3d_surface *src_surface, const RECT *SrcRect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5212 WINED3DTEXTUREFILTERTYPE Filter)
5214 struct wined3d_device *device = dst_surface->resource.device;
5215 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5216 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5217 RECT dst_rect, src_rect;
5219 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5220 dst_surface, wine_dbgstr_rect(DestRect), src_surface, wine_dbgstr_rect(SrcRect),
5221 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5223 /* Get the swapchain. One of the surfaces has to be a primary surface */
5224 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5226 WARN("Destination is in sysmem, rejecting gl blt\n");
5227 return WINED3DERR_INVALIDCALL;
5230 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5231 dstSwapchain = dst_surface->container.u.swapchain;
5235 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5237 WARN("Src is in sysmem, rejecting gl blt\n");
5238 return WINED3DERR_INVALIDCALL;
5241 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5242 srcSwapchain = src_surface->container.u.swapchain;
5245 /* Early sort out of cases where no render target is used */
5246 if (!dstSwapchain && !srcSwapchain
5247 && src_surface != device->fb.render_targets[0]
5248 && dst_surface != device->fb.render_targets[0])
5250 TRACE("No surface is render target, not using hardware blit.\n");
5251 return WINED3DERR_INVALIDCALL;
5254 /* No destination color keying supported */
5255 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5257 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5258 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5259 return WINED3DERR_INVALIDCALL;
5262 surface_get_rect(dst_surface, DestRect, &dst_rect);
5263 if (src_surface) surface_get_rect(src_surface, SrcRect, &src_rect);
5265 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
5266 if (dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->back_buffers
5267 && dst_surface == dstSwapchain->front_buffer
5268 && src_surface == dstSwapchain->back_buffers[0])
5270 /* Half-Life does a Blt from the back buffer to the front buffer,
5271 * Full surface size, no flags... Use present instead
5273 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
5276 /* Check rects - IWineD3DDevice_Present doesn't handle them */
5279 TRACE("Looking if a Present can be done...\n");
5280 /* Source Rectangle must be full surface */
5281 if (src_rect.left || src_rect.top
5282 || src_rect.right != src_surface->resource.width
5283 || src_rect.bottom != src_surface->resource.height)
5285 TRACE("No, Source rectangle doesn't match\n");
5289 /* No stretching may occur */
5290 if(src_rect.right != dst_rect.right - dst_rect.left ||
5291 src_rect.bottom != dst_rect.bottom - dst_rect.top) {
5292 TRACE("No, stretching is done\n");
5296 /* Destination must be full surface or match the clipping rectangle */
5297 if (dst_surface->clipper && dst_surface->clipper->hWnd)
5301 GetClientRect(dst_surface->clipper->hWnd, &cliprect);
5302 pos[0].x = dst_rect.left;
5303 pos[0].y = dst_rect.top;
5304 pos[1].x = dst_rect.right;
5305 pos[1].y = dst_rect.bottom;
5306 MapWindowPoints(GetDesktopWindow(), dst_surface->clipper->hWnd, pos, 2);
5308 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
5309 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
5311 TRACE("No, dest rectangle doesn't match(clipper)\n");
5312 TRACE("Clip rect at %s\n", wine_dbgstr_rect(&cliprect));
5313 TRACE("Blt dest: %s\n", wine_dbgstr_rect(&dst_rect));
5317 else if (dst_rect.left || dst_rect.top
5318 || dst_rect.right != dst_surface->resource.width
5319 || dst_rect.bottom != dst_surface->resource.height)
5321 TRACE("No, dest rectangle doesn't match(surface size)\n");
5327 /* These flags are unimportant for the flag check, remove them */
5328 if (!(flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)))
5330 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
5332 /* The idea behind this is that a glReadPixels and a glDrawPixels call
5333 * take very long, while a flip is fast.
5334 * This applies to Half-Life, which does such Blts every time it finished
5335 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
5336 * menu. This is also used by all apps when they do windowed rendering
5338 * The problem is that flipping is not really the same as copying. After a
5339 * Blt the front buffer is a copy of the back buffer, and the back buffer is
5340 * untouched. Therefore it's necessary to override the swap effect
5341 * and to set it back after the flip.
5343 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
5347 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
5348 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
5350 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead.\n");
5351 wined3d_swapchain_present(dstSwapchain, NULL, NULL, dstSwapchain->win_handle, NULL, 0);
5353 dstSwapchain->presentParms.SwapEffect = orig_swap;
5360 TRACE("Unsupported blit between buffers on the same swapchain\n");
5361 return WINED3DERR_INVALIDCALL;
5362 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
5363 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5364 return WINED3DERR_INVALIDCALL;
5365 } else if(dstSwapchain && srcSwapchain) {
5366 FIXME("Implement hardware blit between two different swapchains\n");
5367 return WINED3DERR_INVALIDCALL;
5369 else if (dstSwapchain)
5371 /* Handled with regular texture -> swapchain blit */
5372 if (src_surface == device->fb.render_targets[0])
5373 TRACE("Blit from active render target to a swapchain\n");
5375 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5377 FIXME("Implement blit from a swapchain to the active render target\n");
5378 return WINED3DERR_INVALIDCALL;
5381 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5383 /* Blit from render target to texture */
5386 /* P8 read back is not implemented */
5387 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5388 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5390 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5391 return WINED3DERR_INVALIDCALL;
5394 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5396 TRACE("Color keying not supported by frame buffer to texture blit\n");
5397 return WINED3DERR_INVALIDCALL;
5398 /* Destination color key is checked above */
5401 if(dst_rect.right - dst_rect.left != src_rect.right - src_rect.left) {
5407 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5408 * flip the image nor scale it.
5410 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5411 * -> If the app wants a image width an unscaled width, copy it line per line
5412 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5413 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5414 * back buffer. This is slower than reading line per line, thus not used for flipping
5415 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5418 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
5419 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
5421 if (fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5422 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5423 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5425 surface_blt_fbo(device, Filter,
5426 src_surface, SFLAG_INDRAWABLE, &src_rect,
5427 dst_surface, SFLAG_INDRAWABLE, &dst_rect);
5428 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
5430 else if (!stretchx || dst_rect.right - dst_rect.left > src_surface->resource.width
5431 || dst_rect.bottom - dst_rect.top > src_surface->resource.height)
5433 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5434 fb_copy_to_texture_direct(dst_surface, src_surface, &src_rect, &dst_rect, Filter);
5436 TRACE("Using hardware stretching to flip / stretch the texture\n");
5437 fb_copy_to_texture_hwstretch(dst_surface, src_surface, &src_rect, &dst_rect, Filter);
5440 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5442 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5443 dst_surface->resource.allocatedMemory = NULL;
5444 dst_surface->resource.heapMemory = NULL;
5448 dst_surface->flags &= ~SFLAG_INSYSMEM;
5453 else if (src_surface)
5455 /* Blit from offscreen surface to render target */
5456 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5457 WINEDDCOLORKEY oldBltCKey = src_surface->SrcBltCKey;
5459 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5461 if (!(flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5462 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5463 &src_rect, src_surface->resource.usage, src_surface->resource.pool,
5464 src_surface->resource.format,
5465 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool,
5466 dst_surface->resource.format))
5468 TRACE("Using surface_blt_fbo.\n");
5469 /* The source is always a texture, but never the currently active render target, and the texture
5470 * contents are never upside down. */
5471 surface_blt_fbo(device, Filter,
5472 src_surface, SFLAG_INDRAWABLE, &src_rect,
5473 dst_surface, SFLAG_INDRAWABLE, &dst_rect);
5474 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
5478 if (!(flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5479 && arbfp_blit.blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5480 &src_rect, src_surface->resource.usage, src_surface->resource.pool,
5481 src_surface->resource.format,
5482 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool,
5483 dst_surface->resource.format))
5485 return arbfp_blit_surface(device, src_surface, &src_rect, dst_surface, &dst_rect,
5486 WINED3D_BLIT_OP_COLOR_BLIT, Filter);
5489 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5490 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5491 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5493 FIXME("Unsupported blit operation falling back to software\n");
5494 return WINED3DERR_INVALIDCALL;
5497 /* Color keying: Check if we have to do a color keyed blt,
5498 * and if not check if a color key is activated.
5500 * Just modify the color keying parameters in the surface and restore them afterwards
5501 * The surface keeps track of the color key last used to load the opengl surface.
5502 * PreLoad will catch the change to the flags and color key and reload if necessary.
5504 if (flags & WINEDDBLT_KEYSRC)
5506 /* Use color key from surface */
5508 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5510 /* Use color key from DDBltFx */
5511 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5512 src_surface->SrcBltCKey = DDBltFx->ddckSrcColorkey;
5516 /* Do not use color key */
5517 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5520 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5521 src_surface, &src_rect, dst_surface, &dst_rect);
5523 /* Restore the color key parameters */
5524 src_surface->CKeyFlags = oldCKeyFlags;
5525 src_surface->SrcBltCKey = oldBltCKey;
5527 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
5533 /* Source-Less Blit to render target */
5534 if (flags & WINEDDBLT_COLORFILL)
5536 WINED3DCOLORVALUE color;
5538 TRACE("Colorfill\n");
5540 /* The color as given in the Blt function is in the surface format. */
5541 if (!surface_convert_color_to_float(dst_surface, DDBltFx->u5.dwFillColor, &color))
5542 return WINED3DERR_INVALIDCALL;
5544 return surface_color_fill(dst_surface, &dst_rect, &color);
5548 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5549 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5550 return WINED3DERR_INVALIDCALL;
5553 /* GL locking is done by the caller */
5554 static void surface_depth_blt(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
5555 GLuint texture, GLsizei w, GLsizei h, GLenum target)
5557 struct wined3d_device *device = surface->resource.device;
5558 GLint compare_mode = GL_NONE;
5559 struct blt_info info;
5560 GLint old_binding = 0;
5563 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5565 glDisable(GL_CULL_FACE);
5566 glDisable(GL_BLEND);
5567 glDisable(GL_ALPHA_TEST);
5568 glDisable(GL_SCISSOR_TEST);
5569 glDisable(GL_STENCIL_TEST);
5570 glEnable(GL_DEPTH_TEST);
5571 glDepthFunc(GL_ALWAYS);
5572 glDepthMask(GL_TRUE);
5573 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5574 glViewport(0, surface->pow2Height - h, w, h);
5576 SetRect(&rect, 0, h, w, 0);
5577 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5578 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
5579 glGetIntegerv(info.binding, &old_binding);
5580 glBindTexture(info.bind_target, texture);
5581 if (gl_info->supported[ARB_SHADOW])
5583 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5584 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5587 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5588 gl_info, info.tex_type, &surface->ds_current_size);
5590 glBegin(GL_TRIANGLE_STRIP);
5591 glTexCoord3fv(info.coords[0]);
5592 glVertex2f(-1.0f, -1.0f);
5593 glTexCoord3fv(info.coords[1]);
5594 glVertex2f(1.0f, -1.0f);
5595 glTexCoord3fv(info.coords[2]);
5596 glVertex2f(-1.0f, 1.0f);
5597 glTexCoord3fv(info.coords[3]);
5598 glVertex2f(1.0f, 1.0f);
5601 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5602 glBindTexture(info.bind_target, old_binding);
5606 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5609 void surface_modify_ds_location(struct wined3d_surface *surface,
5610 DWORD location, UINT w, UINT h)
5612 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5614 if (location & ~SFLAG_DS_LOCATIONS)
5615 FIXME("Invalid location (%#x) specified.\n", location);
5617 surface->ds_current_size.cx = w;
5618 surface->ds_current_size.cy = h;
5619 surface->flags &= ~SFLAG_DS_LOCATIONS;
5620 surface->flags |= location;
5623 /* Context activation is done by the caller. */
5624 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5626 struct wined3d_device *device = surface->resource.device;
5627 const struct wined3d_gl_info *gl_info = context->gl_info;
5630 TRACE("surface %p, new location %#x.\n", surface, location);
5632 /* TODO: Make this work for modes other than FBO */
5633 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5635 if (!(surface->flags & location))
5637 w = surface->ds_current_size.cx;
5638 h = surface->ds_current_size.cy;
5639 surface->ds_current_size.cx = 0;
5640 surface->ds_current_size.cy = 0;
5644 w = surface->resource.width;
5645 h = surface->resource.height;
5648 if (surface->ds_current_size.cx == surface->resource.width
5649 && surface->ds_current_size.cy == surface->resource.height)
5651 TRACE("Location (%#x) is already up to date.\n", location);
5655 if (surface->current_renderbuffer)
5657 FIXME("Not supported with fixed up depth stencil.\n");
5661 if (!(surface->flags & SFLAG_DS_LOCATIONS))
5663 /* This mostly happens when a depth / stencil is used without being
5664 * cleared first. In principle we could upload from sysmem, or
5665 * explicitly clear before first usage. For the moment there don't
5666 * appear to be a lot of applications depending on this, so a FIXME
5668 FIXME("No up to date depth stencil location.\n");
5669 surface->flags |= location;
5670 surface->ds_current_size.cx = surface->resource.width;
5671 surface->ds_current_size.cy = surface->resource.height;
5675 if (location == SFLAG_DS_OFFSCREEN)
5677 GLint old_binding = 0;
5680 /* The render target is allowed to be smaller than the depth/stencil
5681 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5682 * than the offscreen surface. Don't overwrite the offscreen surface
5683 * with undefined data. */
5684 w = min(w, context->swapchain->presentParms.BackBufferWidth);
5685 h = min(h, context->swapchain->presentParms.BackBufferHeight);
5687 TRACE("Copying onscreen depth buffer to depth texture.\n");
5691 if (!device->depth_blt_texture)
5693 glGenTextures(1, &device->depth_blt_texture);
5696 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5697 * directly on the FBO texture. That's because we need to flip. */
5698 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
5699 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5701 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5702 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5706 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5707 bind_target = GL_TEXTURE_2D;
5709 glBindTexture(bind_target, device->depth_blt_texture);
5710 glCopyTexImage2D(bind_target, surface->texture_level, surface->resource.format->glInternal, 0, 0, w, h, 0);
5711 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5712 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5713 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5714 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5715 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5716 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5717 glBindTexture(bind_target, old_binding);
5719 /* Setup the destination */
5720 if (!device->depth_blt_rb)
5722 gl_info->fbo_ops.glGenRenderbuffers(1, &device->depth_blt_rb);
5723 checkGLcall("glGenRenderbuffersEXT");
5725 if (device->depth_blt_rb_w != w || device->depth_blt_rb_h != h)
5727 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, device->depth_blt_rb);
5728 checkGLcall("glBindRenderbufferEXT");
5729 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h);
5730 checkGLcall("glRenderbufferStorageEXT");
5731 device->depth_blt_rb_w = w;
5732 device->depth_blt_rb_h = h;
5735 context_bind_fbo(context, GL_FRAMEBUFFER, &context->dst_fbo);
5736 gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER,
5737 GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, device->depth_blt_rb);
5738 checkGLcall("glFramebufferRenderbufferEXT");
5739 context_attach_depth_stencil_fbo(context, GL_FRAMEBUFFER, surface, FALSE);
5741 /* Do the actual blit */
5742 surface_depth_blt(surface, gl_info, device->depth_blt_texture, w, h, bind_target);
5743 checkGLcall("depth_blt");
5745 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
5746 else context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
5750 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5752 else if (location == SFLAG_DS_ONSCREEN)
5754 TRACE("Copying depth texture to onscreen depth buffer.\n");
5758 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
5759 surface_depth_blt(surface, gl_info, surface->texture_name,
5760 w, h, surface->texture_target);
5761 checkGLcall("depth_blt");
5763 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
5767 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5771 ERR("Invalid location (%#x) specified.\n", location);
5774 surface->flags |= location;
5775 surface->ds_current_size.cx = surface->resource.width;
5776 surface->ds_current_size.cy = surface->resource.height;
5779 void surface_modify_location(struct wined3d_surface *surface, DWORD flag, BOOL persistent)
5781 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5782 struct wined3d_surface *overlay;
5784 TRACE("surface %p, location %s, persistent %#x.\n",
5785 surface, debug_surflocation(flag), persistent);
5787 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5789 if (surface_is_offscreen(surface))
5791 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
5792 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
5796 TRACE("Surface %p is an onscreen surface.\n", surface);
5800 if (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5801 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5803 flag |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5808 if (((surface->flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE))
5809 || ((surface->flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX)))
5811 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5813 TRACE("Passing to container.\n");
5814 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5817 surface->flags &= ~SFLAG_LOCATIONS;
5818 surface->flags |= flag;
5820 /* Redraw emulated overlays, if any */
5821 if (flag & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5823 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5825 overlay->surface_ops->surface_draw_overlay(overlay);
5831 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5833 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5835 TRACE("Passing to container\n");
5836 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5839 surface->flags &= ~flag;
5842 if (!(surface->flags & SFLAG_LOCATIONS))
5844 ERR("Surface %p does not have any up to date location.\n", surface);
5848 static DWORD resource_access_from_location(DWORD location)
5852 case SFLAG_INSYSMEM:
5853 return WINED3D_RESOURCE_ACCESS_CPU;
5855 case SFLAG_INDRAWABLE:
5856 case SFLAG_INSRGBTEX:
5857 case SFLAG_INTEXTURE:
5858 return WINED3D_RESOURCE_ACCESS_GPU;
5861 FIXME("Unhandled location %#x.\n", location);
5866 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD flag, const RECT *rect)
5868 struct wined3d_device *device = surface->resource.device;
5869 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5870 BOOL drawable_read_ok = surface_is_offscreen(surface);
5871 struct wined3d_format format;
5872 CONVERT_TYPES convert;
5873 int width, pitch, outpitch;
5875 BOOL in_fbo = FALSE;
5877 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(flag), wine_dbgstr_rect(rect));
5879 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5881 if (flag == SFLAG_INTEXTURE)
5883 struct wined3d_context *context = context_acquire(device, NULL);
5884 surface_load_ds_location(surface, context, SFLAG_DS_OFFSCREEN);
5885 context_release(context);
5890 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(flag));
5891 return WINED3DERR_INVALIDCALL;
5895 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5897 if (surface_is_offscreen(surface))
5899 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
5900 * Prefer SFLAG_INTEXTURE. */
5901 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
5902 drawable_read_ok = FALSE;
5907 TRACE("Surface %p is an onscreen surface.\n", surface);
5911 if (flag == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5913 flag = SFLAG_INTEXTURE;
5916 if (surface->flags & flag)
5918 TRACE("Location already up to date\n");
5922 if (WARN_ON(d3d_surface))
5924 DWORD required_access = resource_access_from_location(flag);
5925 if ((surface->resource.access_flags & required_access) != required_access)
5926 WARN("Operation requires %#x access, but surface only has %#x.\n",
5927 required_access, surface->resource.access_flags);
5930 if (!(surface->flags & SFLAG_LOCATIONS))
5932 ERR("Surface %p does not have any up to date location.\n", surface);
5933 surface->flags |= SFLAG_LOST;
5934 return WINED3DERR_DEVICELOST;
5937 if (flag == SFLAG_INSYSMEM)
5939 surface_prepare_system_memory(surface);
5941 /* Download the surface to system memory */
5942 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5944 struct wined3d_context *context = NULL;
5946 if (!device->isInDraw) context = context_acquire(device, NULL);
5948 surface_bind_and_dirtify(surface, gl_info, !(surface->flags & SFLAG_INTEXTURE));
5949 surface_download_data(surface, gl_info);
5951 if (context) context_release(context);
5955 /* Note: It might be faster to download into a texture first. */
5956 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5957 wined3d_surface_get_pitch(surface));
5960 else if (flag == SFLAG_INDRAWABLE)
5962 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5963 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5965 if (surface->flags & SFLAG_INTEXTURE)
5969 surface_get_rect(surface, rect, &r);
5970 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5975 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5977 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
5978 * values, otherwise we get incorrect values in the target. For now go the slow way
5979 * via a system memory copy
5981 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5984 d3dfmt_get_conv(surface, FALSE /* We need color keying */,
5985 FALSE /* We won't use textures */, &format, &convert);
5987 /* The width is in 'length' not in bytes */
5988 width = surface->resource.width;
5989 pitch = wined3d_surface_get_pitch(surface);
5991 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
5992 * but it isn't set (yet) in all cases it is getting called. */
5993 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5995 struct wined3d_context *context = NULL;
5997 TRACE("Removing the pbo attached to surface %p.\n", surface);
5999 if (!device->isInDraw) context = context_acquire(device, NULL);
6000 surface_remove_pbo(surface, gl_info);
6001 if (context) context_release(context);
6004 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
6006 int height = surface->resource.height;
6007 byte_count = format.conv_byte_count;
6009 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6010 outpitch = width * byte_count;
6011 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6013 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
6015 ERR("Out of memory %d, %d!\n", outpitch, height);
6016 return WINED3DERR_OUTOFVIDEOMEMORY;
6018 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, pitch,
6019 width, height, outpitch, convert, surface);
6021 surface->flags |= SFLAG_CONVERTED;
6025 surface->flags &= ~SFLAG_CONVERTED;
6026 mem = surface->resource.allocatedMemory;
6027 byte_count = format.byte_count;
6030 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
6032 /* Don't delete PBO memory */
6033 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6034 HeapFree(GetProcessHeap(), 0, mem);
6037 else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */
6039 const DWORD attach_flags = WINED3DFMT_FLAG_FBO_ATTACHABLE | WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB;
6041 if (drawable_read_ok && (surface->flags & SFLAG_INDRAWABLE))
6043 read_from_framebuffer_texture(surface, flag == SFLAG_INSRGBTEX);
6045 else if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6046 && (surface->resource.format->flags & attach_flags) == attach_flags
6047 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6048 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6049 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6051 DWORD src_location = flag == SFLAG_INSRGBTEX ? SFLAG_INTEXTURE : SFLAG_INSRGBTEX;
6052 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6054 surface_blt_fbo(surface->resource.device, WINED3DTEXF_POINT,
6055 surface, src_location, &rect, surface, flag, &rect);
6059 /* Upload from system memory */
6060 BOOL srgb = flag == SFLAG_INSRGBTEX;
6061 struct wined3d_context *context = NULL;
6063 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6064 TRUE /* We will use textures */, &format, &convert);
6068 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6070 /* Performance warning... */
6071 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6072 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6077 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6079 /* Performance warning... */
6080 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6081 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6084 if (!(surface->flags & SFLAG_INSYSMEM))
6086 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6087 /* Lets hope we get it from somewhere... */
6088 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6091 if (!device->isInDraw) context = context_acquire(device, NULL);
6093 surface_prepare_texture(surface, gl_info, srgb);
6094 surface_bind_and_dirtify(surface, gl_info, srgb);
6096 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6098 surface->flags |= SFLAG_GLCKEY;
6099 surface->glCKey = surface->SrcBltCKey;
6101 else surface->flags &= ~SFLAG_GLCKEY;
6103 /* The width is in 'length' not in bytes */
6104 width = surface->resource.width;
6105 pitch = wined3d_surface_get_pitch(surface);
6107 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
6108 * but it isn't set (yet) in all cases it is getting called. */
6109 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6111 TRACE("Removing the pbo attached to surface %p.\n", surface);
6112 surface_remove_pbo(surface, gl_info);
6117 /* This code is entered for texture formats which need a fixup. */
6118 UINT height = surface->resource.height;
6120 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6121 outpitch = width * format.conv_byte_count;
6122 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6124 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
6126 ERR("Out of memory %d, %d!\n", outpitch, height);
6127 if (context) context_release(context);
6128 return WINED3DERR_OUTOFVIDEOMEMORY;
6130 format.convert(surface->resource.allocatedMemory, mem, pitch, width, height);
6132 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6134 /* This code is only entered for color keying fixups */
6135 UINT height = surface->resource.height;
6137 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6138 outpitch = width * format.conv_byte_count;
6139 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6141 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
6143 ERR("Out of memory %d, %d!\n", outpitch, height);
6144 if (context) context_release(context);
6145 return WINED3DERR_OUTOFVIDEOMEMORY;
6147 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, pitch,
6148 width, height, outpitch, convert, surface);
6152 mem = surface->resource.allocatedMemory;
6155 /* Make sure the correct pitch is used */
6157 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
6160 if (mem || (surface->flags & SFLAG_PBO))
6161 surface_upload_data(surface, gl_info, &format, srgb, mem);
6163 /* Restore the default pitch */
6165 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
6168 if (context) context_release(context);
6170 /* Don't delete PBO memory */
6171 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6172 HeapFree(GetProcessHeap(), 0, mem);
6178 surface->flags |= flag;
6180 if (flag != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6181 surface_evict_sysmem(surface);
6184 if (in_fbo && (surface->flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)))
6186 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
6187 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
6190 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6191 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6193 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6199 BOOL surface_is_offscreen(struct wined3d_surface *surface)
6201 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6203 /* Not on a swapchain - must be offscreen */
6204 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6206 /* The front buffer is always onscreen */
6207 if (surface == swapchain->front_buffer) return FALSE;
6209 /* If the swapchain is rendered to an FBO, the backbuffer is
6210 * offscreen, otherwise onscreen */
6211 return swapchain->render_to_fbo;
6214 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6215 /* Context activation is done by the caller. */
6216 static void ffp_blit_free(struct wined3d_device *device) { }
6218 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6219 /* Context activation is done by the caller. */
6220 static void ffp_blit_p8_upload_palette(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6223 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6225 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6227 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6229 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6233 /* Context activation is done by the caller. */
6234 static HRESULT ffp_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, struct wined3d_surface *surface)
6236 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6238 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6239 * else the surface is converted in software at upload time in LoadLocation.
6241 if(fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6242 ffp_blit_p8_upload_palette(surface, gl_info);
6245 glEnable(surface->texture_target);
6246 checkGLcall("glEnable(surface->texture_target)");
6251 /* Context activation is done by the caller. */
6252 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6255 glDisable(GL_TEXTURE_2D);
6256 checkGLcall("glDisable(GL_TEXTURE_2D)");
6257 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6259 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6260 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6262 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6264 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6265 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6270 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6271 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6272 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6274 enum complex_fixup src_fixup;
6278 case WINED3D_BLIT_OP_COLOR_BLIT:
6279 src_fixup = get_complex_fixup(src_format->color_fixup);
6280 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6282 TRACE("Checking support for fixup:\n");
6283 dump_color_fixup_desc(src_format->color_fixup);
6286 if (!is_identity_fixup(dst_format->color_fixup))
6288 TRACE("Destination fixups are not supported\n");
6292 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6294 TRACE("P8 fixup supported\n");
6298 /* We only support identity conversions. */
6299 if (is_identity_fixup(src_format->color_fixup))
6305 TRACE("[FAILED]\n");
6308 case WINED3D_BLIT_OP_COLOR_FILL:
6309 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6311 TRACE("Color fill not supported\n");
6317 case WINED3D_BLIT_OP_DEPTH_FILL:
6321 TRACE("Unsupported blit_op=%d\n", blit_op);
6326 /* Do not call while under the GL lock. */
6327 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6328 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
6330 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6332 return device_clear_render_targets(device, 1, &dst_surface, NULL,
6333 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6336 /* Do not call while under the GL lock. */
6337 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6338 struct wined3d_surface *surface, const RECT *rect, float depth)
6340 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6342 return device_clear_render_targets(device, 0, NULL, surface,
6343 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6346 const struct blit_shader ffp_blit = {
6352 ffp_blit_color_fill,
6353 ffp_blit_depth_fill,
6356 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6361 /* Context activation is done by the caller. */
6362 static void cpu_blit_free(struct wined3d_device *device)
6366 /* Context activation is done by the caller. */
6367 static HRESULT cpu_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, struct wined3d_surface *surface)
6372 /* Context activation is done by the caller. */
6373 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6377 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6378 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6379 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6381 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6389 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6390 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6391 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6393 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6394 const struct wined3d_format *src_format, *dst_format;
6395 struct wined3d_surface *orig_src = src_surface;
6396 WINED3DLOCKED_RECT dlock, slock;
6397 HRESULT hr = WINED3D_OK;
6403 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6404 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6405 flags, fx, debug_d3dtexturefiltertype(filter));
6407 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
6409 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY\n");
6410 return WINEDDERR_SURFACEBUSY;
6413 /* First check for the validity of source / destination rectangles.
6414 * This was verified using a test application and by MSDN. */
6419 if (src_rect->right < src_rect->left || src_rect->bottom < src_rect->top
6420 || src_rect->left > src_surface->resource.width || src_rect->left < 0
6421 || src_rect->top > src_surface->resource.height || src_rect->top < 0
6422 || src_rect->right > src_surface->resource.width || src_rect->right < 0
6423 || src_rect->bottom > src_surface->resource.height || src_rect->bottom < 0)
6425 WARN("Application gave us bad source rectangle for Blt.\n");
6426 return WINEDDERR_INVALIDRECT;
6429 if (!src_rect->right || !src_rect->bottom
6430 || src_rect->left == (int)src_surface->resource.width
6431 || src_rect->top == (int)src_surface->resource.height)
6433 TRACE("Nothing to be done.\n");
6440 else if (src_surface)
6444 xsrc.right = src_surface->resource.width;
6445 xsrc.bottom = src_surface->resource.height;
6449 memset(&xsrc, 0, sizeof(xsrc));
6454 /* For the Destination rect, it can be out of bounds on the condition
6455 * that a clipper is set for the given surface. */
6456 if (!dst_surface->clipper && (dst_rect->right < dst_rect->left || dst_rect->bottom < dst_rect->top
6457 || dst_rect->left > dst_surface->resource.width || dst_rect->left < 0
6458 || dst_rect->top > dst_surface->resource.height || dst_rect->top < 0
6459 || dst_rect->right > dst_surface->resource.width || dst_rect->right < 0
6460 || dst_rect->bottom > dst_surface->resource.height || dst_rect->bottom < 0))
6462 WARN("Application gave us bad destination rectangle for Blt without a clipper set.\n");
6463 return WINEDDERR_INVALIDRECT;
6466 if (dst_rect->right <= 0 || dst_rect->bottom <= 0
6467 || dst_rect->left >= (int)dst_surface->resource.width
6468 || dst_rect->top >= (int)dst_surface->resource.height)
6470 TRACE("Nothing to be done.\n");
6480 full_rect.right = dst_surface->resource.width;
6481 full_rect.bottom = dst_surface->resource.height;
6482 IntersectRect(&xdst, &full_rect, dst_rect);
6486 BOOL clip_horiz, clip_vert;
6489 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6490 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6492 if (clip_vert || clip_horiz)
6494 /* Now check if this is a special case or not... */
6495 if ((flags & WINEDDBLT_DDFX)
6496 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6497 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6499 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6507 xsrc.left -= xdst.left;
6510 if (xdst.right > dst_surface->resource.width)
6512 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6513 xdst.right = (int)dst_surface->resource.width;
6521 xsrc.top -= xdst.top;
6524 if (xdst.bottom > dst_surface->resource.height)
6526 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6527 xdst.bottom = (int)dst_surface->resource.height;
6531 /* And check if after clipping something is still to be done... */
6532 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6533 || (xdst.left >= (int)dst_surface->resource.width)
6534 || (xdst.top >= (int)dst_surface->resource.height)
6535 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6536 || (xsrc.left >= (int)src_surface->resource.width)
6537 || (xsrc.top >= (int)src_surface->resource.height))
6539 TRACE("Nothing to be done after clipping.\n");
6549 xdst.right = dst_surface->resource.width;
6550 xdst.bottom = dst_surface->resource.height;
6553 if (src_surface == dst_surface)
6555 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6557 src_format = dst_surface->resource.format;
6558 dst_format = src_format;
6562 dst_format = dst_surface->resource.format;
6565 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6567 src_surface = surface_convert_format(src_surface, dst_format->id);
6570 /* The conv function writes a FIXME */
6571 WARN("Cannot convert source surface format to dest format.\n");
6575 wined3d_surface_map(src_surface, &slock, NULL, WINED3DLOCK_READONLY);
6576 src_format = src_surface->resource.format;
6580 src_format = dst_format;
6583 wined3d_surface_map(dst_surface, &dlock, &xdst, 0);
6585 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6588 if (!fx || !(fx->dwDDFX)) flags &= ~WINEDDBLT_DDFX;
6590 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_FOURCC)
6592 if (!dst_rect || src_surface == dst_surface)
6594 memcpy(dlock.pBits, slock.pBits, dst_surface->resource.size);
6599 bpp = dst_surface->resource.format->byte_count;
6600 srcheight = xsrc.bottom - xsrc.top;
6601 srcwidth = xsrc.right - xsrc.left;
6602 dstheight = xdst.bottom - xdst.top;
6603 dstwidth = xdst.right - xdst.left;
6604 width = (xdst.right - xdst.left) * bpp;
6606 if (dst_rect && src_surface != dst_surface)
6609 dbuf = (BYTE*)dlock.pBits+(xdst.top*dlock.Pitch)+(xdst.left*bpp);
6611 if (flags & WINEDDBLT_WAIT)
6613 flags &= ~WINEDDBLT_WAIT;
6615 if (flags & WINEDDBLT_ASYNC)
6617 static BOOL displayed = FALSE;
6619 FIXME("Can't handle WINEDDBLT_ASYNC flag right now.\n");
6621 flags &= ~WINEDDBLT_ASYNC;
6623 if (flags & WINEDDBLT_DONOTWAIT)
6625 /* WINEDDBLT_DONOTWAIT appeared in DX7 */
6626 static BOOL displayed = FALSE;
6628 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag right now.\n");
6630 flags &= ~WINEDDBLT_DONOTWAIT;
6633 /* First, all the 'source-less' blits */
6634 if (flags & WINEDDBLT_COLORFILL)
6636 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dlock.Pitch, fx->u5.dwFillColor);
6637 flags &= ~WINEDDBLT_COLORFILL;
6640 if (flags & WINEDDBLT_DEPTHFILL)
6642 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6644 if (flags & WINEDDBLT_ROP)
6646 /* Catch some degenerate cases here. */
6650 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,0);
6652 case 0xAA0029: /* No-op */
6655 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,~0);
6657 case SRCCOPY: /* Well, we do that below? */
6660 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6663 flags &= ~WINEDDBLT_ROP;
6665 if (flags & WINEDDBLT_DDROPS)
6667 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6669 /* Now the 'with source' blits. */
6673 int sx, xinc, sy, yinc;
6675 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6678 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6679 && (srcwidth != dstwidth || srcheight != dstheight))
6681 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6682 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6685 sbase = (BYTE*)slock.pBits+(xsrc.top*slock.Pitch)+xsrc.left*bpp;
6686 xinc = (srcwidth << 16) / dstwidth;
6687 yinc = (srcheight << 16) / dstheight;
6691 /* No effects, we can cheat here. */
6692 if (dstwidth == srcwidth)
6694 if (dstheight == srcheight)
6696 /* No stretching in either direction. This needs to be as
6697 * fast as possible. */
6700 /* Check for overlapping surfaces. */
6701 if (src_surface != dst_surface || xdst.top < xsrc.top
6702 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6704 /* No overlap, or dst above src, so copy from top downwards. */
6705 for (y = 0; y < dstheight; ++y)
6707 memcpy(dbuf, sbuf, width);
6708 sbuf += slock.Pitch;
6709 dbuf += dlock.Pitch;
6712 else if (xdst.top > xsrc.top)
6714 /* Copy from bottom upwards. */
6715 sbuf += (slock.Pitch*dstheight);
6716 dbuf += (dlock.Pitch*dstheight);
6717 for (y = 0; y < dstheight; ++y)
6719 sbuf -= slock.Pitch;
6720 dbuf -= dlock.Pitch;
6721 memcpy(dbuf, sbuf, width);
6726 /* Src and dst overlapping on the same line, use memmove. */
6727 for (y = 0; y < dstheight; ++y)
6729 memmove(dbuf, sbuf, width);
6730 sbuf += slock.Pitch;
6731 dbuf += dlock.Pitch;
6737 /* Stretching in y direction only. */
6738 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6740 sbuf = sbase + (sy >> 16) * slock.Pitch;
6741 memcpy(dbuf, sbuf, width);
6742 dbuf += dlock.Pitch;
6748 /* Stretching in X direction. */
6750 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6752 sbuf = sbase + (sy >> 16) * slock.Pitch;
6754 if ((sy >> 16) == (last_sy >> 16))
6756 /* This source row is the same as last source row -
6757 * Copy the already stretched row. */
6758 memcpy(dbuf, dbuf - dlock.Pitch, width);
6762 #define STRETCH_ROW(type) \
6764 const type *s = (const type *)sbuf; \
6765 type *d = (type *)dbuf; \
6766 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6767 d[x] = s[sx >> 16]; \
6785 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6789 s = sbuf + 3 * (sx >> 16);
6790 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6791 d[0] = (pixel ) & 0xff;
6792 d[1] = (pixel >> 8) & 0xff;
6793 d[2] = (pixel >> 16) & 0xff;
6799 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6800 hr = WINED3DERR_NOTAVAILABLE;
6805 dbuf += dlock.Pitch;
6812 LONG dstyinc = dlock.Pitch, dstxinc = bpp;
6813 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6814 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6815 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6817 /* The color keying flags are checked for correctness in ddraw */
6818 if (flags & WINEDDBLT_KEYSRC)
6820 keylow = src_surface->SrcBltCKey.dwColorSpaceLowValue;
6821 keyhigh = src_surface->SrcBltCKey.dwColorSpaceHighValue;
6823 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6825 keylow = fx->ddckSrcColorkey.dwColorSpaceLowValue;
6826 keyhigh = fx->ddckSrcColorkey.dwColorSpaceHighValue;
6829 if (flags & WINEDDBLT_KEYDEST)
6831 /* Destination color keys are taken from the source surface! */
6832 destkeylow = src_surface->DestBltCKey.dwColorSpaceLowValue;
6833 destkeyhigh = src_surface->DestBltCKey.dwColorSpaceHighValue;
6835 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6837 destkeylow = fx->ddckDestColorkey.dwColorSpaceLowValue;
6838 destkeyhigh = fx->ddckDestColorkey.dwColorSpaceHighValue;
6847 keymask = src_format->red_mask
6848 | src_format->green_mask
6849 | src_format->blue_mask;
6851 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6854 if (flags & WINEDDBLT_DDFX)
6856 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6859 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6860 dBottomLeft = dTopLeft + ((dstheight - 1) * dlock.Pitch);
6861 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6863 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6865 /* I don't think we need to do anything about this flag */
6866 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6868 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6871 dTopRight = dTopLeft;
6874 dBottomRight = dBottomLeft;
6876 dstxinc = dstxinc * -1;
6878 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6881 dTopLeft = dBottomLeft;
6884 dTopRight = dBottomRight;
6886 dstyinc = dstyinc * -1;
6888 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6890 /* I don't think we need to do anything about this flag */
6891 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6893 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6896 dBottomRight = dTopLeft;
6899 dBottomLeft = dTopRight;
6901 dstxinc = dstxinc * -1;
6902 dstyinc = dstyinc * -1;
6904 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6907 dTopLeft = dBottomLeft;
6908 dBottomLeft = dBottomRight;
6909 dBottomRight = dTopRight;
6914 dstxinc = dstxinc * -1;
6916 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6919 dTopLeft = dTopRight;
6920 dTopRight = dBottomRight;
6921 dBottomRight = dBottomLeft;
6926 dstyinc = dstyinc * -1;
6928 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6930 /* I don't think we need to do anything about this flag */
6931 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6934 flags &= ~(WINEDDBLT_DDFX);
6937 #define COPY_COLORKEY_FX(type) \
6940 type *d = (type *)dbuf, *dx, tmp; \
6941 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6943 s = (const type *)(sbase + (sy >> 16) * slock.Pitch); \
6945 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6947 tmp = s[sx >> 16]; \
6948 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6949 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6953 dx = (type *)(((BYTE *)dx) + dstxinc); \
6955 d = (type *)(((BYTE *)d) + dstyinc); \
6962 COPY_COLORKEY_FX(BYTE);
6965 COPY_COLORKEY_FX(WORD);
6968 COPY_COLORKEY_FX(DWORD);
6973 BYTE *d = dbuf, *dx;
6974 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6976 sbuf = sbase + (sy >> 16) * slock.Pitch;
6978 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
6980 DWORD pixel, dpixel = 0;
6981 s = sbuf + 3 * (sx>>16);
6982 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6983 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
6984 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
6985 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
6987 dx[0] = (pixel ) & 0xff;
6988 dx[1] = (pixel >> 8) & 0xff;
6989 dx[2] = (pixel >> 16) & 0xff;
6998 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
6999 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7000 hr = WINED3DERR_NOTAVAILABLE;
7002 #undef COPY_COLORKEY_FX
7008 if (flags && FIXME_ON(d3d_surface))
7010 FIXME("\tUnsupported flags: %#x.\n", flags);
7014 wined3d_surface_unmap(dst_surface);
7015 if (src_surface && src_surface != dst_surface)
7016 wined3d_surface_unmap(src_surface);
7017 /* Release the converted surface, if any. */
7018 if (src_surface && src_surface != orig_src)
7019 wined3d_surface_decref(src_surface);
7024 static HRESULT surface_cpu_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
7025 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD trans)
7027 const struct wined3d_format *src_format, *dst_format;
7028 RECT lock_src, lock_dst, lock_union;
7029 WINED3DLOCKED_RECT dlock, slock;
7030 HRESULT hr = WINED3D_OK;
7031 int bpp, w, h, x, y;
7036 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect %s, flags %#x.\n",
7037 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect), trans);
7039 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface->flags & SFLAG_LOCKED))
7041 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
7042 return WINEDDERR_SURFACEBUSY;
7047 WARN("src_rect is NULL!\n");
7050 rsrc2.right = src_surface->resource.width;
7051 rsrc2.bottom = src_surface->resource.height;
7055 /* Check source rect for validity. Copied from normal Blt. Fixes Baldur's Gate. */
7056 if ((src_rect->bottom > src_surface->resource.height) || (src_rect->bottom < 0)
7057 || (src_rect->top > src_surface->resource.height) || (src_rect->top < 0)
7058 || (src_rect->left > src_surface->resource.width) || (src_rect->left < 0)
7059 || (src_rect->right > src_surface->resource.width) || (src_rect->right < 0)
7060 || (src_rect->right < src_rect->left) || (src_rect->bottom < src_rect->top))
7062 WARN("Application gave us bad source rectangle for BltFast.\n");
7063 return WINEDDERR_INVALIDRECT;
7066 h = src_rect->bottom - src_rect->top;
7067 if (h > dst_surface->resource.height - dst_y)
7068 h = dst_surface->resource.height - dst_y;
7069 if (h > src_surface->resource.height - src_rect->top)
7070 h = src_surface->resource.height - src_rect->top;
7072 return WINEDDERR_INVALIDRECT;
7074 w = src_rect->right - src_rect->left;
7075 if (w > dst_surface->resource.width - dst_x)
7076 w = dst_surface->resource.width - dst_x;
7077 if (w > src_surface->resource.width - src_rect->left)
7078 w = src_surface->resource.width - src_rect->left;
7080 return WINEDDERR_INVALIDRECT;
7082 /* Now compute the locking rectangle... */
7083 lock_src.left = src_rect->left;
7084 lock_src.top = src_rect->top;
7085 lock_src.right = lock_src.left + w;
7086 lock_src.bottom = lock_src.top + h;
7088 lock_dst.left = dst_x;
7089 lock_dst.top = dst_y;
7090 lock_dst.right = dst_x + w;
7091 lock_dst.bottom = dst_y + h;
7093 bpp = dst_surface->resource.format->byte_count;
7095 /* We need to lock the surfaces, or we won't get refreshes when done. */
7096 if (src_surface == dst_surface)
7100 UnionRect(&lock_union, &lock_src, &lock_dst);
7102 /* Lock the union of the two rectangles. */
7103 hr = wined3d_surface_map(dst_surface, &dlock, &lock_union, 0);
7107 pitch = dlock.Pitch;
7108 slock.Pitch = dlock.Pitch;
7110 /* Since slock was originally copied from this surface's description, we can just reuse it. */
7111 sbuf = dst_surface->resource.allocatedMemory + lock_src.top * pitch + lock_src.left * bpp;
7112 dbuf = dst_surface->resource.allocatedMemory + lock_dst.top * pitch + lock_dst.left * bpp;
7113 src_format = src_surface->resource.format;
7114 dst_format = src_format;
7118 hr = wined3d_surface_map(src_surface, &slock, &lock_src, WINED3DLOCK_READONLY);
7121 hr = wined3d_surface_map(dst_surface, &dlock, &lock_dst, 0);
7127 TRACE("Dst is at %p, Src is at %p.\n", dbuf, sbuf);
7129 src_format = src_surface->resource.format;
7130 dst_format = dst_surface->resource.format;
7133 /* Handle compressed surfaces first... */
7134 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_COMPRESSED)
7136 UINT row_block_count;
7138 TRACE("compressed -> compressed copy\n");
7140 FIXME("trans arg not supported when a compressed surface is involved\n");
7142 FIXME("offset for destination surface is not supported\n");
7143 if (src_surface->resource.format->id != dst_surface->resource.format->id)
7145 FIXME("compressed -> compressed copy only supported for the same type of surface\n");
7146 hr = WINED3DERR_WRONGTEXTUREFORMAT;
7150 row_block_count = (w + dst_format->block_width - 1) / dst_format->block_width;
7151 for (y = 0; y < h; y += dst_format->block_height)
7153 memcpy(dbuf, sbuf, row_block_count * dst_format->block_byte_count);
7154 dbuf += dlock.Pitch;
7155 sbuf += slock.Pitch;
7160 if ((src_format->flags & WINED3DFMT_FLAG_COMPRESSED) && !(dst_format->flags & WINED3DFMT_FLAG_COMPRESSED))
7162 /* TODO: Use the libtxc_dxtn.so shared library to do software
7164 ERR("Software decompression not supported.\n");
7168 if (trans & (WINEDDBLTFAST_SRCCOLORKEY | WINEDDBLTFAST_DESTCOLORKEY))
7170 DWORD keylow, keyhigh;
7171 DWORD mask = src_surface->resource.format->red_mask
7172 | src_surface->resource.format->green_mask
7173 | src_surface->resource.format->blue_mask;
7175 /* For some 8-bit formats like L8 and P8 color masks don't make sense */
7176 if (!mask && bpp == 1)
7179 TRACE("Color keyed copy.\n");
7180 if (trans & WINEDDBLTFAST_SRCCOLORKEY)
7182 keylow = src_surface->SrcBltCKey.dwColorSpaceLowValue;
7183 keyhigh = src_surface->SrcBltCKey.dwColorSpaceHighValue;
7187 /* I'm not sure if this is correct. */
7188 FIXME("WINEDDBLTFAST_DESTCOLORKEY not fully supported yet.\n");
7189 keylow = dst_surface->DestBltCKey.dwColorSpaceLowValue;
7190 keyhigh = dst_surface->DestBltCKey.dwColorSpaceHighValue;
7193 #define COPYBOX_COLORKEY(type) \
7195 const type *s = (const type *)sbuf; \
7196 type *d = (type *)dbuf; \
7198 for (y = 0; y < h; y++) \
7200 for (x = 0; x < w; x++) \
7203 if ((tmp & mask) < keylow || (tmp & mask) > keyhigh) d[x] = tmp; \
7205 s = (const type *)((const BYTE *)s + slock.Pitch); \
7206 d = (type *)((BYTE *)d + dlock.Pitch); \
7213 COPYBOX_COLORKEY(BYTE);
7216 COPYBOX_COLORKEY(WORD);
7219 COPYBOX_COLORKEY(DWORD);
7228 for (y = 0; y < h; ++y)
7230 for (x = 0; x < w * 3; x += 3)
7232 tmp = (DWORD)s[x] + ((DWORD)s[x + 1] << 8) + ((DWORD)s[x + 2] << 16);
7233 if (tmp < keylow || tmp > keyhigh)
7235 d[x + 0] = s[x + 0];
7236 d[x + 1] = s[x + 1];
7237 d[x + 2] = s[x + 2];
7246 FIXME("Source color key blitting not supported for bpp %u.\n", bpp * 8);
7247 hr = WINED3DERR_NOTAVAILABLE;
7250 #undef COPYBOX_COLORKEY
7251 TRACE("Copy done.\n");
7255 int width = w * bpp;
7256 INT sbufpitch, dbufpitch;
7258 TRACE("No color key copy.\n");
7259 /* Handle overlapping surfaces. */
7262 sbuf += (h - 1) * slock.Pitch;
7263 dbuf += (h - 1) * dlock.Pitch;
7264 sbufpitch = -slock.Pitch;
7265 dbufpitch = -dlock.Pitch;
7269 sbufpitch = slock.Pitch;
7270 dbufpitch = dlock.Pitch;
7272 for (y = 0; y < h; ++y)
7274 /* This is pretty easy, a line for line memcpy. */
7275 memmove(dbuf, sbuf, width);
7279 TRACE("Copy done.\n");
7283 if (src_surface == dst_surface)
7285 wined3d_surface_unmap(dst_surface);
7289 wined3d_surface_unmap(dst_surface);
7290 wined3d_surface_unmap(src_surface);
7296 /* Do not call while under the GL lock. */
7297 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7298 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
7302 memset(&BltFx, 0, sizeof(BltFx));
7303 BltFx.dwSize = sizeof(BltFx);
7304 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface->resource.format, color);
7305 return wined3d_surface_blt(dst_surface, dst_rect, NULL, NULL,
7306 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7309 /* Do not call while under the GL lock. */
7310 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7311 struct wined3d_surface *surface, const RECT *rect, float depth)
7313 FIXME("Depth filling not implemented by cpu_blit.\n");
7314 return WINED3DERR_INVALIDCALL;
7317 const struct blit_shader cpu_blit = {
7323 cpu_blit_color_fill,
7324 cpu_blit_depth_fill,
7327 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7328 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
7329 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7330 WINED3DPOOL pool, void *parent, const struct wined3d_parent_ops *parent_ops)
7332 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7333 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7334 unsigned int resource_size;
7337 if (multisample_quality > 0)
7339 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7340 multisample_quality = 0;
7343 /* Quick lockable sanity check.
7344 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7345 * this function is too deep to need to care about things like this.
7346 * Levels need to be checked too, since they all affect what can be done. */
7349 case WINED3DPOOL_SCRATCH:
7352 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7353 "which are mutually exclusive, setting lockable to TRUE.\n");
7358 case WINED3DPOOL_SYSTEMMEM:
7360 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7363 case WINED3DPOOL_MANAGED:
7364 if (usage & WINED3DUSAGE_DYNAMIC)
7365 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7368 case WINED3DPOOL_DEFAULT:
7369 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7370 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7374 FIXME("Unknown pool %#x.\n", pool);
7378 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7379 FIXME("Trying to create a render target that isn't in the default pool.\n");
7381 /* FIXME: Check that the format is supported by the device. */
7383 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7385 return WINED3DERR_INVALIDCALL;
7387 surface->surface_type = surface_type;
7389 switch (surface_type)
7391 case SURFACE_OPENGL:
7392 surface->surface_ops = &surface_ops;
7396 surface->surface_ops = &gdi_surface_ops;
7400 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7401 return WINED3DERR_INVALIDCALL;
7404 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7405 multisample_type, multisample_quality, usage, pool, width, height, 1,
7406 resource_size, parent, parent_ops, &surface_resource_ops);
7409 WARN("Failed to initialize resource, returning %#x.\n", hr);
7413 /* "Standalone" surface. */
7414 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7416 surface->texture_level = level;
7417 list_init(&surface->overlays);
7420 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7422 surface->flags |= SFLAG_DISCARD;
7423 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7424 surface->flags |= SFLAG_LOCKABLE;
7425 /* I'm not sure if this qualifies as a hack or as an optimization. It
7426 * seems reasonable to assume that lockable render targets will get
7427 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7428 * creation. However, the other reason we want to do this is that several
7429 * ddraw applications access surface memory while the surface isn't
7430 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7431 * future locks prevents these from crashing. */
7432 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7433 surface->flags |= SFLAG_DYNLOCK;
7435 /* Mark the texture as dirty so that it gets loaded first time around. */
7436 surface_add_dirty_rect(surface, NULL);
7437 list_init(&surface->renderbuffers);
7439 TRACE("surface %p, memory %p, size %u\n",
7440 surface, surface->resource.allocatedMemory, surface->resource.size);
7442 /* Call the private setup routine */
7443 hr = surface->surface_ops->surface_private_setup(surface);
7446 ERR("Private setup failed, returning %#x\n", hr);
7447 surface->surface_ops->surface_cleanup(surface);
7454 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7455 enum wined3d_format_id format_id, BOOL lockable, BOOL discard, UINT level, DWORD usage, WINED3DPOOL pool,
7456 WINED3DMULTISAMPLE_TYPE multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7457 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7459 struct wined3d_surface *object;
7462 TRACE("device %p, width %u, height %u, format %s, lockable %#x, discard %#x, level %u\n",
7463 device, width, height, debug_d3dformat(format_id), lockable, discard, level);
7464 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7465 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7466 TRACE("surface_type %#x, parent %p, parent_ops %p.\n", surface_type, parent, parent_ops);
7468 if (surface_type == SURFACE_OPENGL && !device->adapter)
7470 ERR("OpenGL surfaces are not available without OpenGL.\n");
7471 return WINED3DERR_NOTAVAILABLE;
7474 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7477 ERR("Failed to allocate surface memory.\n");
7478 return WINED3DERR_OUTOFVIDEOMEMORY;
7481 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level, lockable,
7482 discard, multisample_type, multisample_quality, device, usage, format_id, pool, parent, parent_ops);
7485 WARN("Failed to initialize surface, returning %#x.\n", hr);
7486 HeapFree(GetProcessHeap(), 0, object);
7490 TRACE("Created surface %p.\n", object);