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 void surface_get_rect(const 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(const 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(const 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 context_invalidate_state(context, 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 context_invalidate_state(context, STATE_RENDER(WINED3DRS_TWOSIDEDSTENCILMODE));
1158 context_invalidate_state(context, STATE_RENDER(WINED3DRS_STENCILWRITEMASK));
1161 glDisable(GL_SCISSOR_TEST);
1162 context_invalidate_state(context, 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(const 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,
1285 struct wined3d_surface *src_surface, const RECT *src_rect, 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), src_surface, wine_dbgstr_rect(src_rect),
1293 flags, fx, debug_d3dtexturefiltertype(filter));
1294 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1296 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1298 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1302 if (src_ds_flags || dst_ds_flags)
1304 if (flags & WINEDDBLT_DEPTHFILL)
1308 TRACE("Depth fill.\n");
1310 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1311 return WINED3DERR_INVALIDCALL;
1313 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, dst_rect, depth)))
1318 /* Accessing depth / stencil surfaces is supposed to fail while in
1319 * a scene, except for fills, which seem to work. */
1320 if (device->inScene)
1322 WARN("Rejecting depth / stencil access while in scene.\n");
1323 return WINED3DERR_INVALIDCALL;
1326 if (src_ds_flags != dst_ds_flags)
1328 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1329 return WINED3DERR_INVALIDCALL;
1332 if (src_rect->top || src_rect->left
1333 || src_rect->bottom != src_surface->resource.height
1334 || src_rect->right != src_surface->resource.width)
1336 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1337 wine_dbgstr_rect(src_rect));
1338 return WINED3DERR_INVALIDCALL;
1341 if (dst_rect->top || dst_rect->left
1342 || dst_rect->bottom != dst_surface->resource.height
1343 || dst_rect->right != dst_surface->resource.width)
1345 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1346 wine_dbgstr_rect(src_rect));
1347 return WINED3DERR_INVALIDCALL;
1350 if (src_surface->resource.height != dst_surface->resource.height
1351 || src_surface->resource.width != dst_surface->resource.width)
1353 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1354 return WINED3DERR_INVALIDCALL;
1357 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_rect, dst_surface, dst_rect)))
1362 /* Special cases for render targets. */
1363 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1364 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1366 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, dst_rect,
1367 src_surface, src_rect, flags, fx, filter)))
1371 /* For the rest call the X11 surface implementation. For render targets
1372 * this should be implemented OpenGL accelerated in BltOverride, other
1373 * blits are rather rare. */
1374 return surface_cpu_blt(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter);
1377 /* Do not call while under the GL lock. */
1378 static HRESULT surface_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
1379 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD trans)
1381 struct wined3d_device *device = dst_surface->resource.device;
1383 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect %s, flags %#x.\n",
1384 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect_in), trans);
1386 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface->flags & SFLAG_LOCKED))
1388 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1389 return WINEDDERR_SURFACEBUSY;
1392 if (device->inScene && (dst_surface == device->fb.depth_stencil || src_surface == device->fb.depth_stencil))
1394 WARN("Attempt to access the depth / stencil surface while in a scene.\n");
1395 return WINED3DERR_INVALIDCALL;
1398 /* Special cases for RenderTargets */
1399 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1400 || (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
1403 RECT src_rect, dst_rect;
1406 surface_get_rect(src_surface, src_rect_in, &src_rect);
1408 dst_rect.left = dst_x;
1409 dst_rect.top = dst_y;
1410 dst_rect.right = dst_x + src_rect.right - src_rect.left;
1411 dst_rect.bottom = dst_y + src_rect.bottom - src_rect.top;
1413 /* Convert BltFast flags into Blt ones because BltOverride is called
1414 * from Blt as well. */
1415 if (trans & WINEDDBLTFAST_SRCCOLORKEY)
1416 flags |= WINEDDBLT_KEYSRC;
1417 if (trans & WINEDDBLTFAST_DESTCOLORKEY)
1418 flags |= WINEDDBLT_KEYDEST;
1419 if (trans & WINEDDBLTFAST_WAIT)
1420 flags |= WINEDDBLT_WAIT;
1421 if (trans & WINEDDBLTFAST_DONOTWAIT)
1422 flags |= WINEDDBLT_DONOTWAIT;
1424 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface,
1425 &dst_rect, src_surface, &src_rect, flags, NULL, WINED3DTEXF_POINT)))
1429 return surface_cpu_bltfast(dst_surface, dst_x, dst_y, src_surface, src_rect_in, trans);
1432 static HRESULT surface_set_mem(struct wined3d_surface *surface, void *mem)
1434 TRACE("surface %p, mem %p.\n", surface, mem);
1436 if (mem && mem != surface->resource.allocatedMemory)
1438 void *release = NULL;
1440 /* Do I have to copy the old surface content? */
1441 if (surface->flags & SFLAG_DIBSECTION)
1443 SelectObject(surface->hDC, surface->dib.holdbitmap);
1444 DeleteDC(surface->hDC);
1445 /* Release the DIB section. */
1446 DeleteObject(surface->dib.DIBsection);
1447 surface->dib.bitmap_data = NULL;
1448 surface->resource.allocatedMemory = NULL;
1449 surface->hDC = NULL;
1450 surface->flags &= ~SFLAG_DIBSECTION;
1452 else if (!(surface->flags & SFLAG_USERPTR))
1454 release = surface->resource.heapMemory;
1455 surface->resource.heapMemory = NULL;
1457 surface->resource.allocatedMemory = mem;
1458 surface->flags |= SFLAG_USERPTR;
1460 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
1461 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1463 /* For client textures OpenGL has to be notified. */
1464 if (surface->flags & SFLAG_CLIENT)
1465 surface_release_client_storage(surface);
1467 /* Now free the old memory if any. */
1468 HeapFree(GetProcessHeap(), 0, release);
1470 else if (surface->flags & SFLAG_USERPTR)
1472 /* Map and GetDC will re-create the dib section and allocated memory. */
1473 surface->resource.allocatedMemory = NULL;
1474 /* HeapMemory should be NULL already. */
1475 if (surface->resource.heapMemory)
1476 ERR("User pointer surface has heap memory allocated.\n");
1477 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
1479 if (surface->flags & SFLAG_CLIENT)
1480 surface_release_client_storage(surface);
1482 surface_prepare_system_memory(surface);
1483 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1489 /* Context activation is done by the caller. */
1490 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1492 if (!surface->resource.heapMemory)
1494 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1495 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1496 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1500 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1501 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1502 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1503 surface->resource.size, surface->resource.allocatedMemory));
1504 checkGLcall("glGetBufferSubDataARB");
1505 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1506 checkGLcall("glDeleteBuffersARB");
1510 surface->flags &= ~SFLAG_PBO;
1513 /* Do not call while under the GL lock. */
1514 static void surface_unload(struct wined3d_resource *resource)
1516 struct wined3d_surface *surface = surface_from_resource(resource);
1517 struct wined3d_renderbuffer_entry *entry, *entry2;
1518 struct wined3d_device *device = resource->device;
1519 const struct wined3d_gl_info *gl_info;
1520 struct wined3d_context *context;
1522 TRACE("surface %p.\n", surface);
1524 if (resource->pool == WINED3DPOOL_DEFAULT)
1526 /* Default pool resources are supposed to be destroyed before Reset is called.
1527 * Implicit resources stay however. So this means we have an implicit render target
1528 * or depth stencil. The content may be destroyed, but we still have to tear down
1529 * opengl resources, so we cannot leave early.
1531 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1532 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1533 * or the depth stencil into an FBO the texture or render buffer will be removed
1534 * and all flags get lost
1536 surface_init_sysmem(surface);
1540 /* Load the surface into system memory */
1541 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1542 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
1544 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1545 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1546 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1548 context = context_acquire(device, NULL);
1549 gl_info = context->gl_info;
1551 /* Destroy PBOs, but load them into real sysmem before */
1552 if (surface->flags & SFLAG_PBO)
1553 surface_remove_pbo(surface, gl_info);
1555 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1556 * all application-created targets the application has to release the surface
1557 * before calling _Reset
1559 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1562 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1564 list_remove(&entry->entry);
1565 HeapFree(GetProcessHeap(), 0, entry);
1567 list_init(&surface->renderbuffers);
1568 surface->current_renderbuffer = NULL;
1570 /* If we're in a texture, the texture name belongs to the texture.
1571 * Otherwise, destroy it. */
1572 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1575 glDeleteTextures(1, &surface->texture_name);
1576 surface->texture_name = 0;
1577 glDeleteTextures(1, &surface->texture_name_srgb);
1578 surface->texture_name_srgb = 0;
1582 context_release(context);
1584 resource_unload(resource);
1587 static const struct wined3d_resource_ops surface_resource_ops =
1592 static const struct wined3d_surface_ops surface_ops =
1594 surface_private_setup,
1596 surface_realize_palette,
1597 surface_draw_overlay,
1608 /*****************************************************************************
1609 * Initializes the GDI surface, aka creates the DIB section we render to
1610 * The DIB section creation is done by calling GetDC, which will create the
1611 * section and releasing the dc to allow the app to use it. The dib section
1612 * will stay until the surface is released
1614 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1615 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1616 * avoid confusion in the shared surface code.
1619 * WINED3D_OK on success
1620 * The return values of called methods on failure
1622 *****************************************************************************/
1623 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1627 TRACE("surface %p.\n", surface);
1629 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1631 ERR("Overlays not yet supported by GDI surfaces.\n");
1632 return WINED3DERR_INVALIDCALL;
1635 /* Sysmem textures have memory already allocated - release it,
1636 * this avoids an unnecessary memcpy. */
1637 hr = surface_create_dib_section(surface);
1640 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1641 surface->resource.heapMemory = NULL;
1642 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1645 /* We don't mind the nonpow2 stuff in GDI. */
1646 surface->pow2Width = surface->resource.width;
1647 surface->pow2Height = surface->resource.height;
1652 static void surface_gdi_cleanup(struct wined3d_surface *surface)
1654 TRACE("surface %p.\n", surface);
1656 if (surface->flags & SFLAG_DIBSECTION)
1658 /* Release the DC. */
1659 SelectObject(surface->hDC, surface->dib.holdbitmap);
1660 DeleteDC(surface->hDC);
1661 /* Release the DIB section. */
1662 DeleteObject(surface->dib.DIBsection);
1663 surface->dib.bitmap_data = NULL;
1664 surface->resource.allocatedMemory = NULL;
1667 if (surface->flags & SFLAG_USERPTR)
1668 wined3d_surface_set_mem(surface, NULL);
1669 if (surface->overlay_dest)
1670 list_remove(&surface->overlay_entry);
1672 HeapFree(GetProcessHeap(), 0, surface->palette9);
1674 resource_cleanup(&surface->resource);
1677 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1679 struct wined3d_palette *palette = surface->palette;
1681 TRACE("surface %p.\n", surface);
1683 if (!palette) return;
1685 if (surface->flags & SFLAG_DIBSECTION)
1690 TRACE("Updating the DC's palette.\n");
1692 for (i = 0; i < 256; ++i)
1694 col[i].rgbRed = palette->palents[i].peRed;
1695 col[i].rgbGreen = palette->palents[i].peGreen;
1696 col[i].rgbBlue = palette->palents[i].peBlue;
1697 col[i].rgbReserved = 0;
1699 SetDIBColorTable(surface->hDC, 0, 256, col);
1702 /* Update the image because of the palette change. Some games like e.g.
1703 * Red Alert call SetEntries a lot to implement fading. */
1704 /* Tell the swapchain to update the screen. */
1705 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1707 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1708 if (surface == swapchain->front_buffer)
1710 x11_copy_to_screen(swapchain, NULL);
1715 static HRESULT gdi_surface_draw_overlay(struct wined3d_surface *surface)
1717 FIXME("GDI surfaces can't draw overlays yet.\n");
1721 static void gdi_surface_preload(struct wined3d_surface *surface)
1723 TRACE("surface %p.\n", surface);
1725 ERR("Preloading GDI surfaces is not supported.\n");
1728 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
1730 TRACE("surface %p, rect %s, flags %#x.\n",
1731 surface, wine_dbgstr_rect(rect), flags);
1733 if (!surface->resource.allocatedMemory)
1735 /* This happens on gdi surfaces if the application set a user pointer
1736 * and resets it. Recreate the DIB section. */
1737 surface_create_dib_section(surface);
1738 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1742 static void gdi_surface_unmap(struct wined3d_surface *surface)
1744 TRACE("surface %p.\n", surface);
1746 /* Tell the swapchain to update the screen. */
1747 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1749 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1750 if (surface == swapchain->front_buffer)
1752 x11_copy_to_screen(swapchain, &surface->lockedRect);
1756 memset(&surface->lockedRect, 0, sizeof(RECT));
1759 static HRESULT gdi_surface_getdc(struct wined3d_surface *surface)
1761 WINED3DLOCKED_RECT lock;
1764 TRACE("surface %p.\n", surface);
1766 /* Should have a DIB section already. */
1767 if (!(surface->flags & SFLAG_DIBSECTION))
1769 WARN("DC not supported on this surface\n");
1770 return WINED3DERR_INVALIDCALL;
1773 /* Map the surface. */
1774 hr = wined3d_surface_map(surface, &lock, NULL, 0);
1776 ERR("Map failed, hr %#x.\n", hr);
1781 static HRESULT gdi_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override)
1783 TRACE("surface %p, override %p.\n", surface, override);
1788 static HRESULT gdi_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
1789 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
1790 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1792 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1793 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
1794 flags, fx, debug_d3dtexturefiltertype(filter));
1796 return surface_cpu_blt(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter);
1799 static HRESULT gdi_surface_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
1800 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD trans)
1802 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect %s, flags %#x.\n",
1803 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect), trans);
1805 return surface_cpu_bltfast(dst_surface, dst_x, dst_y, src_surface, src_rect, trans);
1808 static HRESULT gdi_surface_set_mem(struct wined3d_surface *surface, void *mem)
1810 TRACE("surface %p, mem %p.\n", surface, mem);
1812 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
1813 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1815 ERR("Not supported on render targets.\n");
1816 return WINED3DERR_INVALIDCALL;
1819 if (mem && mem != surface->resource.allocatedMemory)
1821 void *release = NULL;
1823 /* Do I have to copy the old surface content? */
1824 if (surface->flags & SFLAG_DIBSECTION)
1826 SelectObject(surface->hDC, surface->dib.holdbitmap);
1827 DeleteDC(surface->hDC);
1828 /* Release the DIB section. */
1829 DeleteObject(surface->dib.DIBsection);
1830 surface->dib.bitmap_data = NULL;
1831 surface->resource.allocatedMemory = NULL;
1832 surface->hDC = NULL;
1833 surface->flags &= ~SFLAG_DIBSECTION;
1835 else if (!(surface->flags & SFLAG_USERPTR))
1837 release = surface->resource.allocatedMemory;
1839 surface->resource.allocatedMemory = mem;
1840 surface->flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
1842 /* Now free the old memory, if any. */
1843 HeapFree(GetProcessHeap(), 0, release);
1845 else if (surface->flags & SFLAG_USERPTR)
1847 /* Map() and GetDC() will re-create the dib section and allocated memory. */
1848 surface->resource.allocatedMemory = NULL;
1849 surface->flags &= ~SFLAG_USERPTR;
1855 static const struct wined3d_surface_ops gdi_surface_ops =
1857 gdi_surface_private_setup,
1858 surface_gdi_cleanup,
1859 gdi_surface_realize_palette,
1860 gdi_surface_draw_overlay,
1861 gdi_surface_preload,
1867 gdi_surface_bltfast,
1868 gdi_surface_set_mem,
1871 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
1876 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
1880 name = &surface->texture_name_srgb;
1881 flag = SFLAG_INSRGBTEX;
1885 name = &surface->texture_name;
1886 flag = SFLAG_INTEXTURE;
1889 if (!*name && new_name)
1891 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
1892 * surface has no texture name yet. See if we can get rid of this. */
1893 if (surface->flags & flag)
1894 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
1895 surface_modify_location(surface, flag, FALSE);
1899 surface_force_reload(surface);
1902 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
1904 TRACE("surface %p, target %#x.\n", surface, target);
1906 if (surface->texture_target != target)
1908 if (target == GL_TEXTURE_RECTANGLE_ARB)
1910 surface->flags &= ~SFLAG_NORMCOORD;
1912 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
1914 surface->flags |= SFLAG_NORMCOORD;
1917 surface->texture_target = target;
1918 surface_force_reload(surface);
1921 /* Context activation is done by the caller. */
1922 void surface_bind(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
1924 TRACE("surface %p, gl_info %p, srgb %#x.\n", surface, gl_info, srgb);
1926 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
1928 struct wined3d_texture *texture = surface->container.u.texture;
1930 TRACE("Passing to container (%p).\n", texture);
1931 texture->texture_ops->texture_bind(texture, gl_info, srgb);
1935 if (surface->texture_level)
1937 ERR("Standalone surface %p is non-zero texture level %u.\n",
1938 surface, surface->texture_level);
1942 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
1946 if (!surface->texture_name)
1948 glGenTextures(1, &surface->texture_name);
1949 checkGLcall("glGenTextures");
1951 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
1953 glBindTexture(surface->texture_target, surface->texture_name);
1954 checkGLcall("glBindTexture");
1955 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1956 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1957 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
1958 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1959 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1960 checkGLcall("glTexParameteri");
1964 glBindTexture(surface->texture_target, surface->texture_name);
1965 checkGLcall("glBindTexture");
1972 /* This function checks if the primary render target uses the 8bit paletted format. */
1973 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1975 if (device->fb.render_targets && device->fb.render_targets[0])
1977 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1978 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1979 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1985 /* This call just downloads data, the caller is responsible for binding the
1986 * correct texture. */
1987 /* Context activation is done by the caller. */
1988 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1990 const struct wined3d_format *format = surface->resource.format;
1992 /* Only support read back of converted P8 surfaces. */
1993 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1995 FIXME("Readback conversion not supported for format %s.\n", debug_d3dformat(format->id));
2001 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2003 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2004 surface, surface->texture_level, format->glFormat, format->glType,
2005 surface->resource.allocatedMemory);
2007 if (surface->flags & SFLAG_PBO)
2009 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2010 checkGLcall("glBindBufferARB");
2011 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2012 checkGLcall("glGetCompressedTexImageARB");
2013 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2014 checkGLcall("glBindBufferARB");
2018 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2019 surface->texture_level, surface->resource.allocatedMemory));
2020 checkGLcall("glGetCompressedTexImageARB");
2028 GLenum gl_format = format->glFormat;
2029 GLenum gl_type = format->glType;
2033 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2034 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2036 gl_format = GL_ALPHA;
2037 gl_type = GL_UNSIGNED_BYTE;
2040 if (surface->flags & SFLAG_NONPOW2)
2042 unsigned char alignment = surface->resource.device->surface_alignment;
2043 src_pitch = format->byte_count * surface->pow2Width;
2044 dst_pitch = wined3d_surface_get_pitch(surface);
2045 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2046 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2050 mem = surface->resource.allocatedMemory;
2053 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2054 surface, surface->texture_level, gl_format, gl_type, mem);
2056 if (surface->flags & SFLAG_PBO)
2058 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2059 checkGLcall("glBindBufferARB");
2061 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2062 checkGLcall("glGetTexImage");
2064 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2065 checkGLcall("glBindBufferARB");
2069 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2070 checkGLcall("glGetTexImage");
2074 if (surface->flags & SFLAG_NONPOW2)
2076 const BYTE *src_data;
2080 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2081 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2082 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2084 * We're doing this...
2086 * instead of boxing the texture :
2087 * |<-texture width ->| -->pow2width| /\
2088 * |111111111111111111| | |
2089 * |222 Texture 222222| boxed empty | texture height
2090 * |3333 Data 33333333| | |
2091 * |444444444444444444| | \/
2092 * ----------------------------------- |
2093 * | boxed empty | boxed empty | pow2height
2095 * -----------------------------------
2098 * we're repacking the data to the expected texture width
2100 * |<-texture width ->| -->pow2width| /\
2101 * |111111111111111111222222222222222| |
2102 * |222333333333333333333444444444444| texture height
2106 * | empty | pow2height
2108 * -----------------------------------
2112 * |<-texture width ->| /\
2113 * |111111111111111111|
2114 * |222222222222222222|texture height
2115 * |333333333333333333|
2116 * |444444444444444444| \/
2117 * --------------------
2119 * this also means that any references to allocatedMemory should work with the data as if were a
2120 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2122 * internally the texture is still stored in a boxed format so any references to textureName will
2123 * get a boxed texture with width pow2width and not a texture of width resource.width.
2125 * Performance should not be an issue, because applications normally do not lock the surfaces when
2126 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2127 * and doesn't have to be re-read. */
2129 dst_data = surface->resource.allocatedMemory;
2130 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2131 for (y = 1; y < surface->resource.height; ++y)
2133 /* skip the first row */
2134 src_data += src_pitch;
2135 dst_data += dst_pitch;
2136 memcpy(dst_data, src_data, dst_pitch);
2139 HeapFree(GetProcessHeap(), 0, mem);
2143 /* Surface has now been downloaded */
2144 surface->flags |= SFLAG_INSYSMEM;
2147 /* This call just uploads data, the caller is responsible for binding the
2148 * correct texture. */
2149 /* Context activation is done by the caller. */
2150 void surface_upload_data(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2151 const struct wined3d_format *format, const RECT *src_rect, UINT src_w, const POINT *dst_point,
2152 BOOL srgb, const struct wined3d_bo_address *data)
2154 UINT update_w = src_rect->right - src_rect->left;
2155 UINT update_h = src_rect->bottom - src_rect->top;
2157 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_w %u, dst_point %p, srgb %#x, data {%#x:%p}.\n",
2158 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_w,
2159 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2161 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2162 update_h *= format->heightscale;
2166 if (data->buffer_object)
2168 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2169 checkGLcall("glBindBufferARB");
2172 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2174 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2175 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2176 UINT src_pitch = wined3d_format_calculate_size(format, 1, src_w, 1);
2177 const BYTE *addr = data->addr;
2180 addr += (src_rect->top / format->block_height) * src_pitch;
2181 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2184 internal = format->glGammaInternal;
2185 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2186 internal = format->rtInternal;
2188 internal = format->glInternal;
2190 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2191 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2192 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2194 if (row_length == src_pitch)
2196 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2197 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2203 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2204 * can't use the unpack row length like below. */
2205 for (row = 0, y = dst_point->y; row < row_count; ++row)
2207 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2208 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2209 y += format->block_height;
2213 checkGLcall("glCompressedTexSubImage2DARB");
2217 const BYTE *addr = data->addr;
2219 addr += src_rect->top * src_w * format->byte_count;
2220 addr += src_rect->left * format->byte_count;
2222 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2223 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2224 update_w, update_h, format->glFormat, format->glType, addr);
2226 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_w);
2227 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2228 update_w, update_h, format->glFormat, format->glType, addr);
2229 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2230 checkGLcall("glTexSubImage2D");
2233 if (data->buffer_object)
2235 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2236 checkGLcall("glBindBufferARB");
2241 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2243 struct wined3d_device *device = surface->resource.device;
2246 for (i = 0; i < device->context_count; ++i)
2248 context_surface_update(device->contexts[i], surface);
2253 /* This call just allocates the texture, the caller is responsible for binding
2254 * the correct texture. */
2255 /* Context activation is done by the caller. */
2256 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2257 const struct wined3d_format *format, BOOL srgb)
2259 BOOL enable_client_storage = FALSE;
2260 GLsizei width = surface->pow2Width;
2261 GLsizei height = surface->pow2Height;
2262 const BYTE *mem = NULL;
2267 internal = format->glGammaInternal;
2269 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2271 internal = format->rtInternal;
2275 internal = format->glInternal;
2278 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2280 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",
2281 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2282 internal, width, height, format->glFormat, format->glType);
2286 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2288 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2289 || !surface->resource.allocatedMemory)
2291 /* In some cases we want to disable client storage.
2292 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2293 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2294 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2295 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2297 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2298 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2299 surface->flags &= ~SFLAG_CLIENT;
2300 enable_client_storage = TRUE;
2304 surface->flags |= SFLAG_CLIENT;
2306 /* Point OpenGL to our allocated texture memory. Do not use
2307 * resource.allocatedMemory here because it might point into a
2308 * PBO. Instead use heapMemory, but get the alignment right. */
2309 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2310 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2314 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2316 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2317 internal, width, height, 0, surface->resource.size, mem));
2318 checkGLcall("glCompressedTexImage2DARB");
2322 glTexImage2D(surface->texture_target, surface->texture_level,
2323 internal, width, height, 0, format->glFormat, format->glType, mem);
2324 checkGLcall("glTexImage2D");
2327 if(enable_client_storage) {
2328 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2329 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2334 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2335 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2336 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2337 /* GL locking is done by the caller */
2338 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2340 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2341 struct wined3d_renderbuffer_entry *entry;
2342 GLuint renderbuffer = 0;
2343 unsigned int src_width, src_height;
2344 unsigned int width, height;
2346 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2348 width = rt->pow2Width;
2349 height = rt->pow2Height;
2353 width = surface->pow2Width;
2354 height = surface->pow2Height;
2357 src_width = surface->pow2Width;
2358 src_height = surface->pow2Height;
2360 /* A depth stencil smaller than the render target is not valid */
2361 if (width > src_width || height > src_height) return;
2363 /* Remove any renderbuffer set if the sizes match */
2364 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2365 || (width == src_width && height == src_height))
2367 surface->current_renderbuffer = NULL;
2371 /* Look if we've already got a renderbuffer of the correct dimensions */
2372 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2374 if (entry->width == width && entry->height == height)
2376 renderbuffer = entry->id;
2377 surface->current_renderbuffer = entry;
2384 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2385 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2386 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2387 surface->resource.format->glInternal, width, height);
2389 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2390 entry->width = width;
2391 entry->height = height;
2392 entry->id = renderbuffer;
2393 list_add_head(&surface->renderbuffers, &entry->entry);
2395 surface->current_renderbuffer = entry;
2398 checkGLcall("set_compatible_renderbuffer");
2401 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2403 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2405 TRACE("surface %p.\n", surface);
2407 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2409 ERR("Surface %p is not on a swapchain.\n", surface);
2413 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2415 if (swapchain->render_to_fbo)
2417 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2418 return GL_COLOR_ATTACHMENT0;
2420 TRACE("Returning GL_BACK\n");
2423 else if (surface == swapchain->front_buffer)
2425 TRACE("Returning GL_FRONT\n");
2429 FIXME("Higher back buffer, returning GL_BACK\n");
2433 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2434 void surface_add_dirty_rect(struct wined3d_surface *surface, const WINED3DBOX *dirty_rect)
2436 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2438 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2439 /* No partial locking for textures yet. */
2440 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2442 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2445 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->Left);
2446 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->Top);
2447 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->Right);
2448 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->Bottom);
2452 surface->dirtyRect.left = 0;
2453 surface->dirtyRect.top = 0;
2454 surface->dirtyRect.right = surface->resource.width;
2455 surface->dirtyRect.bottom = surface->resource.height;
2458 /* if the container is a texture then mark it dirty. */
2459 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2461 TRACE("Passing to container.\n");
2462 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2466 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
2467 DWORD color, WINED3DCOLORVALUE *float_color)
2469 const struct wined3d_format *format = surface->resource.format;
2470 const struct wined3d_device *device = surface->resource.device;
2474 case WINED3DFMT_P8_UINT:
2475 if (surface->palette)
2477 float_color->r = surface->palette->palents[color].peRed / 255.0f;
2478 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
2479 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
2483 float_color->r = 0.0f;
2484 float_color->g = 0.0f;
2485 float_color->b = 0.0f;
2487 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
2490 case WINED3DFMT_B5G6R5_UNORM:
2491 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
2492 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
2493 float_color->b = (color & 0x1f) / 31.0f;
2494 float_color->a = 1.0f;
2497 case WINED3DFMT_B8G8R8_UNORM:
2498 case WINED3DFMT_B8G8R8X8_UNORM:
2499 float_color->r = D3DCOLOR_R(color);
2500 float_color->g = D3DCOLOR_G(color);
2501 float_color->b = D3DCOLOR_B(color);
2502 float_color->a = 1.0f;
2505 case WINED3DFMT_B8G8R8A8_UNORM:
2506 float_color->r = D3DCOLOR_R(color);
2507 float_color->g = D3DCOLOR_G(color);
2508 float_color->b = D3DCOLOR_B(color);
2509 float_color->a = D3DCOLOR_A(color);
2513 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
2520 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2522 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2524 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2526 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2528 ERR("Not supported on scratch surfaces.\n");
2529 return WINED3DERR_INVALIDCALL;
2532 if (!(surface->flags & flag))
2534 TRACE("Reloading because surface is dirty\n");
2536 /* Reload if either the texture and sysmem have different ideas about the
2537 * color key, or the actual key values changed. */
2538 else if (!(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2539 || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2540 && (surface->glCKey.dwColorSpaceLowValue != surface->SrcBltCKey.dwColorSpaceLowValue
2541 || surface->glCKey.dwColorSpaceHighValue != surface->SrcBltCKey.dwColorSpaceHighValue)))
2543 TRACE("Reloading because of color keying\n");
2544 /* To perform the color key conversion we need a sysmem copy of
2545 * the surface. Make sure we have it. */
2547 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2548 /* Make sure the texture is reloaded because of the color key change,
2549 * this kills performance though :( */
2550 /* TODO: This is not necessarily needed with hw palettized texture support. */
2551 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2555 TRACE("surface is already in texture\n");
2559 /* No partial locking for textures yet. */
2560 surface_load_location(surface, flag, NULL);
2561 surface_evict_sysmem(surface);
2566 /* See also float_16_to_32() in wined3d_private.h */
2567 static inline unsigned short float_32_to_16(const float *in)
2570 float tmp = fabsf(*in);
2571 unsigned int mantissa;
2574 /* Deal with special numbers */
2580 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2582 if (tmp < powf(2, 10))
2588 } while (tmp < powf(2, 10));
2590 else if (tmp >= powf(2, 11))
2596 } while (tmp >= powf(2, 11));
2599 mantissa = (unsigned int)tmp;
2600 if (tmp - mantissa >= 0.5f)
2601 ++mantissa; /* Round to nearest, away from zero. */
2603 exp += 10; /* Normalize the mantissa. */
2604 exp += 15; /* Exponent is encoded with excess 15. */
2606 if (exp > 30) /* too big */
2608 ret = 0x7c00; /* INF */
2612 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2615 mantissa = mantissa >> 1;
2618 ret = mantissa & 0x3ff;
2622 ret = (exp << 10) | (mantissa & 0x3ff);
2625 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2629 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2633 TRACE("Surface %p, container %p of type %#x.\n",
2634 surface, surface->container.u.base, surface->container.type);
2636 switch (surface->container.type)
2638 case WINED3D_CONTAINER_TEXTURE:
2639 return wined3d_texture_incref(surface->container.u.texture);
2641 case WINED3D_CONTAINER_SWAPCHAIN:
2642 return wined3d_swapchain_incref(surface->container.u.swapchain);
2645 ERR("Unhandled container type %#x.\n", surface->container.type);
2646 case WINED3D_CONTAINER_NONE:
2650 refcount = InterlockedIncrement(&surface->resource.ref);
2651 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2656 /* Do not call while under the GL lock. */
2657 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2661 TRACE("Surface %p, container %p of type %#x.\n",
2662 surface, surface->container.u.base, surface->container.type);
2664 switch (surface->container.type)
2666 case WINED3D_CONTAINER_TEXTURE:
2667 return wined3d_texture_decref(surface->container.u.texture);
2669 case WINED3D_CONTAINER_SWAPCHAIN:
2670 return wined3d_swapchain_decref(surface->container.u.swapchain);
2673 ERR("Unhandled container type %#x.\n", surface->container.type);
2674 case WINED3D_CONTAINER_NONE:
2678 refcount = InterlockedDecrement(&surface->resource.ref);
2679 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2683 surface->surface_ops->surface_cleanup(surface);
2684 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2686 TRACE("Destroyed surface %p.\n", surface);
2687 HeapFree(GetProcessHeap(), 0, surface);
2693 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2695 return resource_set_priority(&surface->resource, priority);
2698 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2700 return resource_get_priority(&surface->resource);
2703 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2705 TRACE("surface %p.\n", surface);
2707 surface->surface_ops->surface_preload(surface);
2710 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2712 TRACE("surface %p.\n", surface);
2714 return surface->resource.parent;
2717 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2719 TRACE("surface %p.\n", surface);
2721 return &surface->resource;
2724 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2726 TRACE("surface %p, flags %#x.\n", surface, flags);
2730 case WINEDDGBS_CANBLT:
2731 case WINEDDGBS_ISBLTDONE:
2735 return WINED3DERR_INVALIDCALL;
2739 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2741 TRACE("surface %p, flags %#x.\n", surface, flags);
2743 /* XXX: DDERR_INVALIDSURFACETYPE */
2747 case WINEDDGFS_CANFLIP:
2748 case WINEDDGFS_ISFLIPDONE:
2752 return WINED3DERR_INVALIDCALL;
2756 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2758 TRACE("surface %p.\n", surface);
2760 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2761 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2764 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2766 TRACE("surface %p.\n", surface);
2768 /* So far we don't lose anything :) */
2769 surface->flags &= ~SFLAG_LOST;
2773 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2775 TRACE("surface %p, palette %p.\n", surface, palette);
2777 if (surface->palette == palette)
2779 TRACE("Nop palette change.\n");
2783 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2784 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2786 surface->palette = palette;
2790 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2791 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
2793 surface->surface_ops->surface_realize_palette(surface);
2799 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
2800 DWORD flags, const WINEDDCOLORKEY *color_key)
2802 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
2804 if (flags & WINEDDCKEY_COLORSPACE)
2806 FIXME(" colorkey value not supported (%08x) !\n", flags);
2807 return WINED3DERR_INVALIDCALL;
2810 /* Dirtify the surface, but only if a key was changed. */
2813 switch (flags & ~WINEDDCKEY_COLORSPACE)
2815 case WINEDDCKEY_DESTBLT:
2816 surface->DestBltCKey = *color_key;
2817 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
2820 case WINEDDCKEY_DESTOVERLAY:
2821 surface->DestOverlayCKey = *color_key;
2822 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
2825 case WINEDDCKEY_SRCOVERLAY:
2826 surface->SrcOverlayCKey = *color_key;
2827 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
2830 case WINEDDCKEY_SRCBLT:
2831 surface->SrcBltCKey = *color_key;
2832 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
2838 switch (flags & ~WINEDDCKEY_COLORSPACE)
2840 case WINEDDCKEY_DESTBLT:
2841 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
2844 case WINEDDCKEY_DESTOVERLAY:
2845 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
2848 case WINEDDCKEY_SRCOVERLAY:
2849 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
2852 case WINEDDCKEY_SRCBLT:
2853 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
2861 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
2863 TRACE("surface %p.\n", surface);
2865 return surface->palette;
2868 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2870 const struct wined3d_format *format = surface->resource.format;
2873 TRACE("surface %p.\n", surface);
2875 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
2877 /* Since compressed formats are block based, pitch means the amount of
2878 * bytes to the next row of block rather than the next row of pixels. */
2879 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
2880 pitch = row_block_count * format->block_byte_count;
2884 unsigned char alignment = surface->resource.device->surface_alignment;
2885 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
2886 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2889 TRACE("Returning %u.\n", pitch);
2894 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
2896 TRACE("surface %p, mem %p.\n", surface, mem);
2898 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
2900 WARN("Surface is locked or the DC is in use.\n");
2901 return WINED3DERR_INVALIDCALL;
2904 return surface->surface_ops->surface_set_mem(surface, mem);
2907 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2911 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2913 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2915 WARN("Not an overlay surface.\n");
2916 return WINEDDERR_NOTAOVERLAYSURFACE;
2919 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2920 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2921 surface->overlay_destrect.left = x;
2922 surface->overlay_destrect.top = y;
2923 surface->overlay_destrect.right = x + w;
2924 surface->overlay_destrect.bottom = y + h;
2926 surface->surface_ops->surface_draw_overlay(surface);
2931 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2933 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2935 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2937 TRACE("Not an overlay surface.\n");
2938 return WINEDDERR_NOTAOVERLAYSURFACE;
2941 if (!surface->overlay_dest)
2943 TRACE("Overlay not visible.\n");
2946 return WINEDDERR_OVERLAYNOTVISIBLE;
2949 *x = surface->overlay_destrect.left;
2950 *y = surface->overlay_destrect.top;
2952 TRACE("Returning position %d, %d.\n", *x, *y);
2957 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2958 DWORD flags, struct wined3d_surface *ref)
2960 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2962 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2964 TRACE("Not an overlay surface.\n");
2965 return WINEDDERR_NOTAOVERLAYSURFACE;
2971 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2972 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2974 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2975 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2977 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2979 WARN("Not an overlay surface.\n");
2980 return WINEDDERR_NOTAOVERLAYSURFACE;
2982 else if (!dst_surface)
2984 WARN("Dest surface is NULL.\n");
2985 return WINED3DERR_INVALIDCALL;
2990 surface->overlay_srcrect = *src_rect;
2994 surface->overlay_srcrect.left = 0;
2995 surface->overlay_srcrect.top = 0;
2996 surface->overlay_srcrect.right = surface->resource.width;
2997 surface->overlay_srcrect.bottom = surface->resource.height;
3002 surface->overlay_destrect = *dst_rect;
3006 surface->overlay_destrect.left = 0;
3007 surface->overlay_destrect.top = 0;
3008 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3009 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3012 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3014 list_remove(&surface->overlay_entry);
3017 if (flags & WINEDDOVER_SHOW)
3019 if (surface->overlay_dest != dst_surface)
3021 surface->overlay_dest = dst_surface;
3022 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3025 else if (flags & WINEDDOVER_HIDE)
3027 /* tests show that the rectangles are erased on hide */
3028 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3029 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3030 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3031 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3032 surface->overlay_dest = NULL;
3035 surface->surface_ops->surface_draw_overlay(surface);
3040 HRESULT CDECL wined3d_surface_set_clipper(struct wined3d_surface *surface, struct wined3d_clipper *clipper)
3042 TRACE("surface %p, clipper %p.\n", surface, clipper);
3044 surface->clipper = clipper;
3049 struct wined3d_clipper * CDECL wined3d_surface_get_clipper(const struct wined3d_surface *surface)
3051 TRACE("surface %p.\n", surface);
3053 return surface->clipper;
3056 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3058 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3060 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3062 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3064 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3065 return WINED3DERR_INVALIDCALL;
3068 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3069 surface->pow2Width, surface->pow2Height);
3070 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3071 surface->resource.format = format;
3073 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3074 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3075 format->glFormat, format->glInternal, format->glType);
3080 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3081 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3083 unsigned short *dst_s;
3087 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3089 for (y = 0; y < h; ++y)
3091 src_f = (const float *)(src + y * pitch_in);
3092 dst_s = (unsigned short *) (dst + y * pitch_out);
3093 for (x = 0; x < w; ++x)
3095 dst_s[x] = float_32_to_16(src_f + x);
3100 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3101 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3103 static const unsigned char convert_5to8[] =
3105 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3106 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3107 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3108 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3110 static const unsigned char convert_6to8[] =
3112 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3113 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3114 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3115 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3116 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3117 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3118 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3119 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3123 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3125 for (y = 0; y < h; ++y)
3127 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3128 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3129 for (x = 0; x < w; ++x)
3131 WORD pixel = src_line[x];
3132 dst_line[x] = 0xff000000
3133 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3134 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3135 | convert_5to8[(pixel & 0x001f)];
3140 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3141 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3145 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3147 for (y = 0; y < h; ++y)
3149 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3150 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3152 for (x = 0; x < w; ++x)
3154 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3159 static inline BYTE cliptobyte(int x)
3161 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3164 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3165 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3167 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3170 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3172 for (y = 0; y < h; ++y)
3174 const BYTE *src_line = src + y * pitch_in;
3175 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3176 for (x = 0; x < w; ++x)
3178 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3179 * C = Y - 16; D = U - 128; E = V - 128;
3180 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3181 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3182 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3183 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3184 * U and V are shared between the pixels. */
3185 if (!(x & 1)) /* For every even pixel, read new U and V. */
3187 d = (int) src_line[1] - 128;
3188 e = (int) src_line[3] - 128;
3190 g2 = - 100 * d - 208 * e + 128;
3193 c2 = 298 * ((int) src_line[0] - 16);
3194 dst_line[x] = 0xff000000
3195 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3196 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3197 | cliptobyte((c2 + b2) >> 8); /* blue */
3198 /* Scale RGB values to 0..255 range,
3199 * then clip them if still not in range (may be negative),
3200 * then shift them within DWORD if necessary. */
3206 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3207 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3210 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3212 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3214 for (y = 0; y < h; ++y)
3216 const BYTE *src_line = src + y * pitch_in;
3217 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3218 for (x = 0; x < w; ++x)
3220 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3221 * C = Y - 16; D = U - 128; E = V - 128;
3222 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3223 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3224 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3225 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3226 * U and V are shared between the pixels. */
3227 if (!(x & 1)) /* For every even pixel, read new U and V. */
3229 d = (int) src_line[1] - 128;
3230 e = (int) src_line[3] - 128;
3232 g2 = - 100 * d - 208 * e + 128;
3235 c2 = 298 * ((int) src_line[0] - 16);
3236 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3237 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3238 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3239 /* Scale RGB values to 0..255 range,
3240 * then clip them if still not in range (may be negative),
3241 * then shift them within DWORD if necessary. */
3247 struct d3dfmt_convertor_desc
3249 enum wined3d_format_id from, to;
3250 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3253 static const struct d3dfmt_convertor_desc convertors[] =
3255 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3256 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3257 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3258 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3259 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3262 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3263 enum wined3d_format_id to)
3267 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3269 if (convertors[i].from == from && convertors[i].to == to)
3270 return &convertors[i];
3276 /*****************************************************************************
3277 * surface_convert_format
3279 * Creates a duplicate of a surface in a different format. Is used by Blt to
3280 * blit between surfaces with different formats.
3283 * source: Source surface
3284 * fmt: Requested destination format
3286 *****************************************************************************/
3287 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3289 const struct d3dfmt_convertor_desc *conv;
3290 WINED3DLOCKED_RECT lock_src, lock_dst;
3291 struct wined3d_surface *ret = NULL;
3294 conv = find_convertor(source->resource.format->id, to_fmt);
3297 FIXME("Cannot find a conversion function from format %s to %s.\n",
3298 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3302 wined3d_surface_create(source->resource.device, source->resource.width,
3303 source->resource.height, to_fmt, TRUE /* lockable */, TRUE /* discard */, 0 /* level */,
3304 0 /* usage */, WINED3DPOOL_SCRATCH, WINED3DMULTISAMPLE_NONE /* TODO: Multisampled conversion */,
3305 0 /* MultiSampleQuality */, source->surface_type, NULL /* parent */, &wined3d_null_parent_ops, &ret);
3308 ERR("Failed to create a destination surface for conversion.\n");
3312 memset(&lock_src, 0, sizeof(lock_src));
3313 memset(&lock_dst, 0, sizeof(lock_dst));
3315 hr = wined3d_surface_map(source, &lock_src, NULL, WINED3DLOCK_READONLY);
3318 ERR("Failed to lock the source surface.\n");
3319 wined3d_surface_decref(ret);
3322 hr = wined3d_surface_map(ret, &lock_dst, NULL, WINED3DLOCK_READONLY);
3325 ERR("Failed to lock the destination surface.\n");
3326 wined3d_surface_unmap(source);
3327 wined3d_surface_decref(ret);
3331 conv->convert(lock_src.pBits, lock_dst.pBits, lock_src.Pitch, lock_dst.Pitch,
3332 source->resource.width, source->resource.height);
3334 wined3d_surface_unmap(ret);
3335 wined3d_surface_unmap(source);
3340 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3341 unsigned int bpp, UINT pitch, DWORD color)
3348 #define COLORFILL_ROW(type) \
3350 type *d = (type *)buf; \
3351 for (x = 0; x < width; ++x) \
3352 d[x] = (type)color; \
3358 COLORFILL_ROW(BYTE);
3362 COLORFILL_ROW(WORD);
3368 for (x = 0; x < width; ++x, d += 3)
3370 d[0] = (color ) & 0xFF;
3371 d[1] = (color >> 8) & 0xFF;
3372 d[2] = (color >> 16) & 0xFF;
3377 COLORFILL_ROW(DWORD);
3381 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3382 return WINED3DERR_NOTAVAILABLE;
3385 #undef COLORFILL_ROW
3387 /* Now copy first row. */
3389 for (y = 1; y < height; ++y)
3392 memcpy(buf, first, width * bpp);
3398 /* Do not call while under the GL lock. */
3399 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
3400 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
3401 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
3403 RECT src_rect, dst_rect;
3405 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
3406 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
3407 flags, fx, debug_d3dtexturefiltertype(filter));
3409 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
3411 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
3412 return WINEDDERR_SURFACEBUSY;
3415 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
3417 surface_get_rect(src_surface, src_rect_in, &src_rect);
3419 memset(&src_rect, 0, sizeof(src_rect));
3421 return dst_surface->surface_ops->surface_blt(dst_surface,
3422 &dst_rect, src_surface, &src_rect, flags, fx, filter);
3425 /* Do not call while under the GL lock. */
3426 HRESULT CDECL wined3d_surface_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
3427 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD trans)
3429 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect %s, trans %#x.\n",
3430 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect), trans);
3432 return dst_surface->surface_ops->surface_bltfast(dst_surface,
3433 dst_x, dst_y, src_surface, src_rect, trans);
3436 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3438 TRACE("surface %p.\n", surface);
3440 if (!(surface->flags & SFLAG_LOCKED))
3442 WARN("Trying to unmap unmapped surface.\n");
3443 return WINEDDERR_NOTLOCKED;
3445 surface->flags &= ~SFLAG_LOCKED;
3447 surface->surface_ops->surface_unmap(surface);
3452 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3453 WINED3DLOCKED_RECT *locked_rect, const RECT *rect, DWORD flags)
3455 TRACE("surface %p, locked_rect %p, rect %s, flags %#x.\n",
3456 surface, locked_rect, wine_dbgstr_rect(rect), flags);
3458 if (surface->flags & SFLAG_LOCKED)
3460 WARN("Surface is already mapped.\n");
3461 return WINED3DERR_INVALIDCALL;
3463 surface->flags |= SFLAG_LOCKED;
3465 if (!(surface->flags & SFLAG_LOCKABLE))
3466 WARN("Trying to lock unlockable surface.\n");
3468 surface->surface_ops->surface_map(surface, rect, flags);
3470 locked_rect->Pitch = wined3d_surface_get_pitch(surface);
3474 locked_rect->pBits = surface->resource.allocatedMemory;
3475 surface->lockedRect.left = 0;
3476 surface->lockedRect.top = 0;
3477 surface->lockedRect.right = surface->resource.width;
3478 surface->lockedRect.bottom = surface->resource.height;
3482 const struct wined3d_format *format = surface->resource.format;
3484 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3486 /* Compressed textures are block based, so calculate the offset of
3487 * the block that contains the top-left pixel of the locked rectangle. */
3488 locked_rect->pBits = surface->resource.allocatedMemory
3489 + ((rect->top / format->block_height) * locked_rect->Pitch)
3490 + ((rect->left / format->block_width) * format->block_byte_count);
3494 locked_rect->pBits = surface->resource.allocatedMemory
3495 + (locked_rect->Pitch * rect->top)
3496 + (rect->left * format->byte_count);
3498 surface->lockedRect.left = rect->left;
3499 surface->lockedRect.top = rect->top;
3500 surface->lockedRect.right = rect->right;
3501 surface->lockedRect.bottom = rect->bottom;
3504 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3505 TRACE("Returning memory %p, pitch %u.\n", locked_rect->pBits, locked_rect->Pitch);
3510 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3514 TRACE("surface %p, dc %p.\n", surface, dc);
3516 if (surface->flags & SFLAG_USERPTR)
3518 ERR("Not supported on surfaces with application-provided memory.\n");
3519 return WINEDDERR_NODC;
3522 /* Give more detailed info for ddraw. */
3523 if (surface->flags & SFLAG_DCINUSE)
3524 return WINEDDERR_DCALREADYCREATED;
3526 /* Can't GetDC if the surface is locked. */
3527 if (surface->flags & SFLAG_LOCKED)
3528 return WINED3DERR_INVALIDCALL;
3530 hr = surface->surface_ops->surface_getdc(surface);
3534 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3535 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3537 /* GetDC on palettized formats is unsupported in D3D9, and the method
3538 * is missing in D3D8, so this should only be used for DX <=7
3539 * surfaces (with non-device palettes). */
3540 const PALETTEENTRY *pal = NULL;
3542 if (surface->palette)
3544 pal = surface->palette->palents;
3548 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3549 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3551 if (dds_primary && dds_primary->palette)
3552 pal = dds_primary->palette->palents;
3560 for (i = 0; i < 256; ++i)
3562 col[i].rgbRed = pal[i].peRed;
3563 col[i].rgbGreen = pal[i].peGreen;
3564 col[i].rgbBlue = pal[i].peBlue;
3565 col[i].rgbReserved = 0;
3567 SetDIBColorTable(surface->hDC, 0, 256, col);
3571 surface->flags |= SFLAG_DCINUSE;
3574 TRACE("Returning dc %p.\n", *dc);
3579 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3581 TRACE("surface %p, dc %p.\n", surface, dc);
3583 if (!(surface->flags & SFLAG_DCINUSE))
3584 return WINEDDERR_NODC;
3586 if (surface->hDC != dc)
3588 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3590 return WINEDDERR_NODC;
3593 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3595 /* Copy the contents of the DIB over to the PBO. */
3596 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->dib.bitmap_size);
3599 /* We locked first, so unlock now. */
3600 wined3d_surface_unmap(surface);
3602 surface->flags &= ~SFLAG_DCINUSE;
3607 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3609 struct wined3d_swapchain *swapchain;
3612 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3614 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
3616 ERR("Flipped surface is not on a swapchain.\n");
3617 return WINEDDERR_NOTFLIPPABLE;
3619 swapchain = surface->container.u.swapchain;
3621 hr = surface->surface_ops->surface_flip(surface, override);
3625 /* Just overwrite the swapchain presentation interval. This is ok because
3626 * only ddraw apps can call Flip, and only d3d8 and d3d9 applications
3627 * specify the presentation interval. */
3628 if (!(flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)))
3629 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
3630 else if (flags & WINEDDFLIP_NOVSYNC)
3631 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3632 else if (flags & WINEDDFLIP_INTERVAL2)
3633 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
3634 else if (flags & WINEDDFLIP_INTERVAL3)
3635 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
3637 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3639 return wined3d_swapchain_present(swapchain, NULL, NULL, swapchain->win_handle, NULL, 0);
3642 /* Do not call while under the GL lock. */
3643 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3645 struct wined3d_device *device = surface->resource.device;
3647 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3649 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3651 struct wined3d_texture *texture = surface->container.u.texture;
3653 TRACE("Passing to container (%p).\n", texture);
3654 texture->texture_ops->texture_preload(texture, srgb);
3658 struct wined3d_context *context = NULL;
3660 TRACE("(%p) : About to load surface\n", surface);
3662 if (!device->isInDraw) context = context_acquire(device, NULL);
3664 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3665 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3667 if (palette9_changed(surface))
3669 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
3670 /* TODO: This is not necessarily needed with hw palettized texture support */
3671 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3672 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
3673 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
3677 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3679 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3681 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3685 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3689 if (context) context_release(context);
3693 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3695 if (!surface->resource.allocatedMemory)
3697 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3698 surface->resource.size + RESOURCE_ALIGNMENT);
3699 if (!surface->resource.heapMemory)
3701 ERR("Out of memory\n");
3704 surface->resource.allocatedMemory =
3705 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
3709 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
3712 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3717 /* Read the framebuffer back into the surface */
3718 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
3720 struct wined3d_device *device = surface->resource.device;
3721 const struct wined3d_gl_info *gl_info;
3722 struct wined3d_context *context;
3726 BYTE *row, *top, *bottom;
3730 BOOL srcIsUpsideDown;
3735 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
3736 static BOOL warned = FALSE;
3738 ERR("The application tries to lock the render target, but render target locking is disabled\n");
3744 context = context_acquire(device, surface);
3745 context_apply_blit_state(context, device);
3746 gl_info = context->gl_info;
3750 /* Select the correct read buffer, and give some debug output.
3751 * There is no need to keep track of the current read buffer or reset it, every part of the code
3752 * that reads sets the read buffer as desired.
3754 if (surface_is_offscreen(surface))
3756 /* Mapping the primary render target which is not on a swapchain.
3757 * Read from the back buffer. */
3758 TRACE("Mapping offscreen render target.\n");
3759 glReadBuffer(device->offscreenBuffer);
3760 srcIsUpsideDown = TRUE;
3764 /* Onscreen surfaces are always part of a swapchain */
3765 GLenum buffer = surface_get_gl_buffer(surface);
3766 TRACE("Mapping %#x buffer.\n", buffer);
3767 glReadBuffer(buffer);
3768 checkGLcall("glReadBuffer");
3769 srcIsUpsideDown = FALSE;
3772 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
3775 local_rect.left = 0;
3777 local_rect.right = surface->resource.width;
3778 local_rect.bottom = surface->resource.height;
3784 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
3786 switch (surface->resource.format->id)
3788 case WINED3DFMT_P8_UINT:
3790 if (primary_render_target_is_p8(device))
3792 /* In case of P8 render targets the index is stored in the alpha component */
3794 type = GL_UNSIGNED_BYTE;
3796 bpp = surface->resource.format->byte_count;
3800 /* GL can't return palettized data, so read ARGB pixels into a
3801 * separate block of memory and convert them into palettized format
3802 * in software. Slow, but if the app means to use palettized render
3803 * targets and locks it...
3805 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
3806 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
3807 * for the color channels when palettizing the colors.
3810 type = GL_UNSIGNED_BYTE;
3812 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
3815 ERR("Out of memory\n");
3819 bpp = surface->resource.format->byte_count * 3;
3826 fmt = surface->resource.format->glFormat;
3827 type = surface->resource.format->glType;
3828 bpp = surface->resource.format->byte_count;
3831 if (surface->flags & SFLAG_PBO)
3833 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
3834 checkGLcall("glBindBufferARB");
3837 ERR("mem not null for pbo -- unexpected\n");
3842 /* Save old pixel store pack state */
3843 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
3844 checkGLcall("glGetIntegerv");
3845 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
3846 checkGLcall("glGetIntegerv");
3847 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
3848 checkGLcall("glGetIntegerv");
3850 /* Setup pixel store pack state -- to glReadPixels into the correct place */
3851 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
3852 checkGLcall("glPixelStorei");
3853 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
3854 checkGLcall("glPixelStorei");
3855 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
3856 checkGLcall("glPixelStorei");
3858 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
3859 local_rect.right - local_rect.left,
3860 local_rect.bottom - local_rect.top,
3862 checkGLcall("glReadPixels");
3864 /* Reset previous pixel store pack state */
3865 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
3866 checkGLcall("glPixelStorei");
3867 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
3868 checkGLcall("glPixelStorei");
3869 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
3870 checkGLcall("glPixelStorei");
3872 if (surface->flags & SFLAG_PBO)
3874 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
3875 checkGLcall("glBindBufferARB");
3877 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
3878 * to get a pointer to it and perform the flipping in software. This is a lot
3879 * faster than calling glReadPixels for each line. In case we want more speed
3880 * we should rerender it flipped in a FBO and read the data back from the FBO. */
3881 if (!srcIsUpsideDown)
3883 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
3884 checkGLcall("glBindBufferARB");
3886 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3887 checkGLcall("glMapBufferARB");
3891 /* TODO: Merge this with the palettization loop below for P8 targets */
3892 if(!srcIsUpsideDown) {
3894 /* glReadPixels returns the image upside down, and there is no way to prevent this.
3895 Flip the lines in software */
3896 len = (local_rect.right - local_rect.left) * bpp;
3897 off = local_rect.left * bpp;
3899 row = HeapAlloc(GetProcessHeap(), 0, len);
3901 ERR("Out of memory\n");
3902 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
3903 HeapFree(GetProcessHeap(), 0, mem);
3908 top = mem + pitch * local_rect.top;
3909 bottom = mem + pitch * (local_rect.bottom - 1);
3910 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
3911 memcpy(row, top + off, len);
3912 memcpy(top + off, bottom + off, len);
3913 memcpy(bottom + off, row, len);
3917 HeapFree(GetProcessHeap(), 0, row);
3919 /* Unmap the temp PBO buffer */
3920 if (surface->flags & SFLAG_PBO)
3922 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
3923 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
3928 context_release(context);
3930 /* For P8 textures we need to perform an inverse palette lookup. This is
3931 * done by searching for a palette index which matches the RGB value.
3932 * Note this isn't guaranteed to work when there are multiple entries for
3933 * the same color but we have no choice. In case of P8 render targets,
3934 * the index is stored in the alpha component so no conversion is needed. */
3935 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
3937 const PALETTEENTRY *pal = NULL;
3938 DWORD width = pitch / 3;
3941 if (surface->palette)
3943 pal = surface->palette->palents;
3947 ERR("Palette is missing, cannot perform inverse palette lookup\n");
3948 HeapFree(GetProcessHeap(), 0, mem);
3952 for(y = local_rect.top; y < local_rect.bottom; y++) {
3953 for(x = local_rect.left; x < local_rect.right; x++) {
3954 /* start lines pixels */
3955 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
3956 const BYTE *green = blue + 1;
3957 const BYTE *red = green + 1;
3959 for(c = 0; c < 256; c++) {
3960 if(*red == pal[c].peRed &&
3961 *green == pal[c].peGreen &&
3962 *blue == pal[c].peBlue)
3964 *((BYTE *) dest + y * width + x) = c;
3970 HeapFree(GetProcessHeap(), 0, mem);
3974 /* Read the framebuffer contents into a texture */
3975 static void read_from_framebuffer_texture(struct wined3d_surface *surface, BOOL srgb)
3977 struct wined3d_device *device = surface->resource.device;
3978 const struct wined3d_gl_info *gl_info;
3979 struct wined3d_context *context;
3981 if (!surface_is_offscreen(surface))
3983 /* We would need to flip onscreen surfaces, but there's no efficient
3984 * way to do that here. It makes more sense for the caller to
3985 * explicitly go through sysmem. */
3986 ERR("Not supported for onscreen targets.\n");
3990 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
3991 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
3992 * states in the stateblock, and no driver was found yet that had bugs in that regard.
3994 context = context_acquire(device, surface);
3995 gl_info = context->gl_info;
3996 device_invalidate_state(device, STATE_FRAMEBUFFER);
3998 surface_prepare_texture(surface, gl_info, srgb);
3999 surface_bind_and_dirtify(surface, gl_info, srgb);
4001 TRACE("Reading back offscreen render target %p.\n", surface);
4005 glReadBuffer(device->offscreenBuffer);
4006 checkGLcall("glReadBuffer");
4008 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4009 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4010 checkGLcall("glCopyTexSubImage2D");
4014 context_release(context);
4017 /* Context activation is done by the caller. */
4018 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4019 const struct wined3d_gl_info *gl_info, BOOL srgb)
4021 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4022 CONVERT_TYPES convert;
4023 struct wined3d_format format;
4025 if (surface->flags & alloc_flag) return;
4027 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4028 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4029 else surface->flags &= ~SFLAG_CONVERTED;
4031 surface_bind_and_dirtify(surface, gl_info, srgb);
4032 surface_allocate_surface(surface, gl_info, &format, srgb);
4033 surface->flags |= alloc_flag;
4036 /* Context activation is done by the caller. */
4037 void surface_prepare_texture(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
4039 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4041 struct wined3d_texture *texture = surface->container.u.texture;
4042 UINT sub_count = texture->level_count * texture->layer_count;
4045 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4047 for (i = 0; i < sub_count; ++i)
4049 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4050 surface_prepare_texture_internal(s, gl_info, srgb);
4056 surface_prepare_texture_internal(surface, gl_info, srgb);
4059 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4060 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4062 struct wined3d_device *device = surface->resource.device;
4063 UINT pitch = wined3d_surface_get_pitch(surface);
4064 const struct wined3d_gl_info *gl_info;
4065 struct wined3d_context *context;
4069 surface_get_rect(surface, rect, &local_rect);
4071 mem += local_rect.top * pitch + local_rect.left * bpp;
4072 w = local_rect.right - local_rect.left;
4073 h = local_rect.bottom - local_rect.top;
4075 /* Activate the correct context for the render target */
4076 context = context_acquire(device, surface);
4077 context_apply_blit_state(context, device);
4078 gl_info = context->gl_info;
4082 if (!surface_is_offscreen(surface))
4084 GLenum buffer = surface_get_gl_buffer(surface);
4085 TRACE("Unlocking %#x buffer.\n", buffer);
4086 context_set_draw_buffer(context, buffer);
4088 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4089 glPixelZoom(1.0f, -1.0f);
4093 /* Primary offscreen render target */
4094 TRACE("Offscreen render target.\n");
4095 context_set_draw_buffer(context, device->offscreenBuffer);
4097 glPixelZoom(1.0f, 1.0f);
4100 glRasterPos3i(local_rect.left, local_rect.top, 1);
4101 checkGLcall("glRasterPos3i");
4103 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4104 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4106 if (surface->flags & SFLAG_PBO)
4108 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4109 checkGLcall("glBindBufferARB");
4112 glDrawPixels(w, h, fmt, type, mem);
4113 checkGLcall("glDrawPixels");
4115 if (surface->flags & SFLAG_PBO)
4117 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4118 checkGLcall("glBindBufferARB");
4121 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4122 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4126 if (wined3d_settings.strict_draw_ordering
4127 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4128 && surface->container.u.swapchain->front_buffer == surface))
4131 context_release(context);
4134 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4135 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4137 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4138 const struct wined3d_device *device = surface->resource.device;
4139 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4140 BOOL blit_supported = FALSE;
4142 /* Copy the default values from the surface. Below we might perform fixups */
4143 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4144 *format = *surface->resource.format;
4145 *convert = NO_CONVERSION;
4147 /* Ok, now look if we have to do any conversion */
4148 switch (surface->resource.format->id)
4150 case WINED3DFMT_P8_UINT:
4151 /* Below the call to blit_supported is disabled for Wine 1.2
4152 * because the function isn't operating correctly yet. At the
4153 * moment 8-bit blits are handled in software and if certain GL
4154 * extensions are around, surface conversion is performed at
4155 * upload time. The blit_supported call recognizes it as a
4156 * destination fixup. This type of upload 'fixup' and 8-bit to
4157 * 8-bit blits need to be handled by the blit_shader.
4158 * TODO: get rid of this #if 0. */
4160 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4161 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4162 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4164 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4166 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4167 * texturing. Further also use conversion in case of color keying.
4168 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4169 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4170 * conflicts with this.
4172 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4173 || colorkey_active || !use_texturing)
4175 format->glFormat = GL_RGBA;
4176 format->glInternal = GL_RGBA;
4177 format->glType = GL_UNSIGNED_BYTE;
4178 format->conv_byte_count = 4;
4179 if (colorkey_active)
4180 *convert = CONVERT_PALETTED_CK;
4182 *convert = CONVERT_PALETTED;
4186 case WINED3DFMT_B2G3R3_UNORM:
4187 /* **********************
4188 GL_UNSIGNED_BYTE_3_3_2
4189 ********************** */
4190 if (colorkey_active) {
4191 /* This texture format will never be used.. So do not care about color keying
4192 up until the point in time it will be needed :-) */
4193 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4197 case WINED3DFMT_B5G6R5_UNORM:
4198 if (colorkey_active)
4200 *convert = CONVERT_CK_565;
4201 format->glFormat = GL_RGBA;
4202 format->glInternal = GL_RGB5_A1;
4203 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4204 format->conv_byte_count = 2;
4208 case WINED3DFMT_B5G5R5X1_UNORM:
4209 if (colorkey_active)
4211 *convert = CONVERT_CK_5551;
4212 format->glFormat = GL_BGRA;
4213 format->glInternal = GL_RGB5_A1;
4214 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4215 format->conv_byte_count = 2;
4219 case WINED3DFMT_B8G8R8_UNORM:
4220 if (colorkey_active)
4222 *convert = CONVERT_CK_RGB24;
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;
4230 case WINED3DFMT_B8G8R8X8_UNORM:
4231 if (colorkey_active)
4233 *convert = CONVERT_RGB32_888;
4234 format->glFormat = GL_RGBA;
4235 format->glInternal = GL_RGBA8;
4236 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4237 format->conv_byte_count = 4;
4248 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4250 const struct wined3d_device *device = surface->resource.device;
4251 const struct wined3d_palette *pal = surface->palette;
4252 BOOL index_in_alpha = FALSE;
4255 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4256 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4257 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4258 * duplicate entries. Store the color key in the unused alpha component to speed the
4259 * download up and to make conversion unneeded. */
4260 index_in_alpha = primary_render_target_is_p8(device);
4264 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
4265 if (device->wined3d->flags & WINED3D_PALETTE_PER_SURFACE)
4267 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4270 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4271 * there's no palette at this time. */
4272 for (i = 0; i < 256; i++) table[i][3] = i;
4277 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
4278 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
4279 * capability flag is present (wine does advertise this capability) */
4280 for (i = 0; i < 256; ++i)
4282 table[i][0] = device->palettes[device->currentPalette][i].peRed;
4283 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
4284 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
4285 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
4291 TRACE("Using surface palette %p\n", pal);
4292 /* Get the surface's palette */
4293 for (i = 0; i < 256; ++i)
4295 table[i][0] = pal->palents[i].peRed;
4296 table[i][1] = pal->palents[i].peGreen;
4297 table[i][2] = pal->palents[i].peBlue;
4299 /* When index_in_alpha is set the palette index is stored in the
4300 * alpha component. In case of a readback we can then read
4301 * GL_ALPHA. Color keying is handled in BltOverride using a
4302 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4303 * color key itself is passed to glAlphaFunc in other cases the
4304 * alpha component of pixels that should be masked away is set to 0. */
4309 else if (colorkey && (i >= surface->SrcBltCKey.dwColorSpaceLowValue)
4310 && (i <= surface->SrcBltCKey.dwColorSpaceHighValue))
4314 else if (pal->flags & WINEDDPCAPS_ALPHA)
4316 table[i][3] = pal->palents[i].peFlags;
4326 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4327 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4331 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4336 memcpy(dst, src, pitch * height);
4339 case CONVERT_PALETTED:
4340 case CONVERT_PALETTED_CK:
4345 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4347 for (y = 0; y < height; y++)
4349 source = src + pitch * y;
4350 dest = dst + outpitch * y;
4351 /* This is an 1 bpp format, using the width here is fine */
4352 for (x = 0; x < width; x++) {
4353 BYTE color = *source++;
4354 *dest++ = table[color][0];
4355 *dest++ = table[color][1];
4356 *dest++ = table[color][2];
4357 *dest++ = table[color][3];
4363 case CONVERT_CK_565:
4365 /* Converting the 565 format in 5551 packed to emulate color-keying.
4367 Note : in all these conversion, it would be best to average the averaging
4368 pixels to get the color of the pixel that will be color-keyed to
4369 prevent 'color bleeding'. This will be done later on if ever it is
4372 Note2: Nvidia documents say that their driver does not support alpha + color keying
4373 on the same surface and disables color keying in such a case
4379 TRACE("Color keyed 565\n");
4381 for (y = 0; y < height; y++) {
4382 Source = (const WORD *)(src + y * pitch);
4383 Dest = (WORD *) (dst + y * outpitch);
4384 for (x = 0; x < width; x++ ) {
4385 WORD color = *Source++;
4386 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4387 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4388 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4396 case CONVERT_CK_5551:
4398 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4402 TRACE("Color keyed 5551\n");
4403 for (y = 0; y < height; y++) {
4404 Source = (const WORD *)(src + y * pitch);
4405 Dest = (WORD *) (dst + y * outpitch);
4406 for (x = 0; x < width; x++ ) {
4407 WORD color = *Source++;
4409 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4410 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4413 *Dest &= ~(1 << 15);
4420 case CONVERT_CK_RGB24:
4422 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4424 for (y = 0; y < height; y++)
4426 source = src + pitch * y;
4427 dest = dst + outpitch * y;
4428 for (x = 0; x < width; x++) {
4429 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4430 DWORD dstcolor = color << 8;
4431 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4432 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4434 *(DWORD*)dest = dstcolor;
4442 case CONVERT_RGB32_888:
4444 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4446 for (y = 0; y < height; y++)
4448 source = src + pitch * y;
4449 dest = dst + outpitch * y;
4450 for (x = 0; x < width; x++) {
4451 DWORD color = 0xffffff & *(const DWORD*)source;
4452 DWORD dstcolor = color << 8;
4453 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4454 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4456 *(DWORD*)dest = dstcolor;
4465 ERR("Unsupported conversion type %#x.\n", convert);
4470 BOOL palette9_changed(struct wined3d_surface *surface)
4472 struct wined3d_device *device = surface->resource.device;
4474 if (surface->palette || (surface->resource.format->id != WINED3DFMT_P8_UINT
4475 && surface->resource.format->id != WINED3DFMT_P8_UINT_A8_UNORM))
4477 /* If a ddraw-style palette is attached assume no d3d9 palette change.
4478 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
4483 if (surface->palette9)
4485 if (!memcmp(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
4492 surface->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
4494 memcpy(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
4499 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4501 /* Flip the surface contents */
4506 front->hDC = back->hDC;
4510 /* Flip the DIBsection */
4513 BOOL hasDib = front->flags & SFLAG_DIBSECTION;
4514 tmp = front->dib.DIBsection;
4515 front->dib.DIBsection = back->dib.DIBsection;
4516 back->dib.DIBsection = tmp;
4518 if (back->flags & SFLAG_DIBSECTION) front->flags |= SFLAG_DIBSECTION;
4519 else front->flags &= ~SFLAG_DIBSECTION;
4520 if (hasDib) back->flags |= SFLAG_DIBSECTION;
4521 else back->flags &= ~SFLAG_DIBSECTION;
4524 /* Flip the surface data */
4528 tmp = front->dib.bitmap_data;
4529 front->dib.bitmap_data = back->dib.bitmap_data;
4530 back->dib.bitmap_data = tmp;
4532 tmp = front->resource.allocatedMemory;
4533 front->resource.allocatedMemory = back->resource.allocatedMemory;
4534 back->resource.allocatedMemory = tmp;
4536 tmp = front->resource.heapMemory;
4537 front->resource.heapMemory = back->resource.heapMemory;
4538 back->resource.heapMemory = tmp;
4543 GLuint tmp_pbo = front->pbo;
4544 front->pbo = back->pbo;
4545 back->pbo = tmp_pbo;
4548 /* client_memory should not be different, but just in case */
4551 tmp = front->dib.client_memory;
4552 front->dib.client_memory = back->dib.client_memory;
4553 back->dib.client_memory = tmp;
4556 /* Flip the opengl texture */
4560 tmp = back->texture_name;
4561 back->texture_name = front->texture_name;
4562 front->texture_name = tmp;
4564 tmp = back->texture_name_srgb;
4565 back->texture_name_srgb = front->texture_name_srgb;
4566 front->texture_name_srgb = tmp;
4568 resource_unload(&back->resource);
4569 resource_unload(&front->resource);
4573 DWORD tmp_flags = back->flags;
4574 back->flags = front->flags;
4575 front->flags = tmp_flags;
4579 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4580 * pixel copy calls. */
4581 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4582 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4584 struct wined3d_device *device = dst_surface->resource.device;
4587 struct wined3d_context *context;
4588 BOOL upsidedown = FALSE;
4589 RECT dst_rect = *dst_rect_in;
4591 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4592 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4594 if(dst_rect.top > dst_rect.bottom) {
4595 UINT tmp = dst_rect.bottom;
4596 dst_rect.bottom = dst_rect.top;
4601 context = context_acquire(device, src_surface);
4602 context_apply_blit_state(context, device);
4603 surface_internal_preload(dst_surface, SRGB_RGB);
4606 /* Bind the target texture */
4607 glBindTexture(dst_surface->texture_target, dst_surface->texture_name);
4608 checkGLcall("glBindTexture");
4609 if (surface_is_offscreen(src_surface))
4611 TRACE("Reading from an offscreen target\n");
4612 upsidedown = !upsidedown;
4613 glReadBuffer(device->offscreenBuffer);
4617 glReadBuffer(surface_get_gl_buffer(src_surface));
4619 checkGLcall("glReadBuffer");
4621 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4622 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4624 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4626 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4628 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4629 ERR("Texture filtering not supported in direct blit\n");
4632 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4633 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4635 ERR("Texture filtering not supported in direct blit\n");
4639 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4640 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4642 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4644 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4645 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4646 src_rect->left, src_surface->resource.height - src_rect->bottom,
4647 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4651 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4652 /* I have to process this row by row to swap the image,
4653 * otherwise it would be upside down, so stretching in y direction
4654 * doesn't cost extra time
4656 * However, stretching in x direction can be avoided if not necessary
4658 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4659 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4661 /* Well, that stuff works, but it's very slow.
4662 * find a better way instead
4666 for (col = dst_rect.left; col < dst_rect.right; ++col)
4668 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4669 dst_rect.left + col /* x offset */, row /* y offset */,
4670 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4675 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4676 dst_rect.left /* x offset */, row /* y offset */,
4677 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4681 checkGLcall("glCopyTexSubImage2D");
4684 context_release(context);
4686 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4687 * path is never entered
4689 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4692 /* Uses the hardware to stretch and flip the image */
4693 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4694 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4696 struct wined3d_device *device = dst_surface->resource.device;
4697 struct wined3d_swapchain *src_swapchain = NULL;
4698 GLuint src, backup = 0;
4699 float left, right, top, bottom; /* Texture coordinates */
4700 UINT fbwidth = src_surface->resource.width;
4701 UINT fbheight = src_surface->resource.height;
4702 struct wined3d_context *context;
4703 GLenum drawBuffer = GL_BACK;
4704 GLenum texture_target;
4705 BOOL noBackBufferBackup;
4707 BOOL upsidedown = FALSE;
4708 RECT dst_rect = *dst_rect_in;
4710 TRACE("Using hwstretch blit\n");
4711 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4712 context = context_acquire(device, src_surface);
4713 context_apply_blit_state(context, device);
4714 surface_internal_preload(dst_surface, SRGB_RGB);
4716 src_offscreen = surface_is_offscreen(src_surface);
4717 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4718 if (!noBackBufferBackup && !src_surface->texture_name)
4720 /* Get it a description */
4721 surface_internal_preload(src_surface, SRGB_RGB);
4725 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4726 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4728 if (context->aux_buffers >= 2)
4730 /* Got more than one aux buffer? Use the 2nd aux buffer */
4731 drawBuffer = GL_AUX1;
4733 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4735 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4736 drawBuffer = GL_AUX0;
4739 if(noBackBufferBackup) {
4740 glGenTextures(1, &backup);
4741 checkGLcall("glGenTextures");
4742 glBindTexture(GL_TEXTURE_2D, backup);
4743 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
4744 texture_target = GL_TEXTURE_2D;
4746 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4747 * we are reading from the back buffer, the backup can be used as source texture
4749 texture_target = src_surface->texture_target;
4750 glBindTexture(texture_target, src_surface->texture_name);
4751 checkGLcall("glBindTexture(texture_target, src_surface->texture_name)");
4752 glEnable(texture_target);
4753 checkGLcall("glEnable(texture_target)");
4755 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4756 src_surface->flags &= ~SFLAG_INTEXTURE;
4759 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4760 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4762 if(dst_rect.top > dst_rect.bottom) {
4763 UINT tmp = dst_rect.bottom;
4764 dst_rect.bottom = dst_rect.top;
4771 TRACE("Reading from an offscreen target\n");
4772 upsidedown = !upsidedown;
4773 glReadBuffer(device->offscreenBuffer);
4777 glReadBuffer(surface_get_gl_buffer(src_surface));
4780 /* TODO: Only back up the part that will be overwritten */
4781 glCopyTexSubImage2D(texture_target, 0,
4782 0, 0 /* read offsets */,
4787 checkGLcall("glCopyTexSubImage2D");
4789 /* No issue with overriding these - the sampler is dirty due to blit usage */
4790 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
4791 wined3d_gl_mag_filter(magLookup, Filter));
4792 checkGLcall("glTexParameteri");
4793 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
4794 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
4795 checkGLcall("glTexParameteri");
4797 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
4798 src_swapchain = src_surface->container.u.swapchain;
4799 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
4801 src = backup ? backup : src_surface->texture_name;
4805 glReadBuffer(GL_FRONT);
4806 checkGLcall("glReadBuffer(GL_FRONT)");
4808 glGenTextures(1, &src);
4809 checkGLcall("glGenTextures(1, &src)");
4810 glBindTexture(GL_TEXTURE_2D, src);
4811 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
4813 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
4814 * out for power of 2 sizes
4816 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
4817 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
4818 checkGLcall("glTexImage2D");
4819 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
4820 0, 0 /* read offsets */,
4825 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4826 checkGLcall("glTexParameteri");
4827 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4828 checkGLcall("glTexParameteri");
4830 glReadBuffer(GL_BACK);
4831 checkGLcall("glReadBuffer(GL_BACK)");
4833 if(texture_target != GL_TEXTURE_2D) {
4834 glDisable(texture_target);
4835 glEnable(GL_TEXTURE_2D);
4836 texture_target = GL_TEXTURE_2D;
4839 checkGLcall("glEnd and previous");
4841 left = src_rect->left;
4842 right = src_rect->right;
4846 top = src_surface->resource.height - src_rect->top;
4847 bottom = src_surface->resource.height - src_rect->bottom;
4851 top = src_surface->resource.height - src_rect->bottom;
4852 bottom = src_surface->resource.height - src_rect->top;
4855 if (src_surface->flags & SFLAG_NORMCOORD)
4857 left /= src_surface->pow2Width;
4858 right /= src_surface->pow2Width;
4859 top /= src_surface->pow2Height;
4860 bottom /= src_surface->pow2Height;
4863 /* draw the source texture stretched and upside down. The correct surface is bound already */
4864 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
4865 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
4867 context_set_draw_buffer(context, drawBuffer);
4868 glReadBuffer(drawBuffer);
4872 glTexCoord2f(left, bottom);
4876 glTexCoord2f(left, top);
4877 glVertex2i(0, dst_rect.bottom - dst_rect.top);
4880 glTexCoord2f(right, top);
4881 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4884 glTexCoord2f(right, bottom);
4885 glVertex2i(dst_rect.right - dst_rect.left, 0);
4887 checkGLcall("glEnd and previous");
4889 if (texture_target != dst_surface->texture_target)
4891 glDisable(texture_target);
4892 glEnable(dst_surface->texture_target);
4893 texture_target = dst_surface->texture_target;
4896 /* Now read the stretched and upside down image into the destination texture */
4897 glBindTexture(texture_target, dst_surface->texture_name);
4898 checkGLcall("glBindTexture");
4899 glCopyTexSubImage2D(texture_target,
4901 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
4902 0, 0, /* We blitted the image to the origin */
4903 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4904 checkGLcall("glCopyTexSubImage2D");
4906 if(drawBuffer == GL_BACK) {
4907 /* Write the back buffer backup back */
4909 if(texture_target != GL_TEXTURE_2D) {
4910 glDisable(texture_target);
4911 glEnable(GL_TEXTURE_2D);
4912 texture_target = GL_TEXTURE_2D;
4914 glBindTexture(GL_TEXTURE_2D, backup);
4915 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
4919 if (texture_target != src_surface->texture_target)
4921 glDisable(texture_target);
4922 glEnable(src_surface->texture_target);
4923 texture_target = src_surface->texture_target;
4925 glBindTexture(src_surface->texture_target, src_surface->texture_name);
4926 checkGLcall("glBindTexture(src_surface->texture_target, src_surface->texture_name)");
4931 glTexCoord2f(0.0f, 0.0f);
4932 glVertex2i(0, fbheight);
4935 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
4939 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
4940 (float)fbheight / (float)src_surface->pow2Height);
4941 glVertex2i(fbwidth, 0);
4944 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
4945 glVertex2i(fbwidth, fbheight);
4948 glDisable(texture_target);
4949 checkGLcall("glDisable(texture_target)");
4952 if (src != src_surface->texture_name && src != backup)
4954 glDeleteTextures(1, &src);
4955 checkGLcall("glDeleteTextures(1, &src)");
4958 glDeleteTextures(1, &backup);
4959 checkGLcall("glDeleteTextures(1, &backup)");
4964 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
4966 context_release(context);
4968 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4969 * path is never entered
4971 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4974 /* Front buffer coordinates are always full screen coordinates, but our GL
4975 * drawable is limited to the window's client area. The sysmem and texture
4976 * copies do have the full screen size. Note that GL has a bottom-left
4977 * origin, while D3D has a top-left origin. */
4978 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
4980 UINT drawable_height;
4982 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4983 && surface == surface->container.u.swapchain->front_buffer)
4985 POINT offset = {0, 0};
4988 ScreenToClient(window, &offset);
4989 OffsetRect(rect, offset.x, offset.y);
4991 GetClientRect(window, &windowsize);
4992 drawable_height = windowsize.bottom - windowsize.top;
4996 drawable_height = surface->resource.height;
4999 rect->top = drawable_height - rect->top;
5000 rect->bottom = drawable_height - rect->bottom;
5003 /* blit between surface locations. onscreen on different swapchains is not supported.
5004 * depth / stencil is not supported. */
5005 static void surface_blt_fbo(struct wined3d_device *device, const WINED3DTEXTUREFILTERTYPE filter,
5006 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
5007 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
5009 const struct wined3d_gl_info *gl_info;
5010 struct wined3d_context *context;
5011 RECT src_rect, dst_rect;
5015 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
5016 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
5017 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
5018 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
5019 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
5021 src_rect = *src_rect_in;
5022 dst_rect = *dst_rect_in;
5026 case WINED3DTEXF_LINEAR:
5027 gl_filter = GL_LINEAR;
5031 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
5032 case WINED3DTEXF_NONE:
5033 case WINED3DTEXF_POINT:
5034 gl_filter = GL_NEAREST;
5038 if (src_location == SFLAG_INDRAWABLE && surface_is_offscreen(src_surface))
5039 src_location = SFLAG_INTEXTURE;
5040 if (dst_location == SFLAG_INDRAWABLE && surface_is_offscreen(dst_surface))
5041 dst_location = SFLAG_INTEXTURE;
5043 /* Make sure the locations are up-to-date. Loading the destination
5044 * surface isn't required if the entire surface is overwritten. (And is
5045 * in fact harmful if we're being called by surface_load_location() with
5046 * the purpose of loading the destination surface.) */
5047 surface_load_location(src_surface, src_location, NULL);
5048 if (!surface_is_full_rect(dst_surface, &dst_rect))
5049 surface_load_location(dst_surface, dst_location, NULL);
5051 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
5052 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
5053 else context = context_acquire(device, NULL);
5055 if (!context->valid)
5057 context_release(context);
5058 WARN("Invalid context, skipping blit.\n");
5062 gl_info = context->gl_info;
5064 if (src_location == SFLAG_INDRAWABLE)
5066 TRACE("Source surface %p is onscreen.\n", src_surface);
5067 buffer = surface_get_gl_buffer(src_surface);
5068 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
5072 TRACE("Source surface %p is offscreen.\n", src_surface);
5073 buffer = GL_COLOR_ATTACHMENT0;
5077 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
5078 glReadBuffer(buffer);
5079 checkGLcall("glReadBuffer()");
5080 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
5083 if (dst_location == SFLAG_INDRAWABLE)
5085 TRACE("Destination surface %p is onscreen.\n", dst_surface);
5086 buffer = surface_get_gl_buffer(dst_surface);
5087 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5091 TRACE("Destination surface %p is offscreen.\n", dst_surface);
5092 buffer = GL_COLOR_ATTACHMENT0;
5096 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
5097 context_set_draw_buffer(context, buffer);
5098 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
5099 context_invalidate_state(context, STATE_FRAMEBUFFER);
5101 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
5102 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE));
5103 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE1));
5104 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE2));
5105 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE3));
5107 glDisable(GL_SCISSOR_TEST);
5108 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
5110 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
5111 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
5112 checkGLcall("glBlitFramebuffer()");
5116 if (wined3d_settings.strict_draw_ordering
5117 || (dst_location == SFLAG_INDRAWABLE
5118 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
5121 context_release(context);
5124 static void surface_blt_to_drawable(struct wined3d_device *device,
5125 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5126 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5127 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5129 struct wined3d_context *context;
5130 RECT src_rect, dst_rect;
5132 src_rect = *src_rect_in;
5133 dst_rect = *dst_rect_in;
5135 /* Make sure the surface is up-to-date. This should probably use
5136 * surface_load_location() and worry about the destination surface too,
5137 * unless we're overwriting it completely. */
5138 surface_internal_preload(src_surface, SRGB_RGB);
5140 /* Activate the destination context, set it up for blitting */
5141 context = context_acquire(device, dst_surface);
5142 context_apply_blit_state(context, device);
5144 if (!surface_is_offscreen(dst_surface))
5145 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5147 device->blitter->set_shader(device->blit_priv, context->gl_info, src_surface);
5153 glEnable(GL_ALPHA_TEST);
5154 checkGLcall("glEnable(GL_ALPHA_TEST)");
5156 /* When the primary render target uses P8, the alpha component
5157 * contains the palette index. Which means that the colorkey is one of
5158 * the palette entries. In other cases pixels that should be masked
5159 * away have alpha set to 0. */
5160 if (primary_render_target_is_p8(device))
5161 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
5163 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5164 checkGLcall("glAlphaFunc");
5168 glDisable(GL_ALPHA_TEST);
5169 checkGLcall("glDisable(GL_ALPHA_TEST)");
5172 draw_textured_quad(src_surface, &src_rect, &dst_rect, filter);
5176 glDisable(GL_ALPHA_TEST);
5177 checkGLcall("glDisable(GL_ALPHA_TEST)");
5182 /* Leave the opengl state valid for blitting */
5183 device->blitter->unset_shader(context->gl_info);
5185 if (wined3d_settings.strict_draw_ordering
5186 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5187 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5188 wglFlush(); /* Flush to ensure ordering across contexts. */
5190 context_release(context);
5193 /* Do not call while under the GL lock. */
5194 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const WINED3DCOLORVALUE *color)
5196 struct wined3d_device *device = s->resource.device;
5197 const struct blit_shader *blitter;
5199 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5200 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5203 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5204 return WINED3DERR_INVALIDCALL;
5207 return blitter->color_fill(device, s, rect, color);
5210 /* Do not call while under the GL lock. */
5211 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5212 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5213 WINED3DTEXTUREFILTERTYPE Filter)
5215 struct wined3d_device *device = dst_surface->resource.device;
5216 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5217 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
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(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
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 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
5263 if (dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->back_buffers
5264 && dst_surface == dstSwapchain->front_buffer
5265 && src_surface == dstSwapchain->back_buffers[0])
5267 /* Half-Life does a Blt from the back buffer to the front buffer,
5268 * Full surface size, no flags... Use present instead
5270 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
5273 /* Check rects - wined3d_swapchain_present() doesn't handle them. */
5276 TRACE("Looking if a Present can be done...\n");
5277 /* Source Rectangle must be full surface */
5278 if (src_rect->left || src_rect->top
5279 || src_rect->right != src_surface->resource.width
5280 || src_rect->bottom != src_surface->resource.height)
5282 TRACE("No, Source rectangle doesn't match\n");
5286 /* No stretching may occur */
5287 if (src_rect->right != dst_rect->right - dst_rect->left
5288 || src_rect->bottom != dst_rect->bottom - dst_rect->top)
5290 TRACE("No, stretching is done\n");
5294 /* Destination must be full surface or match the clipping rectangle */
5295 if (dst_surface->clipper && dst_surface->clipper->hWnd)
5299 GetClientRect(dst_surface->clipper->hWnd, &cliprect);
5300 pos[0].x = dst_rect->left;
5301 pos[0].y = dst_rect->top;
5302 pos[1].x = dst_rect->right;
5303 pos[1].y = dst_rect->bottom;
5304 MapWindowPoints(GetDesktopWindow(), dst_surface->clipper->hWnd, pos, 2);
5306 if (pos[0].x != cliprect.left || pos[0].y != cliprect.top
5307 || pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
5309 TRACE("No, dest rectangle doesn't match(clipper)\n");
5310 TRACE("Clip rect at %s\n", wine_dbgstr_rect(&cliprect));
5311 TRACE("Blt dest: %s\n", wine_dbgstr_rect(dst_rect));
5315 else if (dst_rect->left || dst_rect->top
5316 || dst_rect->right != dst_surface->resource.width
5317 || dst_rect->bottom != dst_surface->resource.height)
5319 TRACE("No, dest rectangle doesn't match(surface size)\n");
5325 /* These flags are unimportant for the flag check, remove them */
5326 if (!(flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)))
5328 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
5330 /* The idea behind this is that a glReadPixels and a glDrawPixels call
5331 * take very long, while a flip is fast.
5332 * This applies to Half-Life, which does such Blts every time it finished
5333 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
5334 * menu. This is also used by all apps when they do windowed rendering
5336 * The problem is that flipping is not really the same as copying. After a
5337 * Blt the front buffer is a copy of the back buffer, and the back buffer is
5338 * untouched. Therefore it's necessary to override the swap effect
5339 * and to set it back after the flip.
5341 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
5345 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
5346 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
5348 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead.\n");
5349 wined3d_swapchain_present(dstSwapchain, NULL, NULL, dstSwapchain->win_handle, NULL, 0);
5351 dstSwapchain->presentParms.SwapEffect = orig_swap;
5358 TRACE("Unsupported blit between buffers on the same swapchain\n");
5359 return WINED3DERR_INVALIDCALL;
5360 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
5361 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5362 return WINED3DERR_INVALIDCALL;
5363 } else if(dstSwapchain && srcSwapchain) {
5364 FIXME("Implement hardware blit between two different swapchains\n");
5365 return WINED3DERR_INVALIDCALL;
5367 else if (dstSwapchain)
5369 /* Handled with regular texture -> swapchain blit */
5370 if (src_surface == device->fb.render_targets[0])
5371 TRACE("Blit from active render target to a swapchain\n");
5373 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5375 FIXME("Implement blit from a swapchain to the active render target\n");
5376 return WINED3DERR_INVALIDCALL;
5379 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5381 /* Blit from render target to texture */
5384 /* P8 read back is not implemented */
5385 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5386 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5388 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5389 return WINED3DERR_INVALIDCALL;
5392 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5394 TRACE("Color keying not supported by frame buffer to texture blit\n");
5395 return WINED3DERR_INVALIDCALL;
5396 /* Destination color key is checked above */
5399 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5404 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5405 * flip the image nor scale it.
5407 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5408 * -> If the app wants a image width an unscaled width, copy it line per line
5409 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5410 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5411 * back buffer. This is slower than reading line per line, thus not used for flipping
5412 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5415 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
5416 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
5418 if (fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5419 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5420 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5422 surface_blt_fbo(device, Filter,
5423 src_surface, SFLAG_INDRAWABLE, src_rect,
5424 dst_surface, SFLAG_INDRAWABLE, dst_rect);
5425 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
5427 else if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5428 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5430 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5431 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, Filter);
5433 TRACE("Using hardware stretching to flip / stretch the texture\n");
5434 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5437 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5439 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5440 dst_surface->resource.allocatedMemory = NULL;
5441 dst_surface->resource.heapMemory = NULL;
5445 dst_surface->flags &= ~SFLAG_INSYSMEM;
5450 else if (src_surface)
5452 /* Blit from offscreen surface to render target */
5453 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5454 WINEDDCOLORKEY oldBltCKey = src_surface->SrcBltCKey;
5456 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5458 if (!(flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5459 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5460 src_rect, src_surface->resource.usage, src_surface->resource.pool,
5461 src_surface->resource.format,
5462 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool,
5463 dst_surface->resource.format))
5465 TRACE("Using surface_blt_fbo.\n");
5466 /* The source is always a texture, but never the currently active render target, and the texture
5467 * contents are never upside down. */
5468 surface_blt_fbo(device, Filter,
5469 src_surface, SFLAG_INDRAWABLE, src_rect,
5470 dst_surface, SFLAG_INDRAWABLE, dst_rect);
5471 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
5475 if (!(flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5476 && arbfp_blit.blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5477 src_rect, src_surface->resource.usage, src_surface->resource.pool,
5478 src_surface->resource.format,
5479 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool,
5480 dst_surface->resource.format))
5482 return arbfp_blit_surface(device, src_surface, src_rect, dst_surface, dst_rect,
5483 WINED3D_BLIT_OP_COLOR_BLIT, Filter);
5486 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5487 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5488 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5490 FIXME("Unsupported blit operation falling back to software\n");
5491 return WINED3DERR_INVALIDCALL;
5494 /* Color keying: Check if we have to do a color keyed blt,
5495 * and if not check if a color key is activated.
5497 * Just modify the color keying parameters in the surface and restore them afterwards
5498 * The surface keeps track of the color key last used to load the opengl surface.
5499 * PreLoad will catch the change to the flags and color key and reload if necessary.
5501 if (flags & WINEDDBLT_KEYSRC)
5503 /* Use color key from surface */
5505 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5507 /* Use color key from DDBltFx */
5508 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5509 src_surface->SrcBltCKey = DDBltFx->ddckSrcColorkey;
5513 /* Do not use color key */
5514 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5517 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5518 src_surface, src_rect, dst_surface, dst_rect);
5520 /* Restore the color key parameters */
5521 src_surface->CKeyFlags = oldCKeyFlags;
5522 src_surface->SrcBltCKey = oldBltCKey;
5524 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
5530 /* Source-Less Blit to render target */
5531 if (flags & WINEDDBLT_COLORFILL)
5533 WINED3DCOLORVALUE color;
5535 TRACE("Colorfill\n");
5537 /* The color as given in the Blt function is in the surface format. */
5538 if (!surface_convert_color_to_float(dst_surface, DDBltFx->u5.dwFillColor, &color))
5539 return WINED3DERR_INVALIDCALL;
5541 return surface_color_fill(dst_surface, dst_rect, &color);
5545 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5546 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5547 return WINED3DERR_INVALIDCALL;
5550 /* GL locking is done by the caller */
5551 static void surface_depth_blt(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
5552 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5554 struct wined3d_device *device = surface->resource.device;
5555 GLint compare_mode = GL_NONE;
5556 struct blt_info info;
5557 GLint old_binding = 0;
5560 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5562 glDisable(GL_CULL_FACE);
5563 glDisable(GL_BLEND);
5564 glDisable(GL_ALPHA_TEST);
5565 glDisable(GL_SCISSOR_TEST);
5566 glDisable(GL_STENCIL_TEST);
5567 glEnable(GL_DEPTH_TEST);
5568 glDepthFunc(GL_ALWAYS);
5569 glDepthMask(GL_TRUE);
5570 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5571 glViewport(x, y, w, h);
5573 SetRect(&rect, 0, h, w, 0);
5574 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5575 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
5576 glGetIntegerv(info.binding, &old_binding);
5577 glBindTexture(info.bind_target, texture);
5578 if (gl_info->supported[ARB_SHADOW])
5580 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5581 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5584 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5585 gl_info, info.tex_type, &surface->ds_current_size);
5587 glBegin(GL_TRIANGLE_STRIP);
5588 glTexCoord3fv(info.coords[0]);
5589 glVertex2f(-1.0f, -1.0f);
5590 glTexCoord3fv(info.coords[1]);
5591 glVertex2f(1.0f, -1.0f);
5592 glTexCoord3fv(info.coords[2]);
5593 glVertex2f(-1.0f, 1.0f);
5594 glTexCoord3fv(info.coords[3]);
5595 glVertex2f(1.0f, 1.0f);
5598 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5599 glBindTexture(info.bind_target, old_binding);
5603 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5606 void surface_modify_ds_location(struct wined3d_surface *surface,
5607 DWORD location, UINT w, UINT h)
5609 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5611 if (location & ~SFLAG_DS_LOCATIONS)
5612 FIXME("Invalid location (%#x) specified.\n", location);
5614 surface->ds_current_size.cx = w;
5615 surface->ds_current_size.cy = h;
5616 surface->flags &= ~SFLAG_DS_LOCATIONS;
5617 surface->flags |= location;
5620 /* Context activation is done by the caller. */
5621 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5623 struct wined3d_device *device = surface->resource.device;
5624 const struct wined3d_gl_info *gl_info = context->gl_info;
5627 TRACE("surface %p, new location %#x.\n", surface, location);
5629 /* TODO: Make this work for modes other than FBO */
5630 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5632 if (!(surface->flags & location))
5634 w = surface->ds_current_size.cx;
5635 h = surface->ds_current_size.cy;
5636 surface->ds_current_size.cx = 0;
5637 surface->ds_current_size.cy = 0;
5641 w = surface->resource.width;
5642 h = surface->resource.height;
5645 if (surface->ds_current_size.cx == surface->resource.width
5646 && surface->ds_current_size.cy == surface->resource.height)
5648 TRACE("Location (%#x) is already up to date.\n", location);
5652 if (surface->current_renderbuffer)
5654 FIXME("Not supported with fixed up depth stencil.\n");
5658 if (!(surface->flags & SFLAG_DS_LOCATIONS))
5660 /* This mostly happens when a depth / stencil is used without being
5661 * cleared first. In principle we could upload from sysmem, or
5662 * explicitly clear before first usage. For the moment there don't
5663 * appear to be a lot of applications depending on this, so a FIXME
5665 FIXME("No up to date depth stencil location.\n");
5666 surface->flags |= location;
5667 surface->ds_current_size.cx = surface->resource.width;
5668 surface->ds_current_size.cy = surface->resource.height;
5672 if (location == SFLAG_DS_OFFSCREEN)
5674 GLint old_binding = 0;
5677 /* The render target is allowed to be smaller than the depth/stencil
5678 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5679 * than the offscreen surface. Don't overwrite the offscreen surface
5680 * with undefined data. */
5681 w = min(w, context->swapchain->presentParms.BackBufferWidth);
5682 h = min(h, context->swapchain->presentParms.BackBufferHeight);
5684 TRACE("Copying onscreen depth buffer to depth texture.\n");
5688 if (!device->depth_blt_texture)
5690 glGenTextures(1, &device->depth_blt_texture);
5693 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5694 * directly on the FBO texture. That's because we need to flip. */
5695 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5696 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5697 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5699 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5700 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5704 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5705 bind_target = GL_TEXTURE_2D;
5707 glBindTexture(bind_target, device->depth_blt_texture);
5708 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5709 * internal format, because the internal format might include stencil
5710 * data. In principle we should copy stencil data as well, but unless
5711 * the driver supports stencil export it's hard to do, and doesn't
5712 * seem to be needed in practice. If the hardware doesn't support
5713 * writing stencil data, the glCopyTexImage2D() call might trigger
5714 * software fallbacks. */
5715 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5716 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5717 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5718 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5719 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5720 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5721 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5722 glBindTexture(bind_target, old_binding);
5724 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5725 NULL, surface, SFLAG_INTEXTURE);
5726 context_set_draw_buffer(context, GL_NONE);
5728 /* Do the actual blit */
5729 surface_depth_blt(surface, gl_info, device->depth_blt_texture, 0, 0, w, h, bind_target);
5730 checkGLcall("depth_blt");
5732 context_invalidate_state(context, STATE_FRAMEBUFFER);
5736 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5738 else if (location == SFLAG_DS_ONSCREEN)
5740 TRACE("Copying depth texture to onscreen depth buffer.\n");
5744 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5745 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5746 surface_depth_blt(surface, gl_info, surface->texture_name,
5747 0, surface->pow2Height - h, w, h, surface->texture_target);
5748 checkGLcall("depth_blt");
5750 context_invalidate_state(context, STATE_FRAMEBUFFER);
5754 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5758 ERR("Invalid location (%#x) specified.\n", location);
5761 surface->flags |= location;
5762 surface->ds_current_size.cx = surface->resource.width;
5763 surface->ds_current_size.cy = surface->resource.height;
5766 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5768 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5769 struct wined3d_surface *overlay;
5771 TRACE("surface %p, location %s, persistent %#x.\n",
5772 surface, debug_surflocation(location), persistent);
5774 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5776 if (surface_is_offscreen(surface))
5778 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same
5779 * for offscreen targets. */
5780 if (location & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))
5781 location |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
5785 TRACE("Surface %p is an onscreen surface.\n", surface);
5789 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5790 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5791 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5795 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5796 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5798 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5800 TRACE("Passing to container.\n");
5801 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5804 surface->flags &= ~SFLAG_LOCATIONS;
5805 surface->flags |= location;
5807 /* Redraw emulated overlays, if any */
5808 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5810 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5812 overlay->surface_ops->surface_draw_overlay(overlay);
5818 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5820 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5822 TRACE("Passing to container\n");
5823 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5826 surface->flags &= ~location;
5829 if (!(surface->flags & SFLAG_LOCATIONS))
5831 ERR("Surface %p does not have any up to date location.\n", surface);
5835 static DWORD resource_access_from_location(DWORD location)
5839 case SFLAG_INSYSMEM:
5840 return WINED3D_RESOURCE_ACCESS_CPU;
5842 case SFLAG_INDRAWABLE:
5843 case SFLAG_INSRGBTEX:
5844 case SFLAG_INTEXTURE:
5845 return WINED3D_RESOURCE_ACCESS_GPU;
5848 FIXME("Unhandled location %#x.\n", location);
5853 static void surface_load_sysmem(struct wined3d_surface *surface,
5854 const struct wined3d_gl_info *gl_info, const RECT *rect)
5856 surface_prepare_system_memory(surface);
5858 /* Download the surface to system memory. */
5859 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5861 struct wined3d_device *device = surface->resource.device;
5862 struct wined3d_context *context = NULL;
5864 if (!device->isInDraw)
5865 context = context_acquire(device, NULL);
5867 surface_bind_and_dirtify(surface, gl_info, !(surface->flags & SFLAG_INTEXTURE));
5868 surface_download_data(surface, gl_info);
5871 context_release(context);
5876 /* Note: It might be faster to download into a texture first. */
5877 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5878 wined3d_surface_get_pitch(surface));
5881 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5882 const struct wined3d_gl_info *gl_info, const RECT *rect)
5884 struct wined3d_device *device = surface->resource.device;
5885 struct wined3d_format format;
5886 CONVERT_TYPES convert;
5890 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5891 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5893 if (surface->flags & SFLAG_INTEXTURE)
5897 surface_get_rect(surface, rect, &r);
5898 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5903 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5905 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5906 * path through sysmem. */
5907 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5910 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5912 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5913 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5915 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5917 struct wined3d_context *context = NULL;
5919 TRACE("Removing the pbo attached to surface %p.\n", surface);
5921 if (!device->isInDraw)
5922 context = context_acquire(device, NULL);
5924 surface_remove_pbo(surface, gl_info);
5927 context_release(context);
5930 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5932 UINT height = surface->resource.height;
5933 UINT width = surface->resource.width;
5934 UINT src_pitch, dst_pitch;
5936 byte_count = format.conv_byte_count;
5937 src_pitch = wined3d_surface_get_pitch(surface);
5939 /* Stick to the alignment for the converted surface too, makes it
5940 * easier to load the surface. */
5941 dst_pitch = width * byte_count;
5942 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5944 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5946 ERR("Out of memory (%u).\n", dst_pitch * height);
5947 return E_OUTOFMEMORY;
5950 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5951 src_pitch, width, height, dst_pitch, convert, surface);
5953 surface->flags |= SFLAG_CONVERTED;
5957 surface->flags &= ~SFLAG_CONVERTED;
5958 mem = surface->resource.allocatedMemory;
5959 byte_count = format.byte_count;
5962 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5964 /* Don't delete PBO memory. */
5965 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5966 HeapFree(GetProcessHeap(), 0, mem);
5971 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5972 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5974 const DWORD attach_flags = WINED3DFMT_FLAG_FBO_ATTACHABLE | WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB;
5975 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5976 struct wined3d_device *device = surface->resource.device;
5977 struct wined3d_context *context = NULL;
5978 UINT width, src_pitch, dst_pitch;
5979 struct wined3d_bo_address data;
5980 struct wined3d_format format;
5981 POINT dst_point = {0, 0};
5982 CONVERT_TYPES convert;
5985 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
5986 && surface_is_offscreen(surface)
5987 && (surface->flags & SFLAG_INDRAWABLE))
5989 read_from_framebuffer_texture(surface, srgb);
5994 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
5995 && (surface->resource.format->flags & attach_flags) == attach_flags
5996 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5997 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5998 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6001 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
6002 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6004 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
6005 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6010 /* Upload from system memory */
6012 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6013 TRUE /* We will use textures */, &format, &convert);
6017 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6019 /* Performance warning... */
6020 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6021 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6026 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6028 /* Performance warning... */
6029 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6030 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6034 if (!(surface->flags & SFLAG_INSYSMEM))
6036 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6037 /* Lets hope we get it from somewhere... */
6038 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6041 if (!device->isInDraw)
6042 context = context_acquire(device, NULL);
6044 surface_prepare_texture(surface, gl_info, srgb);
6045 surface_bind_and_dirtify(surface, gl_info, srgb);
6047 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6049 surface->flags |= SFLAG_GLCKEY;
6050 surface->glCKey = surface->SrcBltCKey;
6052 else surface->flags &= ~SFLAG_GLCKEY;
6054 width = surface->resource.width;
6055 src_pitch = wined3d_surface_get_pitch(surface);
6057 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6058 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6060 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6062 TRACE("Removing the pbo attached to surface %p.\n", surface);
6063 surface_remove_pbo(surface, gl_info);
6068 /* This code is entered for texture formats which need a fixup. */
6069 UINT height = surface->resource.height;
6071 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6072 dst_pitch = width * format.conv_byte_count;
6073 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6075 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6077 ERR("Out of memory (%u).\n", dst_pitch * height);
6079 context_release(context);
6080 return E_OUTOFMEMORY;
6082 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6084 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6086 /* This code is only entered for color keying fixups */
6087 UINT height = surface->resource.height;
6089 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6090 dst_pitch = width * format.conv_byte_count;
6091 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6093 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6095 ERR("Out of memory (%u).\n", dst_pitch * height);
6097 context_release(context);
6098 return E_OUTOFMEMORY;
6100 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6101 width, height, dst_pitch, convert, surface);
6105 mem = surface->resource.allocatedMemory;
6108 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6110 surface_upload_data(surface, gl_info, &format, &src_rect, width, &dst_point, srgb, &data);
6113 context_release(context);
6115 /* Don't delete PBO memory. */
6116 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6117 HeapFree(GetProcessHeap(), 0, mem);
6122 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6124 struct wined3d_device *device = surface->resource.device;
6125 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6126 BOOL in_fbo = FALSE;
6129 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6131 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6133 if (location == SFLAG_INTEXTURE)
6135 struct wined3d_context *context = context_acquire(device, NULL);
6136 surface_load_ds_location(surface, context, SFLAG_DS_OFFSCREEN);
6137 context_release(context);
6142 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6143 return WINED3DERR_INVALIDCALL;
6147 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6149 if (surface_is_offscreen(surface))
6151 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same
6152 * for offscreen targets. Prefer SFLAG_INTEXTURE. */
6153 if (location == SFLAG_INDRAWABLE)
6154 location = SFLAG_INTEXTURE;
6159 TRACE("Surface %p is an onscreen surface.\n", surface);
6163 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6164 location = SFLAG_INTEXTURE;
6166 if (surface->flags & location)
6168 TRACE("Location already up to date.\n");
6172 if (WARN_ON(d3d_surface))
6174 DWORD required_access = resource_access_from_location(location);
6175 if ((surface->resource.access_flags & required_access) != required_access)
6176 WARN("Operation requires %#x access, but surface only has %#x.\n",
6177 required_access, surface->resource.access_flags);
6180 if (!(surface->flags & SFLAG_LOCATIONS))
6182 ERR("Surface %p does not have any up to date location.\n", surface);
6183 surface->flags |= SFLAG_LOST;
6184 return WINED3DERR_DEVICELOST;
6189 case SFLAG_INSYSMEM:
6190 surface_load_sysmem(surface, gl_info, rect);
6193 case SFLAG_INDRAWABLE:
6194 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6198 case SFLAG_INTEXTURE:
6199 case SFLAG_INSRGBTEX:
6200 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6205 ERR("Don't know how to handle location %#x.\n", location);
6211 surface->flags |= location;
6213 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6214 surface_evict_sysmem(surface);
6217 if (in_fbo && (surface->flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)))
6219 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
6220 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
6223 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6224 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6226 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6232 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6234 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6236 /* Not on a swapchain - must be offscreen */
6237 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6239 /* The front buffer is always onscreen */
6240 if (surface == swapchain->front_buffer) return FALSE;
6242 /* If the swapchain is rendered to an FBO, the backbuffer is
6243 * offscreen, otherwise onscreen */
6244 return swapchain->render_to_fbo;
6247 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6248 /* Context activation is done by the caller. */
6249 static void ffp_blit_free(struct wined3d_device *device) { }
6251 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6252 /* Context activation is done by the caller. */
6253 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6256 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6258 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6260 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6262 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6266 /* Context activation is done by the caller. */
6267 static HRESULT ffp_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, struct wined3d_surface *surface)
6269 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6271 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6272 * else the surface is converted in software at upload time in LoadLocation.
6274 if(fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6275 ffp_blit_p8_upload_palette(surface, gl_info);
6278 glEnable(surface->texture_target);
6279 checkGLcall("glEnable(surface->texture_target)");
6284 /* Context activation is done by the caller. */
6285 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6288 glDisable(GL_TEXTURE_2D);
6289 checkGLcall("glDisable(GL_TEXTURE_2D)");
6290 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6292 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6293 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6295 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6297 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6298 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6303 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6304 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6305 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6307 enum complex_fixup src_fixup;
6311 case WINED3D_BLIT_OP_COLOR_BLIT:
6312 src_fixup = get_complex_fixup(src_format->color_fixup);
6313 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6315 TRACE("Checking support for fixup:\n");
6316 dump_color_fixup_desc(src_format->color_fixup);
6319 if (!is_identity_fixup(dst_format->color_fixup))
6321 TRACE("Destination fixups are not supported\n");
6325 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6327 TRACE("P8 fixup supported\n");
6331 /* We only support identity conversions. */
6332 if (is_identity_fixup(src_format->color_fixup))
6338 TRACE("[FAILED]\n");
6341 case WINED3D_BLIT_OP_COLOR_FILL:
6342 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6344 TRACE("Color fill not supported\n");
6350 case WINED3D_BLIT_OP_DEPTH_FILL:
6354 TRACE("Unsupported blit_op=%d\n", blit_op);
6359 /* Do not call while under the GL lock. */
6360 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6361 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
6363 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6364 struct wined3d_fb_state fb = {&dst_surface, NULL};
6366 return device_clear_render_targets(device, 1, &fb,
6367 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6370 /* Do not call while under the GL lock. */
6371 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6372 struct wined3d_surface *surface, const RECT *rect, float depth)
6374 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6375 struct wined3d_fb_state fb = {NULL, surface};
6377 return device_clear_render_targets(device, 0, &fb,
6378 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6381 const struct blit_shader ffp_blit = {
6387 ffp_blit_color_fill,
6388 ffp_blit_depth_fill,
6391 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6396 /* Context activation is done by the caller. */
6397 static void cpu_blit_free(struct wined3d_device *device)
6401 /* Context activation is done by the caller. */
6402 static HRESULT cpu_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, struct wined3d_surface *surface)
6407 /* Context activation is done by the caller. */
6408 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6412 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6413 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6414 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6416 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6424 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6425 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6426 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6428 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6429 const struct wined3d_format *src_format, *dst_format;
6430 struct wined3d_surface *orig_src = src_surface;
6431 WINED3DLOCKED_RECT dlock, slock;
6432 HRESULT hr = WINED3D_OK;
6438 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6439 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6440 flags, fx, debug_d3dtexturefiltertype(filter));
6442 /* First check for the validity of source / destination rectangles.
6443 * This was verified using a test application and by MSDN. */
6448 if (src_rect->right < src_rect->left || src_rect->bottom < src_rect->top
6449 || src_rect->left > src_surface->resource.width || src_rect->left < 0
6450 || src_rect->top > src_surface->resource.height || src_rect->top < 0
6451 || src_rect->right > src_surface->resource.width || src_rect->right < 0
6452 || src_rect->bottom > src_surface->resource.height || src_rect->bottom < 0)
6454 WARN("Application gave us bad source rectangle for Blt.\n");
6455 return WINEDDERR_INVALIDRECT;
6458 if (!src_rect->right || !src_rect->bottom
6459 || src_rect->left == (int)src_surface->resource.width
6460 || src_rect->top == (int)src_surface->resource.height)
6462 TRACE("Nothing to be done.\n");
6469 else if (src_surface)
6473 xsrc.right = src_surface->resource.width;
6474 xsrc.bottom = src_surface->resource.height;
6478 memset(&xsrc, 0, sizeof(xsrc));
6483 /* For the Destination rect, it can be out of bounds on the condition
6484 * that a clipper is set for the given surface. */
6485 if (!dst_surface->clipper && (dst_rect->right < dst_rect->left || dst_rect->bottom < dst_rect->top
6486 || dst_rect->left > dst_surface->resource.width || dst_rect->left < 0
6487 || dst_rect->top > dst_surface->resource.height || dst_rect->top < 0
6488 || dst_rect->right > dst_surface->resource.width || dst_rect->right < 0
6489 || dst_rect->bottom > dst_surface->resource.height || dst_rect->bottom < 0))
6491 WARN("Application gave us bad destination rectangle for Blt without a clipper set.\n");
6492 return WINEDDERR_INVALIDRECT;
6495 if (dst_rect->right <= 0 || dst_rect->bottom <= 0
6496 || dst_rect->left >= (int)dst_surface->resource.width
6497 || dst_rect->top >= (int)dst_surface->resource.height)
6499 TRACE("Nothing to be done.\n");
6509 full_rect.right = dst_surface->resource.width;
6510 full_rect.bottom = dst_surface->resource.height;
6511 IntersectRect(&xdst, &full_rect, dst_rect);
6515 BOOL clip_horiz, clip_vert;
6518 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6519 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6521 if (clip_vert || clip_horiz)
6523 /* Now check if this is a special case or not... */
6524 if ((flags & WINEDDBLT_DDFX)
6525 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6526 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6528 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6536 xsrc.left -= xdst.left;
6539 if (xdst.right > dst_surface->resource.width)
6541 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6542 xdst.right = (int)dst_surface->resource.width;
6550 xsrc.top -= xdst.top;
6553 if (xdst.bottom > dst_surface->resource.height)
6555 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6556 xdst.bottom = (int)dst_surface->resource.height;
6560 /* And check if after clipping something is still to be done... */
6561 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6562 || (xdst.left >= (int)dst_surface->resource.width)
6563 || (xdst.top >= (int)dst_surface->resource.height)
6564 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6565 || (xsrc.left >= (int)src_surface->resource.width)
6566 || (xsrc.top >= (int)src_surface->resource.height))
6568 TRACE("Nothing to be done after clipping.\n");
6578 xdst.right = dst_surface->resource.width;
6579 xdst.bottom = dst_surface->resource.height;
6582 if (src_surface == dst_surface)
6584 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6586 src_format = dst_surface->resource.format;
6587 dst_format = src_format;
6591 dst_format = dst_surface->resource.format;
6594 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6596 src_surface = surface_convert_format(src_surface, dst_format->id);
6599 /* The conv function writes a FIXME */
6600 WARN("Cannot convert source surface format to dest format.\n");
6604 wined3d_surface_map(src_surface, &slock, NULL, WINED3DLOCK_READONLY);
6605 src_format = src_surface->resource.format;
6609 src_format = dst_format;
6612 wined3d_surface_map(dst_surface, &dlock, &xdst, 0);
6614 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6617 if (!fx || !(fx->dwDDFX)) flags &= ~WINEDDBLT_DDFX;
6619 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_FOURCC)
6621 if (!dst_rect || src_surface == dst_surface)
6623 memcpy(dlock.pBits, slock.pBits, dst_surface->resource.size);
6628 bpp = dst_surface->resource.format->byte_count;
6629 srcheight = xsrc.bottom - xsrc.top;
6630 srcwidth = xsrc.right - xsrc.left;
6631 dstheight = xdst.bottom - xdst.top;
6632 dstwidth = xdst.right - xdst.left;
6633 width = (xdst.right - xdst.left) * bpp;
6635 if (dst_rect && src_surface != dst_surface)
6638 dbuf = (BYTE*)dlock.pBits+(xdst.top*dlock.Pitch)+(xdst.left*bpp);
6640 if (flags & WINEDDBLT_WAIT)
6642 flags &= ~WINEDDBLT_WAIT;
6644 if (flags & WINEDDBLT_ASYNC)
6646 static BOOL displayed = FALSE;
6648 FIXME("Can't handle WINEDDBLT_ASYNC flag right now.\n");
6650 flags &= ~WINEDDBLT_ASYNC;
6652 if (flags & WINEDDBLT_DONOTWAIT)
6654 /* WINEDDBLT_DONOTWAIT appeared in DX7 */
6655 static BOOL displayed = FALSE;
6657 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag right now.\n");
6659 flags &= ~WINEDDBLT_DONOTWAIT;
6662 /* First, all the 'source-less' blits */
6663 if (flags & WINEDDBLT_COLORFILL)
6665 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dlock.Pitch, fx->u5.dwFillColor);
6666 flags &= ~WINEDDBLT_COLORFILL;
6669 if (flags & WINEDDBLT_DEPTHFILL)
6671 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6673 if (flags & WINEDDBLT_ROP)
6675 /* Catch some degenerate cases here. */
6679 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,0);
6681 case 0xAA0029: /* No-op */
6684 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,~0);
6686 case SRCCOPY: /* Well, we do that below? */
6689 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6692 flags &= ~WINEDDBLT_ROP;
6694 if (flags & WINEDDBLT_DDROPS)
6696 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6698 /* Now the 'with source' blits. */
6702 int sx, xinc, sy, yinc;
6704 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6707 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6708 && (srcwidth != dstwidth || srcheight != dstheight))
6710 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6711 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6714 sbase = (BYTE*)slock.pBits+(xsrc.top*slock.Pitch)+xsrc.left*bpp;
6715 xinc = (srcwidth << 16) / dstwidth;
6716 yinc = (srcheight << 16) / dstheight;
6720 /* No effects, we can cheat here. */
6721 if (dstwidth == srcwidth)
6723 if (dstheight == srcheight)
6725 /* No stretching in either direction. This needs to be as
6726 * fast as possible. */
6729 /* Check for overlapping surfaces. */
6730 if (src_surface != dst_surface || xdst.top < xsrc.top
6731 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6733 /* No overlap, or dst above src, so copy from top downwards. */
6734 for (y = 0; y < dstheight; ++y)
6736 memcpy(dbuf, sbuf, width);
6737 sbuf += slock.Pitch;
6738 dbuf += dlock.Pitch;
6741 else if (xdst.top > xsrc.top)
6743 /* Copy from bottom upwards. */
6744 sbuf += (slock.Pitch*dstheight);
6745 dbuf += (dlock.Pitch*dstheight);
6746 for (y = 0; y < dstheight; ++y)
6748 sbuf -= slock.Pitch;
6749 dbuf -= dlock.Pitch;
6750 memcpy(dbuf, sbuf, width);
6755 /* Src and dst overlapping on the same line, use memmove. */
6756 for (y = 0; y < dstheight; ++y)
6758 memmove(dbuf, sbuf, width);
6759 sbuf += slock.Pitch;
6760 dbuf += dlock.Pitch;
6766 /* Stretching in y direction only. */
6767 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6769 sbuf = sbase + (sy >> 16) * slock.Pitch;
6770 memcpy(dbuf, sbuf, width);
6771 dbuf += dlock.Pitch;
6777 /* Stretching in X direction. */
6779 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6781 sbuf = sbase + (sy >> 16) * slock.Pitch;
6783 if ((sy >> 16) == (last_sy >> 16))
6785 /* This source row is the same as last source row -
6786 * Copy the already stretched row. */
6787 memcpy(dbuf, dbuf - dlock.Pitch, width);
6791 #define STRETCH_ROW(type) \
6793 const type *s = (const type *)sbuf; \
6794 type *d = (type *)dbuf; \
6795 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6796 d[x] = s[sx >> 16]; \
6814 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6818 s = sbuf + 3 * (sx >> 16);
6819 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6820 d[0] = (pixel ) & 0xff;
6821 d[1] = (pixel >> 8) & 0xff;
6822 d[2] = (pixel >> 16) & 0xff;
6828 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6829 hr = WINED3DERR_NOTAVAILABLE;
6834 dbuf += dlock.Pitch;
6841 LONG dstyinc = dlock.Pitch, dstxinc = bpp;
6842 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6843 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6844 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6846 /* The color keying flags are checked for correctness in ddraw */
6847 if (flags & WINEDDBLT_KEYSRC)
6849 keylow = src_surface->SrcBltCKey.dwColorSpaceLowValue;
6850 keyhigh = src_surface->SrcBltCKey.dwColorSpaceHighValue;
6852 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6854 keylow = fx->ddckSrcColorkey.dwColorSpaceLowValue;
6855 keyhigh = fx->ddckSrcColorkey.dwColorSpaceHighValue;
6858 if (flags & WINEDDBLT_KEYDEST)
6860 /* Destination color keys are taken from the source surface! */
6861 destkeylow = src_surface->DestBltCKey.dwColorSpaceLowValue;
6862 destkeyhigh = src_surface->DestBltCKey.dwColorSpaceHighValue;
6864 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6866 destkeylow = fx->ddckDestColorkey.dwColorSpaceLowValue;
6867 destkeyhigh = fx->ddckDestColorkey.dwColorSpaceHighValue;
6876 keymask = src_format->red_mask
6877 | src_format->green_mask
6878 | src_format->blue_mask;
6880 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6883 if (flags & WINEDDBLT_DDFX)
6885 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6888 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6889 dBottomLeft = dTopLeft + ((dstheight - 1) * dlock.Pitch);
6890 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6892 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6894 /* I don't think we need to do anything about this flag */
6895 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6897 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6900 dTopRight = dTopLeft;
6903 dBottomRight = dBottomLeft;
6905 dstxinc = dstxinc * -1;
6907 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6910 dTopLeft = dBottomLeft;
6913 dTopRight = dBottomRight;
6915 dstyinc = dstyinc * -1;
6917 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6919 /* I don't think we need to do anything about this flag */
6920 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6922 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6925 dBottomRight = dTopLeft;
6928 dBottomLeft = dTopRight;
6930 dstxinc = dstxinc * -1;
6931 dstyinc = dstyinc * -1;
6933 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6936 dTopLeft = dBottomLeft;
6937 dBottomLeft = dBottomRight;
6938 dBottomRight = dTopRight;
6943 dstxinc = dstxinc * -1;
6945 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6948 dTopLeft = dTopRight;
6949 dTopRight = dBottomRight;
6950 dBottomRight = dBottomLeft;
6955 dstyinc = dstyinc * -1;
6957 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6959 /* I don't think we need to do anything about this flag */
6960 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6963 flags &= ~(WINEDDBLT_DDFX);
6966 #define COPY_COLORKEY_FX(type) \
6969 type *d = (type *)dbuf, *dx, tmp; \
6970 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6972 s = (const type *)(sbase + (sy >> 16) * slock.Pitch); \
6974 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6976 tmp = s[sx >> 16]; \
6977 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6978 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6982 dx = (type *)(((BYTE *)dx) + dstxinc); \
6984 d = (type *)(((BYTE *)d) + dstyinc); \
6991 COPY_COLORKEY_FX(BYTE);
6994 COPY_COLORKEY_FX(WORD);
6997 COPY_COLORKEY_FX(DWORD);
7002 BYTE *d = dbuf, *dx;
7003 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
7005 sbuf = sbase + (sy >> 16) * slock.Pitch;
7007 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
7009 DWORD pixel, dpixel = 0;
7010 s = sbuf + 3 * (sx>>16);
7011 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
7012 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
7013 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
7014 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
7016 dx[0] = (pixel ) & 0xff;
7017 dx[1] = (pixel >> 8) & 0xff;
7018 dx[2] = (pixel >> 16) & 0xff;
7027 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7028 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7029 hr = WINED3DERR_NOTAVAILABLE;
7031 #undef COPY_COLORKEY_FX
7037 if (flags && FIXME_ON(d3d_surface))
7039 FIXME("\tUnsupported flags: %#x.\n", flags);
7043 wined3d_surface_unmap(dst_surface);
7044 if (src_surface && src_surface != dst_surface)
7045 wined3d_surface_unmap(src_surface);
7046 /* Release the converted surface, if any. */
7047 if (src_surface && src_surface != orig_src)
7048 wined3d_surface_decref(src_surface);
7053 static HRESULT surface_cpu_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
7054 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD trans)
7056 const struct wined3d_format *src_format, *dst_format;
7057 RECT lock_src, lock_dst, lock_union;
7058 WINED3DLOCKED_RECT dlock, slock;
7059 HRESULT hr = WINED3D_OK;
7060 int bpp, w, h, x, y;
7065 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect %s, flags %#x.\n",
7066 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect), trans);
7068 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface->flags & SFLAG_LOCKED))
7070 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
7071 return WINEDDERR_SURFACEBUSY;
7076 WARN("src_rect is NULL!\n");
7079 rsrc2.right = src_surface->resource.width;
7080 rsrc2.bottom = src_surface->resource.height;
7084 /* Check source rect for validity. Copied from normal Blt. Fixes Baldur's Gate. */
7085 if ((src_rect->bottom > src_surface->resource.height) || (src_rect->bottom < 0)
7086 || (src_rect->top > src_surface->resource.height) || (src_rect->top < 0)
7087 || (src_rect->left > src_surface->resource.width) || (src_rect->left < 0)
7088 || (src_rect->right > src_surface->resource.width) || (src_rect->right < 0)
7089 || (src_rect->right < src_rect->left) || (src_rect->bottom < src_rect->top))
7091 WARN("Application gave us bad source rectangle for BltFast.\n");
7092 return WINEDDERR_INVALIDRECT;
7095 h = src_rect->bottom - src_rect->top;
7096 if (h > dst_surface->resource.height - dst_y)
7097 h = dst_surface->resource.height - dst_y;
7098 if (h > src_surface->resource.height - src_rect->top)
7099 h = src_surface->resource.height - src_rect->top;
7101 return WINEDDERR_INVALIDRECT;
7103 w = src_rect->right - src_rect->left;
7104 if (w > dst_surface->resource.width - dst_x)
7105 w = dst_surface->resource.width - dst_x;
7106 if (w > src_surface->resource.width - src_rect->left)
7107 w = src_surface->resource.width - src_rect->left;
7109 return WINEDDERR_INVALIDRECT;
7111 /* Now compute the locking rectangle... */
7112 lock_src.left = src_rect->left;
7113 lock_src.top = src_rect->top;
7114 lock_src.right = lock_src.left + w;
7115 lock_src.bottom = lock_src.top + h;
7117 lock_dst.left = dst_x;
7118 lock_dst.top = dst_y;
7119 lock_dst.right = dst_x + w;
7120 lock_dst.bottom = dst_y + h;
7122 bpp = dst_surface->resource.format->byte_count;
7124 /* We need to lock the surfaces, or we won't get refreshes when done. */
7125 if (src_surface == dst_surface)
7129 UnionRect(&lock_union, &lock_src, &lock_dst);
7131 /* Lock the union of the two rectangles. */
7132 hr = wined3d_surface_map(dst_surface, &dlock, &lock_union, 0);
7136 pitch = dlock.Pitch;
7137 slock.Pitch = dlock.Pitch;
7139 /* Since slock was originally copied from this surface's description, we can just reuse it. */
7140 sbuf = dst_surface->resource.allocatedMemory + lock_src.top * pitch + lock_src.left * bpp;
7141 dbuf = dst_surface->resource.allocatedMemory + lock_dst.top * pitch + lock_dst.left * bpp;
7142 src_format = src_surface->resource.format;
7143 dst_format = src_format;
7147 hr = wined3d_surface_map(src_surface, &slock, &lock_src, WINED3DLOCK_READONLY);
7150 hr = wined3d_surface_map(dst_surface, &dlock, &lock_dst, 0);
7156 TRACE("Dst is at %p, Src is at %p.\n", dbuf, sbuf);
7158 src_format = src_surface->resource.format;
7159 dst_format = dst_surface->resource.format;
7162 /* Handle compressed surfaces first... */
7163 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_COMPRESSED)
7165 UINT row_block_count;
7167 TRACE("compressed -> compressed copy\n");
7169 FIXME("trans arg not supported when a compressed surface is involved\n");
7171 FIXME("offset for destination surface is not supported\n");
7172 if (src_surface->resource.format->id != dst_surface->resource.format->id)
7174 FIXME("compressed -> compressed copy only supported for the same type of surface\n");
7175 hr = WINED3DERR_WRONGTEXTUREFORMAT;
7179 row_block_count = (w + dst_format->block_width - 1) / dst_format->block_width;
7180 for (y = 0; y < h; y += dst_format->block_height)
7182 memcpy(dbuf, sbuf, row_block_count * dst_format->block_byte_count);
7183 dbuf += dlock.Pitch;
7184 sbuf += slock.Pitch;
7189 if ((src_format->flags & WINED3DFMT_FLAG_COMPRESSED) && !(dst_format->flags & WINED3DFMT_FLAG_COMPRESSED))
7191 /* TODO: Use the libtxc_dxtn.so shared library to do software
7193 ERR("Software decompression not supported.\n");
7197 if (trans & (WINEDDBLTFAST_SRCCOLORKEY | WINEDDBLTFAST_DESTCOLORKEY))
7199 DWORD keylow, keyhigh;
7200 DWORD mask = src_surface->resource.format->red_mask
7201 | src_surface->resource.format->green_mask
7202 | src_surface->resource.format->blue_mask;
7204 /* For some 8-bit formats like L8 and P8 color masks don't make sense */
7205 if (!mask && bpp == 1)
7208 TRACE("Color keyed copy.\n");
7209 if (trans & WINEDDBLTFAST_SRCCOLORKEY)
7211 keylow = src_surface->SrcBltCKey.dwColorSpaceLowValue;
7212 keyhigh = src_surface->SrcBltCKey.dwColorSpaceHighValue;
7216 /* I'm not sure if this is correct. */
7217 FIXME("WINEDDBLTFAST_DESTCOLORKEY not fully supported yet.\n");
7218 keylow = dst_surface->DestBltCKey.dwColorSpaceLowValue;
7219 keyhigh = dst_surface->DestBltCKey.dwColorSpaceHighValue;
7222 #define COPYBOX_COLORKEY(type) \
7224 const type *s = (const type *)sbuf; \
7225 type *d = (type *)dbuf; \
7227 for (y = 0; y < h; y++) \
7229 for (x = 0; x < w; x++) \
7232 if ((tmp & mask) < keylow || (tmp & mask) > keyhigh) d[x] = tmp; \
7234 s = (const type *)((const BYTE *)s + slock.Pitch); \
7235 d = (type *)((BYTE *)d + dlock.Pitch); \
7242 COPYBOX_COLORKEY(BYTE);
7245 COPYBOX_COLORKEY(WORD);
7248 COPYBOX_COLORKEY(DWORD);
7257 for (y = 0; y < h; ++y)
7259 for (x = 0; x < w * 3; x += 3)
7261 tmp = (DWORD)s[x] + ((DWORD)s[x + 1] << 8) + ((DWORD)s[x + 2] << 16);
7262 if (tmp < keylow || tmp > keyhigh)
7264 d[x + 0] = s[x + 0];
7265 d[x + 1] = s[x + 1];
7266 d[x + 2] = s[x + 2];
7275 FIXME("Source color key blitting not supported for bpp %u.\n", bpp * 8);
7276 hr = WINED3DERR_NOTAVAILABLE;
7279 #undef COPYBOX_COLORKEY
7280 TRACE("Copy done.\n");
7284 int width = w * bpp;
7285 INT sbufpitch, dbufpitch;
7287 TRACE("No color key copy.\n");
7288 /* Handle overlapping surfaces. */
7291 sbuf += (h - 1) * slock.Pitch;
7292 dbuf += (h - 1) * dlock.Pitch;
7293 sbufpitch = -slock.Pitch;
7294 dbufpitch = -dlock.Pitch;
7298 sbufpitch = slock.Pitch;
7299 dbufpitch = dlock.Pitch;
7301 for (y = 0; y < h; ++y)
7303 /* This is pretty easy, a line for line memcpy. */
7304 memmove(dbuf, sbuf, width);
7308 TRACE("Copy done.\n");
7312 if (src_surface == dst_surface)
7314 wined3d_surface_unmap(dst_surface);
7318 wined3d_surface_unmap(dst_surface);
7319 wined3d_surface_unmap(src_surface);
7325 /* Do not call while under the GL lock. */
7326 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7327 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
7331 memset(&BltFx, 0, sizeof(BltFx));
7332 BltFx.dwSize = sizeof(BltFx);
7333 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface->resource.format, color);
7334 return wined3d_surface_blt(dst_surface, dst_rect, NULL, NULL,
7335 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7338 /* Do not call while under the GL lock. */
7339 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7340 struct wined3d_surface *surface, const RECT *rect, float depth)
7342 FIXME("Depth filling not implemented by cpu_blit.\n");
7343 return WINED3DERR_INVALIDCALL;
7346 const struct blit_shader cpu_blit = {
7352 cpu_blit_color_fill,
7353 cpu_blit_depth_fill,
7356 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7357 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
7358 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7359 WINED3DPOOL pool, void *parent, const struct wined3d_parent_ops *parent_ops)
7361 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7362 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7363 unsigned int resource_size;
7366 if (multisample_quality > 0)
7368 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7369 multisample_quality = 0;
7372 /* Quick lockable sanity check.
7373 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7374 * this function is too deep to need to care about things like this.
7375 * Levels need to be checked too, since they all affect what can be done. */
7378 case WINED3DPOOL_SCRATCH:
7381 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7382 "which are mutually exclusive, setting lockable to TRUE.\n");
7387 case WINED3DPOOL_SYSTEMMEM:
7389 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7392 case WINED3DPOOL_MANAGED:
7393 if (usage & WINED3DUSAGE_DYNAMIC)
7394 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7397 case WINED3DPOOL_DEFAULT:
7398 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7399 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7403 FIXME("Unknown pool %#x.\n", pool);
7407 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7408 FIXME("Trying to create a render target that isn't in the default pool.\n");
7410 /* FIXME: Check that the format is supported by the device. */
7412 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7414 return WINED3DERR_INVALIDCALL;
7416 surface->surface_type = surface_type;
7418 switch (surface_type)
7420 case SURFACE_OPENGL:
7421 surface->surface_ops = &surface_ops;
7425 surface->surface_ops = &gdi_surface_ops;
7429 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7430 return WINED3DERR_INVALIDCALL;
7433 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7434 multisample_type, multisample_quality, usage, pool, width, height, 1,
7435 resource_size, parent, parent_ops, &surface_resource_ops);
7438 WARN("Failed to initialize resource, returning %#x.\n", hr);
7442 /* "Standalone" surface. */
7443 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7445 surface->texture_level = level;
7446 list_init(&surface->overlays);
7449 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7451 surface->flags |= SFLAG_DISCARD;
7452 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7453 surface->flags |= SFLAG_LOCKABLE;
7454 /* I'm not sure if this qualifies as a hack or as an optimization. It
7455 * seems reasonable to assume that lockable render targets will get
7456 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7457 * creation. However, the other reason we want to do this is that several
7458 * ddraw applications access surface memory while the surface isn't
7459 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7460 * future locks prevents these from crashing. */
7461 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7462 surface->flags |= SFLAG_DYNLOCK;
7464 /* Mark the texture as dirty so that it gets loaded first time around. */
7465 surface_add_dirty_rect(surface, NULL);
7466 list_init(&surface->renderbuffers);
7468 TRACE("surface %p, memory %p, size %u\n",
7469 surface, surface->resource.allocatedMemory, surface->resource.size);
7471 /* Call the private setup routine */
7472 hr = surface->surface_ops->surface_private_setup(surface);
7475 ERR("Private setup failed, returning %#x\n", hr);
7476 surface->surface_ops->surface_cleanup(surface);
7483 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7484 enum wined3d_format_id format_id, BOOL lockable, BOOL discard, UINT level, DWORD usage, WINED3DPOOL pool,
7485 WINED3DMULTISAMPLE_TYPE multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7486 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7488 struct wined3d_surface *object;
7491 TRACE("device %p, width %u, height %u, format %s, lockable %#x, discard %#x, level %u\n",
7492 device, width, height, debug_d3dformat(format_id), lockable, discard, level);
7493 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7494 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7495 TRACE("surface_type %#x, parent %p, parent_ops %p.\n", surface_type, parent, parent_ops);
7497 if (surface_type == SURFACE_OPENGL && !device->adapter)
7499 ERR("OpenGL surfaces are not available without OpenGL.\n");
7500 return WINED3DERR_NOTAVAILABLE;
7503 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7506 ERR("Failed to allocate surface memory.\n");
7507 return WINED3DERR_OUTOFVIDEOMEMORY;
7510 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level, lockable,
7511 discard, multisample_type, multisample_quality, device, usage, format_id, pool, parent, parent_ops);
7514 WARN("Failed to initialize surface, returning %#x.\n", hr);
7515 HeapFree(GetProcessHeap(), 0, object);
7519 TRACE("Created surface %p.\n", object);