2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d);
36 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
37 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
38 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter);
39 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
40 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *fx,
41 WINED3DTEXTUREFILTERTYPE filter);
43 static void surface_cleanup(struct wined3d_surface *surface)
45 TRACE("surface %p.\n", surface);
47 if (surface->texture_name || (surface->flags & SFLAG_PBO) || !list_empty(&surface->renderbuffers))
49 struct wined3d_renderbuffer_entry *entry, *entry2;
50 const struct wined3d_gl_info *gl_info;
51 struct wined3d_context *context;
53 context = context_acquire(surface->resource.device, NULL);
54 gl_info = context->gl_info;
58 if (surface->texture_name)
60 TRACE("Deleting texture %u.\n", surface->texture_name);
61 glDeleteTextures(1, &surface->texture_name);
64 if (surface->flags & SFLAG_PBO)
66 TRACE("Deleting PBO %u.\n", surface->pbo);
67 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
70 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
72 TRACE("Deleting renderbuffer %u.\n", entry->id);
73 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
74 HeapFree(GetProcessHeap(), 0, entry);
79 context_release(context);
82 if (surface->flags & SFLAG_DIBSECTION)
85 SelectObject(surface->hDC, surface->dib.holdbitmap);
86 DeleteDC(surface->hDC);
87 /* Release the DIB section. */
88 DeleteObject(surface->dib.DIBsection);
89 surface->dib.bitmap_data = NULL;
90 surface->resource.allocatedMemory = NULL;
93 if (surface->flags & SFLAG_USERPTR)
94 wined3d_surface_set_mem(surface, NULL);
95 if (surface->overlay_dest)
96 list_remove(&surface->overlay_entry);
98 HeapFree(GetProcessHeap(), 0, surface->palette9);
100 resource_cleanup(&surface->resource);
103 void surface_set_container(struct wined3d_surface *surface, enum wined3d_container_type type, void *container)
105 TRACE("surface %p, container %p.\n", surface, container);
107 if (!container && type != WINED3D_CONTAINER_NONE)
108 ERR("Setting NULL container of type %#x.\n", type);
110 if (type == WINED3D_CONTAINER_SWAPCHAIN)
112 surface->get_drawable_size = get_drawable_size_swapchain;
116 switch (wined3d_settings.offscreen_rendering_mode)
119 surface->get_drawable_size = get_drawable_size_fbo;
123 surface->get_drawable_size = get_drawable_size_backbuffer;
127 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
132 surface->container.type = type;
133 surface->container.u.base = container;
140 enum tex_types tex_type;
141 GLfloat coords[4][3];
152 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
154 f->l = ((r->left * 2.0f) / w) - 1.0f;
155 f->t = ((r->top * 2.0f) / h) - 1.0f;
156 f->r = ((r->right * 2.0f) / w) - 1.0f;
157 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
160 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
162 GLfloat (*coords)[3] = info->coords;
168 FIXME("Unsupported texture target %#x\n", target);
169 /* Fall back to GL_TEXTURE_2D */
171 info->binding = GL_TEXTURE_BINDING_2D;
172 info->bind_target = GL_TEXTURE_2D;
173 info->tex_type = tex_2d;
174 coords[0][0] = (float)rect->left / w;
175 coords[0][1] = (float)rect->top / h;
178 coords[1][0] = (float)rect->right / w;
179 coords[1][1] = (float)rect->top / h;
182 coords[2][0] = (float)rect->left / w;
183 coords[2][1] = (float)rect->bottom / h;
186 coords[3][0] = (float)rect->right / w;
187 coords[3][1] = (float)rect->bottom / h;
191 case GL_TEXTURE_RECTANGLE_ARB:
192 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
193 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
194 info->tex_type = tex_rect;
195 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
196 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
197 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
198 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
201 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
202 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
203 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
204 info->tex_type = tex_cube;
205 cube_coords_float(rect, w, h, &f);
207 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
208 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
209 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
210 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
213 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
214 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
215 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
216 info->tex_type = tex_cube;
217 cube_coords_float(rect, w, h, &f);
219 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
220 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
221 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
222 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
225 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
226 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
227 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
228 info->tex_type = tex_cube;
229 cube_coords_float(rect, w, h, &f);
231 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
232 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
233 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
234 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
237 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
238 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
239 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
240 info->tex_type = tex_cube;
241 cube_coords_float(rect, w, h, &f);
243 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
244 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
245 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
246 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
249 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
250 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
251 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
252 info->tex_type = tex_cube;
253 cube_coords_float(rect, w, h, &f);
255 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
256 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
257 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
258 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
261 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
262 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
263 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
264 info->tex_type = tex_cube;
265 cube_coords_float(rect, w, h, &f);
267 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
268 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
269 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
270 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
275 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
278 *rect_out = *rect_in;
283 rect_out->right = surface->resource.width;
284 rect_out->bottom = surface->resource.height;
288 /* GL locking and context activation is done by the caller */
289 void draw_textured_quad(const struct wined3d_surface *src_surface, const RECT *src_rect,
290 const RECT *dst_rect, WINED3DTEXTUREFILTERTYPE Filter)
292 struct blt_info info;
294 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
296 glEnable(info.bind_target);
297 checkGLcall("glEnable(bind_target)");
299 /* Bind the texture */
300 glBindTexture(info.bind_target, src_surface->texture_name);
301 checkGLcall("glBindTexture");
303 /* Filtering for StretchRect */
304 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
305 wined3d_gl_mag_filter(magLookup, Filter));
306 checkGLcall("glTexParameteri");
307 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
308 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
309 checkGLcall("glTexParameteri");
310 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
311 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
312 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
313 checkGLcall("glTexEnvi");
316 glBegin(GL_TRIANGLE_STRIP);
317 glTexCoord3fv(info.coords[0]);
318 glVertex2i(dst_rect->left, dst_rect->top);
320 glTexCoord3fv(info.coords[1]);
321 glVertex2i(dst_rect->right, dst_rect->top);
323 glTexCoord3fv(info.coords[2]);
324 glVertex2i(dst_rect->left, dst_rect->bottom);
326 glTexCoord3fv(info.coords[3]);
327 glVertex2i(dst_rect->right, dst_rect->bottom);
330 /* Unbind the texture */
331 glBindTexture(info.bind_target, 0);
332 checkGLcall("glBindTexture(info->bind_target, 0)");
334 /* We changed the filtering settings on the texture. Inform the
335 * container about this to get the filters reset properly next draw. */
336 if (src_surface->container.type == WINED3D_CONTAINER_TEXTURE)
338 struct wined3d_texture *texture = src_surface->container.u.texture;
339 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
340 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
341 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
345 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
347 const struct wined3d_format *format = surface->resource.format;
355 TRACE("surface %p.\n", surface);
357 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
359 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
360 return WINED3DERR_INVALIDCALL;
363 switch (format->byte_count)
367 /* Allocate extra space to store the RGB bit masks. */
368 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
372 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
376 /* Allocate extra space for a palette. */
377 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
378 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
383 return E_OUTOFMEMORY;
385 /* Some applications access the surface in via DWORDs, and do not take
386 * the necessary care at the end of the surface. So we need at least
387 * 4 extra bytes at the end of the surface. Check against the page size,
388 * if the last page used for the surface has at least 4 spare bytes we're
389 * safe, otherwise add an extra line to the DIB section. */
390 GetSystemInfo(&sysInfo);
391 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
394 TRACE("Adding an extra line to the DIB section.\n");
397 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
398 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
399 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
400 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
401 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
402 * wined3d_surface_get_pitch(surface);
403 b_info->bmiHeader.biPlanes = 1;
404 b_info->bmiHeader.biBitCount = format->byte_count * 8;
406 b_info->bmiHeader.biXPelsPerMeter = 0;
407 b_info->bmiHeader.biYPelsPerMeter = 0;
408 b_info->bmiHeader.biClrUsed = 0;
409 b_info->bmiHeader.biClrImportant = 0;
411 /* Get the bit masks */
412 masks = (DWORD *)b_info->bmiColors;
413 switch (surface->resource.format->id)
415 case WINED3DFMT_B8G8R8_UNORM:
416 usage = DIB_RGB_COLORS;
417 b_info->bmiHeader.biCompression = BI_RGB;
420 case WINED3DFMT_B5G5R5X1_UNORM:
421 case WINED3DFMT_B5G5R5A1_UNORM:
422 case WINED3DFMT_B4G4R4A4_UNORM:
423 case WINED3DFMT_B4G4R4X4_UNORM:
424 case WINED3DFMT_B2G3R3_UNORM:
425 case WINED3DFMT_B2G3R3A8_UNORM:
426 case WINED3DFMT_R10G10B10A2_UNORM:
427 case WINED3DFMT_R8G8B8A8_UNORM:
428 case WINED3DFMT_R8G8B8X8_UNORM:
429 case WINED3DFMT_B10G10R10A2_UNORM:
430 case WINED3DFMT_B5G6R5_UNORM:
431 case WINED3DFMT_R16G16B16A16_UNORM:
433 b_info->bmiHeader.biCompression = BI_BITFIELDS;
434 masks[0] = format->red_mask;
435 masks[1] = format->green_mask;
436 masks[2] = format->blue_mask;
440 /* Don't know palette */
441 b_info->bmiHeader.biCompression = BI_RGB;
446 if (!(dc = GetDC(0)))
448 HeapFree(GetProcessHeap(), 0, b_info);
449 return HRESULT_FROM_WIN32(GetLastError());
452 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
453 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
454 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
455 surface->dib.DIBsection = CreateDIBSection(dc, b_info, usage, &surface->dib.bitmap_data, 0, 0);
458 if (!surface->dib.DIBsection)
460 ERR("Failed to create DIB section.\n");
461 HeapFree(GetProcessHeap(), 0, b_info);
462 return HRESULT_FROM_WIN32(GetLastError());
465 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
466 /* Copy the existing surface to the dib section. */
467 if (surface->resource.allocatedMemory)
469 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
470 surface->resource.height * wined3d_surface_get_pitch(surface));
474 /* This is to make maps read the GL texture although memory is allocated. */
475 surface->flags &= ~SFLAG_INSYSMEM;
477 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
479 HeapFree(GetProcessHeap(), 0, b_info);
481 /* Now allocate a DC. */
482 surface->hDC = CreateCompatibleDC(0);
483 surface->dib.holdbitmap = SelectObject(surface->hDC, surface->dib.DIBsection);
484 TRACE("Using wined3d palette %p.\n", surface->palette);
485 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
487 surface->flags |= SFLAG_DIBSECTION;
489 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
490 surface->resource.heapMemory = NULL;
495 static void surface_prepare_system_memory(struct wined3d_surface *surface)
497 struct wined3d_device *device = surface->resource.device;
498 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
500 TRACE("surface %p.\n", surface);
502 /* Performance optimization: Count how often a surface is locked, if it is
503 * locked regularly do not throw away the system memory copy. This avoids
504 * the need to download the surface from OpenGL all the time. The surface
505 * is still downloaded if the OpenGL texture is changed. */
506 if (!(surface->flags & SFLAG_DYNLOCK))
508 if (++surface->lockCount > MAXLOCKCOUNT)
510 TRACE("Surface is locked regularly, not freeing the system memory copy any more.\n");
511 surface->flags |= SFLAG_DYNLOCK;
515 /* Create a PBO for dynamically locked surfaces but don't do it for
516 * converted or NPOT surfaces. Also don't create a PBO for systemmem
518 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (surface->flags & SFLAG_DYNLOCK)
519 && !(surface->flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
520 && (surface->resource.pool != WINED3DPOOL_SYSTEMMEM))
522 struct wined3d_context *context;
525 context = context_acquire(device, NULL);
528 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
529 error = glGetError();
530 if (!surface->pbo || error != GL_NO_ERROR)
531 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
533 TRACE("Binding PBO %u.\n", surface->pbo);
535 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
536 checkGLcall("glBindBufferARB");
538 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
539 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
540 checkGLcall("glBufferDataARB");
542 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
543 checkGLcall("glBindBufferARB");
545 /* We don't need the system memory anymore and we can't even use it for PBOs. */
546 if (!(surface->flags & SFLAG_CLIENT))
548 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
549 surface->resource.heapMemory = NULL;
551 surface->resource.allocatedMemory = NULL;
552 surface->flags |= SFLAG_PBO;
554 context_release(context);
556 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
558 /* Whatever surface we have, make sure that there is memory allocated
559 * for the downloaded copy, or a PBO to map. */
560 if (!surface->resource.heapMemory)
561 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
563 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
564 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
566 if (surface->flags & SFLAG_INSYSMEM)
567 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
571 static void surface_evict_sysmem(struct wined3d_surface *surface)
573 if (surface->flags & SFLAG_DONOTFREE)
576 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
577 surface->resource.allocatedMemory = NULL;
578 surface->resource.heapMemory = NULL;
579 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
582 /* Context activation is done by the caller. */
583 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
584 const struct wined3d_gl_info *gl_info, BOOL srgb)
586 struct wined3d_device *device = surface->resource.device;
587 DWORD active_sampler;
588 GLint active_texture;
590 /* We don't need a specific texture unit, but after binding the texture
591 * the current unit is dirty. Read the unit back instead of switching to
592 * 0, this avoids messing around with the state manager's GL states. The
593 * current texture unit should always be a valid one.
595 * To be more specific, this is tricky because we can implicitly be
596 * called from sampler() in state.c. This means we can't touch anything
597 * other than whatever happens to be the currently active texture, or we
598 * would risk marking already applied sampler states dirty again.
600 * TODO: Track the current active texture per GL context instead of using
604 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
606 active_sampler = device->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
608 if (active_sampler != WINED3D_UNMAPPED_STAGE)
609 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
610 surface_bind(surface, gl_info, srgb);
613 static void surface_force_reload(struct wined3d_surface *surface)
615 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
618 static void surface_release_client_storage(struct wined3d_surface *surface)
620 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
623 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
624 if (surface->texture_name)
626 surface_bind_and_dirtify(surface, context->gl_info, FALSE);
627 glTexImage2D(surface->texture_target, surface->texture_level,
628 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
630 if (surface->texture_name_srgb)
632 surface_bind_and_dirtify(surface, context->gl_info, TRUE);
633 glTexImage2D(surface->texture_target, surface->texture_level,
634 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
636 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
639 context_release(context);
641 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
642 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
643 surface_force_reload(surface);
646 static HRESULT surface_private_setup(struct wined3d_surface *surface)
648 /* TODO: Check against the maximum texture sizes supported by the video card. */
649 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
650 unsigned int pow2Width, pow2Height;
652 TRACE("surface %p.\n", surface);
654 surface->texture_name = 0;
655 surface->texture_target = GL_TEXTURE_2D;
657 /* Non-power2 support */
658 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
660 pow2Width = surface->resource.width;
661 pow2Height = surface->resource.height;
665 /* Find the nearest pow2 match */
666 pow2Width = pow2Height = 1;
667 while (pow2Width < surface->resource.width)
669 while (pow2Height < surface->resource.height)
672 surface->pow2Width = pow2Width;
673 surface->pow2Height = pow2Height;
675 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
677 /* TODO: Add support for non power two compressed textures. */
678 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
680 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
681 surface, surface->resource.width, surface->resource.height);
682 return WINED3DERR_NOTAVAILABLE;
686 if (pow2Width != surface->resource.width
687 || pow2Height != surface->resource.height)
689 surface->flags |= SFLAG_NONPOW2;
692 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
693 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
695 /* One of three options:
696 * 1: Do the same as we do with NPOT and scale the texture, (any
697 * texture ops would require the texture to be scaled which is
699 * 2: Set the texture to the maximum size (bad idea).
700 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
701 * 4: Create the surface, but allow it to be used only for DirectDraw
702 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
703 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
704 * the render target. */
705 if (surface->resource.pool == WINED3DPOOL_DEFAULT || surface->resource.pool == WINED3DPOOL_MANAGED)
707 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
708 return WINED3DERR_NOTAVAILABLE;
711 /* We should never use this surface in combination with OpenGL! */
712 TRACE("Creating an oversized surface: %ux%u.\n",
713 surface->pow2Width, surface->pow2Height);
717 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
718 * and EXT_PALETTED_TEXTURE is used in combination with texture
719 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
720 * EXT_PALETTED_TEXTURE doesn't work in combination with
721 * ARB_TEXTURE_RECTANGLE. */
722 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
723 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
724 && gl_info->supported[EXT_PALETTED_TEXTURE]
725 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
727 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
728 surface->pow2Width = surface->resource.width;
729 surface->pow2Height = surface->resource.height;
730 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
734 switch (wined3d_settings.offscreen_rendering_mode)
737 surface->get_drawable_size = get_drawable_size_fbo;
741 surface->get_drawable_size = get_drawable_size_backbuffer;
745 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
746 return WINED3DERR_INVALIDCALL;
749 surface->flags |= SFLAG_INSYSMEM;
754 static void surface_realize_palette(struct wined3d_surface *surface)
756 struct wined3d_palette *palette = surface->palette;
758 TRACE("surface %p.\n", surface);
760 if (!palette) return;
762 if (surface->resource.format->id == WINED3DFMT_P8_UINT
763 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
765 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
767 /* Make sure the texture is up to date. This call doesn't do
768 * anything if the texture is already up to date. */
769 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
771 /* We want to force a palette refresh, so mark the drawable as not being up to date */
772 if (!surface_is_offscreen(surface))
773 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
777 if (!(surface->flags & SFLAG_INSYSMEM))
779 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
780 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
782 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
786 if (surface->flags & SFLAG_DIBSECTION)
791 TRACE("Updating the DC's palette.\n");
793 for (i = 0; i < 256; ++i)
795 col[i].rgbRed = palette->palents[i].peRed;
796 col[i].rgbGreen = palette->palents[i].peGreen;
797 col[i].rgbBlue = palette->palents[i].peBlue;
798 col[i].rgbReserved = 0;
800 SetDIBColorTable(surface->hDC, 0, 256, col);
803 /* Propagate the changes to the drawable when we have a palette. */
804 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
805 surface_load_location(surface, SFLAG_INDRAWABLE, NULL);
808 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
812 /* If there's no destination surface there is nothing to do. */
813 if (!surface->overlay_dest)
816 /* Blt calls ModifyLocation on the dest surface, which in turn calls
817 * DrawOverlay to update the overlay. Prevent an endless recursion. */
818 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
821 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
822 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
823 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3DTEXF_LINEAR);
824 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
829 static void surface_preload(struct wined3d_surface *surface)
831 TRACE("surface %p.\n", surface);
833 surface_internal_preload(surface, SRGB_ANY);
836 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
838 struct wined3d_device *device = surface->resource.device;
839 const RECT *pass_rect = rect;
841 TRACE("surface %p, rect %s, flags %#x.\n",
842 surface, wine_dbgstr_rect(rect), flags);
844 if (flags & WINED3DLOCK_DISCARD)
846 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
847 surface_prepare_system_memory(surface);
848 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
852 /* surface_load_location() does not check if the rectangle specifies
853 * the full surface. Most callers don't need that, so do it here. */
854 if (rect && !rect->top && !rect->left
855 && rect->right == surface->resource.width
856 && rect->bottom == surface->resource.height)
859 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
860 && ((surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
861 || surface == device->fb.render_targets[0])))
862 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
865 if (surface->flags & SFLAG_PBO)
867 const struct wined3d_gl_info *gl_info;
868 struct wined3d_context *context;
870 context = context_acquire(device, NULL);
871 gl_info = context->gl_info;
874 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
875 checkGLcall("glBindBufferARB");
877 /* This shouldn't happen but could occur if some other function
878 * didn't handle the PBO properly. */
879 if (surface->resource.allocatedMemory)
880 ERR("The surface already has PBO memory allocated.\n");
882 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
883 checkGLcall("glMapBufferARB");
885 /* Make sure the PBO isn't set anymore in order not to break non-PBO
887 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
888 checkGLcall("glBindBufferARB");
891 context_release(context);
894 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
897 surface_add_dirty_rect(surface, NULL);
904 b.Right = rect->right;
905 b.Bottom = rect->bottom;
908 surface_add_dirty_rect(surface, &b);
913 static void surface_unmap(struct wined3d_surface *surface)
915 struct wined3d_device *device = surface->resource.device;
918 TRACE("surface %p.\n", surface);
920 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
922 if (surface->flags & SFLAG_PBO)
924 const struct wined3d_gl_info *gl_info;
925 struct wined3d_context *context;
927 TRACE("Freeing PBO memory.\n");
929 context = context_acquire(device, NULL);
930 gl_info = context->gl_info;
933 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
934 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
935 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
936 checkGLcall("glUnmapBufferARB");
938 context_release(context);
940 surface->resource.allocatedMemory = NULL;
943 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
945 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
947 TRACE("Not dirtified, nothing to do.\n");
951 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
952 || (device->fb.render_targets && surface == device->fb.render_targets[0]))
954 if (wined3d_settings.rendertargetlock_mode == RTL_DISABLE)
956 static BOOL warned = FALSE;
959 ERR("The application tries to write to the render target, but render target locking is disabled.\n");
965 if (!surface->dirtyRect.left && !surface->dirtyRect.top
966 && surface->dirtyRect.right == surface->resource.width
967 && surface->dirtyRect.bottom == surface->resource.height)
973 /* TODO: Proper partial rectangle tracking. */
975 surface->flags |= SFLAG_INSYSMEM;
978 surface_load_location(surface, SFLAG_INDRAWABLE, fullsurface ? NULL : &surface->dirtyRect);
980 /* Partial rectangle tracking is not commonly implemented, it is only
981 * done for render targets. INSYSMEM was set before to tell
982 * surface_load_location() where to read the rectangle from.
983 * Indrawable is set because all modifications from the partial
984 * sysmem copy are written back to the drawable, thus the surface is
985 * merged again in the drawable. The sysmem copy is not fully up to
986 * date because only a subrectangle was read in Map(). */
989 surface_modify_location(surface, SFLAG_INDRAWABLE, TRUE);
990 surface_evict_sysmem(surface);
993 surface->dirtyRect.left = surface->resource.width;
994 surface->dirtyRect.top = surface->resource.height;
995 surface->dirtyRect.right = 0;
996 surface->dirtyRect.bottom = 0;
998 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
1000 FIXME("Depth / stencil buffer locking is not implemented.\n");
1004 /* Overlays have to be redrawn manually after changes with the GL implementation */
1005 if (surface->overlay_dest)
1006 surface->surface_ops->surface_draw_overlay(surface);
1009 static HRESULT surface_getdc(struct wined3d_surface *surface)
1011 WINED3DLOCKED_RECT lock;
1014 TRACE("surface %p.\n", surface);
1016 /* Create a DIB section if there isn't a dc yet. */
1019 if (surface->flags & SFLAG_CLIENT)
1021 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1022 surface_release_client_storage(surface);
1024 hr = surface_create_dib_section(surface);
1026 return WINED3DERR_INVALIDCALL;
1028 /* Use the DIB section from now on if we are not using a PBO. */
1029 if (!(surface->flags & SFLAG_PBO))
1030 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1033 /* Map the surface. */
1034 hr = wined3d_surface_map(surface, &lock, NULL, 0);
1036 ERR("Map failed, hr %#x.\n", hr);
1038 /* Sync the DIB with the PBO. This can't be done earlier because Map()
1039 * activates the allocatedMemory. */
1040 if (surface->flags & SFLAG_PBO)
1041 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->dib.bitmap_size);
1046 static HRESULT surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override)
1048 TRACE("surface %p, override %p.\n", surface, override);
1050 /* Flipping is only supported on render targets and overlays. */
1051 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
1053 WARN("Tried to flip a non-render target, non-overlay surface.\n");
1054 return WINEDDERR_NOTFLIPPABLE;
1057 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1059 flip_surface(surface, override);
1061 /* Update the overlay if it is visible */
1062 if (surface->overlay_dest)
1063 return surface->surface_ops->surface_draw_overlay(surface);
1071 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1073 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1075 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1080 static void wined3d_surface_depth_blt_fbo(struct wined3d_device *device, struct wined3d_surface *src_surface,
1081 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1083 const struct wined3d_gl_info *gl_info;
1084 struct wined3d_context *context;
1085 DWORD src_mask, dst_mask;
1088 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1089 device, src_surface, wine_dbgstr_rect(src_rect),
1090 dst_surface, wine_dbgstr_rect(dst_rect));
1092 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1093 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1095 if (src_mask != dst_mask)
1097 ERR("Incompatible formats %s and %s.\n",
1098 debug_d3dformat(src_surface->resource.format->id),
1099 debug_d3dformat(dst_surface->resource.format->id));
1105 ERR("Not a depth / stencil format: %s.\n",
1106 debug_d3dformat(src_surface->resource.format->id));
1111 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1112 gl_mask |= GL_DEPTH_BUFFER_BIT;
1113 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1114 gl_mask |= GL_STENCIL_BUFFER_BIT;
1116 /* Make sure the locations are up-to-date. Loading the destination
1117 * surface isn't required if the entire surface is overwritten. */
1118 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1119 if (!surface_is_full_rect(dst_surface, dst_rect))
1120 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1122 context = context_acquire(device, NULL);
1123 if (!context->valid)
1125 context_release(context);
1126 WARN("Invalid context, skipping blit.\n");
1130 gl_info = context->gl_info;
1134 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1135 glReadBuffer(GL_NONE);
1136 checkGLcall("glReadBuffer()");
1137 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1139 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1140 context_set_draw_buffer(context, GL_NONE);
1141 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1143 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1145 glDepthMask(GL_TRUE);
1146 context_invalidate_state(context, STATE_RENDER(WINED3DRS_ZWRITEENABLE));
1148 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1150 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1152 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1153 context_invalidate_state(context, STATE_RENDER(WINED3DRS_TWOSIDEDSTENCILMODE));
1156 context_invalidate_state(context, STATE_RENDER(WINED3DRS_STENCILWRITEMASK));
1159 glDisable(GL_SCISSOR_TEST);
1160 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1162 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1163 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1164 checkGLcall("glBlitFramebuffer()");
1168 if (wined3d_settings.strict_draw_ordering)
1169 wglFlush(); /* Flush to ensure ordering across contexts. */
1171 context_release(context);
1174 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1175 * Depth / stencil is not supported. */
1176 static void surface_blt_fbo(struct wined3d_device *device, const WINED3DTEXTUREFILTERTYPE filter,
1177 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1178 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1180 const struct wined3d_gl_info *gl_info;
1181 struct wined3d_context *context;
1182 RECT src_rect, dst_rect;
1186 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1187 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1188 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1189 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1190 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1192 src_rect = *src_rect_in;
1193 dst_rect = *dst_rect_in;
1197 case WINED3DTEXF_LINEAR:
1198 gl_filter = GL_LINEAR;
1202 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1203 case WINED3DTEXF_NONE:
1204 case WINED3DTEXF_POINT:
1205 gl_filter = GL_NEAREST;
1209 if (src_location == SFLAG_INDRAWABLE && surface_is_offscreen(src_surface))
1210 src_location = SFLAG_INTEXTURE;
1211 if (dst_location == SFLAG_INDRAWABLE && surface_is_offscreen(dst_surface))
1212 dst_location = SFLAG_INTEXTURE;
1214 /* Make sure the locations are up-to-date. Loading the destination
1215 * surface isn't required if the entire surface is overwritten. (And is
1216 * in fact harmful if we're being called by surface_load_location() with
1217 * the purpose of loading the destination surface.) */
1218 surface_load_location(src_surface, src_location, NULL);
1219 if (!surface_is_full_rect(dst_surface, &dst_rect))
1220 surface_load_location(dst_surface, dst_location, NULL);
1222 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1223 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1224 else context = context_acquire(device, NULL);
1226 if (!context->valid)
1228 context_release(context);
1229 WARN("Invalid context, skipping blit.\n");
1233 gl_info = context->gl_info;
1235 if (src_location == SFLAG_INDRAWABLE)
1237 TRACE("Source surface %p is onscreen.\n", src_surface);
1238 buffer = surface_get_gl_buffer(src_surface);
1239 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1243 TRACE("Source surface %p is offscreen.\n", src_surface);
1244 buffer = GL_COLOR_ATTACHMENT0;
1248 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1249 glReadBuffer(buffer);
1250 checkGLcall("glReadBuffer()");
1251 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1254 if (dst_location == SFLAG_INDRAWABLE)
1256 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1257 buffer = surface_get_gl_buffer(dst_surface);
1258 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1262 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1263 buffer = GL_COLOR_ATTACHMENT0;
1267 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1268 context_set_draw_buffer(context, buffer);
1269 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1270 context_invalidate_state(context, STATE_FRAMEBUFFER);
1272 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1273 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE));
1274 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE1));
1275 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE2));
1276 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE3));
1278 glDisable(GL_SCISSOR_TEST);
1279 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1281 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1282 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1283 checkGLcall("glBlitFramebuffer()");
1287 if (wined3d_settings.strict_draw_ordering
1288 || (dst_location == SFLAG_INDRAWABLE
1289 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1292 context_release(context);
1295 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1296 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
1297 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
1299 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1302 /* Source and/or destination need to be on the GL side */
1303 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
1308 case WINED3D_BLIT_OP_COLOR_BLIT:
1309 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1311 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1315 case WINED3D_BLIT_OP_DEPTH_BLIT:
1316 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1318 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1326 if (!(src_format->id == dst_format->id
1327 || (is_identity_fixup(src_format->color_fixup)
1328 && is_identity_fixup(dst_format->color_fixup))))
1334 /* This function checks if the primary render target uses the 8bit paletted format. */
1335 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1337 if (device->fb.render_targets && device->fb.render_targets[0])
1339 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1340 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1341 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1347 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1348 DWORD color, WINED3DCOLORVALUE *float_color)
1350 const struct wined3d_format *format = surface->resource.format;
1351 const struct wined3d_device *device = surface->resource.device;
1355 case WINED3DFMT_P8_UINT:
1356 if (surface->palette)
1358 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1359 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1360 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1364 float_color->r = 0.0f;
1365 float_color->g = 0.0f;
1366 float_color->b = 0.0f;
1368 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1371 case WINED3DFMT_B5G6R5_UNORM:
1372 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1373 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1374 float_color->b = (color & 0x1f) / 31.0f;
1375 float_color->a = 1.0f;
1378 case WINED3DFMT_B8G8R8_UNORM:
1379 case WINED3DFMT_B8G8R8X8_UNORM:
1380 float_color->r = D3DCOLOR_R(color);
1381 float_color->g = D3DCOLOR_G(color);
1382 float_color->b = D3DCOLOR_B(color);
1383 float_color->a = 1.0f;
1386 case WINED3DFMT_B8G8R8A8_UNORM:
1387 float_color->r = D3DCOLOR_R(color);
1388 float_color->g = D3DCOLOR_G(color);
1389 float_color->b = D3DCOLOR_B(color);
1390 float_color->a = D3DCOLOR_A(color);
1394 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1401 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1403 const struct wined3d_format *format = surface->resource.format;
1407 case WINED3DFMT_S1_UINT_D15_UNORM:
1408 *float_depth = depth / (float)0x00007fff;
1411 case WINED3DFMT_D16_UNORM:
1412 *float_depth = depth / (float)0x0000ffff;
1415 case WINED3DFMT_D24_UNORM_S8_UINT:
1416 case WINED3DFMT_X8D24_UNORM:
1417 *float_depth = depth / (float)0x00ffffff;
1420 case WINED3DFMT_D32_UNORM:
1421 *float_depth = depth / (float)0xffffffff;
1425 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1432 /* Do not call while under the GL lock. */
1433 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1435 const struct wined3d_resource *resource = &surface->resource;
1436 struct wined3d_device *device = resource->device;
1437 const struct blit_shader *blitter;
1439 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1440 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1443 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1444 return WINED3DERR_INVALIDCALL;
1447 return blitter->depth_fill(device, surface, rect, depth);
1450 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1451 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1453 struct wined3d_device *device = src_surface->resource.device;
1455 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1456 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1457 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1458 return WINED3DERR_INVALIDCALL;
1460 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1462 surface_modify_ds_location(dst_surface, SFLAG_DS_OFFSCREEN,
1463 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1464 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
1469 /* Do not call while under the GL lock. */
1470 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1471 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1472 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1474 const struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1475 struct wined3d_device *device = dst_surface->resource.device;
1476 DWORD src_ds_flags, dst_ds_flags;
1477 RECT src_rect, dst_rect;
1479 static const DWORD simple_blit = WINEDDBLT_ASYNC
1480 | WINEDDBLT_COLORFILL
1482 | WINEDDBLT_DEPTHFILL
1483 | WINEDDBLT_DONOTWAIT;
1485 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1486 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1487 flags, fx, debug_d3dtexturefiltertype(filter));
1488 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1490 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1492 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1493 return WINEDDERR_SURFACEBUSY;
1496 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1498 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1499 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1500 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1501 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1502 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1504 /* The destination rect can be out of bounds on the condition
1505 * that a clipper is set for the surface. */
1506 if (dst_surface->clipper)
1507 FIXME("Blit clipping not implemented.\n");
1509 WARN("The application gave us a bad destination rectangle without a clipper set.\n");
1510 return WINEDDERR_INVALIDRECT;
1515 surface_get_rect(src_surface, src_rect_in, &src_rect);
1517 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1518 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1519 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1520 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1521 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1523 WARN("Application gave us bad source rectangle for Blt.\n");
1524 return WINEDDERR_INVALIDRECT;
1529 memset(&src_rect, 0, sizeof(src_rect));
1532 if (!fx || !(fx->dwDDFX))
1533 flags &= ~WINEDDBLT_DDFX;
1535 if (flags & WINEDDBLT_WAIT)
1536 flags &= ~WINEDDBLT_WAIT;
1538 if (flags & WINEDDBLT_ASYNC)
1540 static unsigned int once;
1543 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1544 flags &= ~WINEDDBLT_ASYNC;
1547 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1548 if (flags & WINEDDBLT_DONOTWAIT)
1550 static unsigned int once;
1553 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1554 flags &= ~WINEDDBLT_DONOTWAIT;
1557 if (!device->d3d_initialized)
1559 WARN("D3D not initialized, using fallback.\n");
1563 if (flags & ~simple_blit)
1565 WARN("Using fallback for complex blit (%#x).\n", flags);
1569 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1570 src_swapchain = src_surface->container.u.swapchain;
1572 src_swapchain = NULL;
1574 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1575 dst_swapchain = dst_surface->container.u.swapchain;
1577 dst_swapchain = NULL;
1579 /* This isn't strictly needed. FBO blits for example could deal with
1580 * cross-swapchain blits by first downloading the source to a texture
1581 * before switching to the destination context. We just have this here to
1582 * not have to deal with the issue, since cross-swapchain blits should be
1584 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1586 FIXME("Using fallback for cross-swapchain blit.\n");
1590 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1592 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1596 if (src_ds_flags || dst_ds_flags)
1598 if (flags & WINEDDBLT_DEPTHFILL)
1602 TRACE("Depth fill.\n");
1604 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1605 return WINED3DERR_INVALIDCALL;
1607 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1612 /* Accessing depth / stencil surfaces is supposed to fail while in
1613 * a scene, except for fills, which seem to work. */
1614 if (device->inScene)
1616 WARN("Rejecting depth / stencil access while in scene.\n");
1617 return WINED3DERR_INVALIDCALL;
1620 if (src_ds_flags != dst_ds_flags)
1622 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1623 return WINED3DERR_INVALIDCALL;
1626 if (src_rect.top || src_rect.left
1627 || src_rect.bottom != src_surface->resource.height
1628 || src_rect.right != src_surface->resource.width)
1630 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1631 wine_dbgstr_rect(&src_rect));
1632 return WINED3DERR_INVALIDCALL;
1635 if (dst_rect.top || dst_rect.left
1636 || dst_rect.bottom != dst_surface->resource.height
1637 || dst_rect.right != dst_surface->resource.width)
1639 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1640 wine_dbgstr_rect(&src_rect));
1641 return WINED3DERR_INVALIDCALL;
1644 if (src_surface->resource.height != dst_surface->resource.height
1645 || src_surface->resource.width != dst_surface->resource.width)
1647 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1648 return WINED3DERR_INVALIDCALL;
1651 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1657 if (flags & WINEDDBLT_COLORFILL)
1659 WINED3DCOLORVALUE color;
1661 TRACE("Color fill.\n");
1663 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1666 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1671 TRACE("Color blit.\n");
1673 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1674 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1675 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1677 TRACE("Using FBO blit.\n");
1679 surface_blt_fbo(device, filter,
1680 src_surface, SFLAG_INDRAWABLE, &src_rect,
1681 dst_surface, SFLAG_INDRAWABLE, &dst_rect);
1682 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
1686 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1687 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1688 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1690 TRACE("Using arbfp blit.\n");
1692 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1700 /* Special cases for render targets. */
1701 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1702 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1704 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1705 src_surface, &src_rect, flags, fx, filter)))
1711 /* For the rest call the X11 surface implementation. For render targets
1712 * this should be implemented OpenGL accelerated in BltOverride, other
1713 * blits are rather rare. */
1714 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1717 /* Do not call while under the GL lock. */
1718 HRESULT CDECL wined3d_surface_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
1719 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD trans)
1721 RECT src_rect, dst_rect;
1724 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect_in %s, trans %#x.\n",
1725 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect_in), trans);
1727 surface_get_rect(src_surface, src_rect_in, &src_rect);
1729 dst_rect.left = dst_x;
1730 dst_rect.top = dst_y;
1731 dst_rect.right = dst_x + src_rect.right - src_rect.left;
1732 dst_rect.bottom = dst_y + src_rect.bottom - src_rect.top;
1734 if (trans & WINEDDBLTFAST_SRCCOLORKEY)
1735 flags |= WINEDDBLT_KEYSRC;
1736 if (trans & WINEDDBLTFAST_DESTCOLORKEY)
1737 flags |= WINEDDBLT_KEYDEST;
1738 if (trans & WINEDDBLTFAST_WAIT)
1739 flags |= WINEDDBLT_WAIT;
1740 if (trans & WINEDDBLTFAST_DONOTWAIT)
1741 flags |= WINEDDBLT_DONOTWAIT;
1743 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, NULL, WINED3DTEXF_POINT);
1746 static HRESULT surface_set_mem(struct wined3d_surface *surface, void *mem)
1748 TRACE("surface %p, mem %p.\n", surface, mem);
1750 if (mem && mem != surface->resource.allocatedMemory)
1752 void *release = NULL;
1754 /* Do I have to copy the old surface content? */
1755 if (surface->flags & SFLAG_DIBSECTION)
1757 SelectObject(surface->hDC, surface->dib.holdbitmap);
1758 DeleteDC(surface->hDC);
1759 /* Release the DIB section. */
1760 DeleteObject(surface->dib.DIBsection);
1761 surface->dib.bitmap_data = NULL;
1762 surface->resource.allocatedMemory = NULL;
1763 surface->hDC = NULL;
1764 surface->flags &= ~SFLAG_DIBSECTION;
1766 else if (!(surface->flags & SFLAG_USERPTR))
1768 release = surface->resource.heapMemory;
1769 surface->resource.heapMemory = NULL;
1771 surface->resource.allocatedMemory = mem;
1772 surface->flags |= SFLAG_USERPTR;
1774 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
1775 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1777 /* For client textures OpenGL has to be notified. */
1778 if (surface->flags & SFLAG_CLIENT)
1779 surface_release_client_storage(surface);
1781 /* Now free the old memory if any. */
1782 HeapFree(GetProcessHeap(), 0, release);
1784 else if (surface->flags & SFLAG_USERPTR)
1786 /* Map and GetDC will re-create the dib section and allocated memory. */
1787 surface->resource.allocatedMemory = NULL;
1788 /* HeapMemory should be NULL already. */
1789 if (surface->resource.heapMemory)
1790 ERR("User pointer surface has heap memory allocated.\n");
1791 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
1793 if (surface->flags & SFLAG_CLIENT)
1794 surface_release_client_storage(surface);
1796 surface_prepare_system_memory(surface);
1797 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1803 /* Context activation is done by the caller. */
1804 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1806 if (!surface->resource.heapMemory)
1808 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1809 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1810 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1814 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1815 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1816 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1817 surface->resource.size, surface->resource.allocatedMemory));
1818 checkGLcall("glGetBufferSubDataARB");
1819 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1820 checkGLcall("glDeleteBuffersARB");
1824 surface->flags &= ~SFLAG_PBO;
1827 /* Do not call while under the GL lock. */
1828 static void surface_unload(struct wined3d_resource *resource)
1830 struct wined3d_surface *surface = surface_from_resource(resource);
1831 struct wined3d_renderbuffer_entry *entry, *entry2;
1832 struct wined3d_device *device = resource->device;
1833 const struct wined3d_gl_info *gl_info;
1834 struct wined3d_context *context;
1836 TRACE("surface %p.\n", surface);
1838 if (resource->pool == WINED3DPOOL_DEFAULT)
1840 /* Default pool resources are supposed to be destroyed before Reset is called.
1841 * Implicit resources stay however. So this means we have an implicit render target
1842 * or depth stencil. The content may be destroyed, but we still have to tear down
1843 * opengl resources, so we cannot leave early.
1845 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1846 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1847 * or the depth stencil into an FBO the texture or render buffer will be removed
1848 * and all flags get lost
1850 surface_init_sysmem(surface);
1854 /* Load the surface into system memory */
1855 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1856 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
1858 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1859 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1860 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1862 context = context_acquire(device, NULL);
1863 gl_info = context->gl_info;
1865 /* Destroy PBOs, but load them into real sysmem before */
1866 if (surface->flags & SFLAG_PBO)
1867 surface_remove_pbo(surface, gl_info);
1869 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1870 * all application-created targets the application has to release the surface
1871 * before calling _Reset
1873 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1876 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1878 list_remove(&entry->entry);
1879 HeapFree(GetProcessHeap(), 0, entry);
1881 list_init(&surface->renderbuffers);
1882 surface->current_renderbuffer = NULL;
1884 /* If we're in a texture, the texture name belongs to the texture.
1885 * Otherwise, destroy it. */
1886 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1889 glDeleteTextures(1, &surface->texture_name);
1890 surface->texture_name = 0;
1891 glDeleteTextures(1, &surface->texture_name_srgb);
1892 surface->texture_name_srgb = 0;
1896 context_release(context);
1898 resource_unload(resource);
1901 static const struct wined3d_resource_ops surface_resource_ops =
1906 static const struct wined3d_surface_ops surface_ops =
1908 surface_private_setup,
1910 surface_realize_palette,
1911 surface_draw_overlay,
1920 /*****************************************************************************
1921 * Initializes the GDI surface, aka creates the DIB section we render to
1922 * The DIB section creation is done by calling GetDC, which will create the
1923 * section and releasing the dc to allow the app to use it. The dib section
1924 * will stay until the surface is released
1926 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1927 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1928 * avoid confusion in the shared surface code.
1931 * WINED3D_OK on success
1932 * The return values of called methods on failure
1934 *****************************************************************************/
1935 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1939 TRACE("surface %p.\n", surface);
1941 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1943 ERR("Overlays not yet supported by GDI surfaces.\n");
1944 return WINED3DERR_INVALIDCALL;
1947 /* Sysmem textures have memory already allocated - release it,
1948 * this avoids an unnecessary memcpy. */
1949 hr = surface_create_dib_section(surface);
1952 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1953 surface->resource.heapMemory = NULL;
1954 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1957 /* We don't mind the nonpow2 stuff in GDI. */
1958 surface->pow2Width = surface->resource.width;
1959 surface->pow2Height = surface->resource.height;
1964 static void surface_gdi_cleanup(struct wined3d_surface *surface)
1966 TRACE("surface %p.\n", surface);
1968 if (surface->flags & SFLAG_DIBSECTION)
1970 /* Release the DC. */
1971 SelectObject(surface->hDC, surface->dib.holdbitmap);
1972 DeleteDC(surface->hDC);
1973 /* Release the DIB section. */
1974 DeleteObject(surface->dib.DIBsection);
1975 surface->dib.bitmap_data = NULL;
1976 surface->resource.allocatedMemory = NULL;
1979 if (surface->flags & SFLAG_USERPTR)
1980 wined3d_surface_set_mem(surface, NULL);
1981 if (surface->overlay_dest)
1982 list_remove(&surface->overlay_entry);
1984 HeapFree(GetProcessHeap(), 0, surface->palette9);
1986 resource_cleanup(&surface->resource);
1989 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1991 struct wined3d_palette *palette = surface->palette;
1993 TRACE("surface %p.\n", surface);
1995 if (!palette) return;
1997 if (surface->flags & SFLAG_DIBSECTION)
2002 TRACE("Updating the DC's palette.\n");
2004 for (i = 0; i < 256; ++i)
2006 col[i].rgbRed = palette->palents[i].peRed;
2007 col[i].rgbGreen = palette->palents[i].peGreen;
2008 col[i].rgbBlue = palette->palents[i].peBlue;
2009 col[i].rgbReserved = 0;
2011 SetDIBColorTable(surface->hDC, 0, 256, col);
2014 /* Update the image because of the palette change. Some games like e.g.
2015 * Red Alert call SetEntries a lot to implement fading. */
2016 /* Tell the swapchain to update the screen. */
2017 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2019 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2020 if (surface == swapchain->front_buffer)
2022 x11_copy_to_screen(swapchain, NULL);
2027 static HRESULT gdi_surface_draw_overlay(struct wined3d_surface *surface)
2029 FIXME("GDI surfaces can't draw overlays yet.\n");
2033 static void gdi_surface_preload(struct wined3d_surface *surface)
2035 TRACE("surface %p.\n", surface);
2037 ERR("Preloading GDI surfaces is not supported.\n");
2040 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
2042 TRACE("surface %p, rect %s, flags %#x.\n",
2043 surface, wine_dbgstr_rect(rect), flags);
2045 if (!surface->resource.allocatedMemory)
2047 /* This happens on gdi surfaces if the application set a user pointer
2048 * and resets it. Recreate the DIB section. */
2049 surface_create_dib_section(surface);
2050 surface->resource.allocatedMemory = surface->dib.bitmap_data;
2054 static void gdi_surface_unmap(struct wined3d_surface *surface)
2056 TRACE("surface %p.\n", surface);
2058 /* Tell the swapchain to update the screen. */
2059 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2061 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2062 if (surface == swapchain->front_buffer)
2064 x11_copy_to_screen(swapchain, &surface->lockedRect);
2068 memset(&surface->lockedRect, 0, sizeof(RECT));
2071 static HRESULT gdi_surface_getdc(struct wined3d_surface *surface)
2073 WINED3DLOCKED_RECT lock;
2076 TRACE("surface %p.\n", surface);
2078 /* Should have a DIB section already. */
2079 if (!(surface->flags & SFLAG_DIBSECTION))
2081 WARN("DC not supported on this surface\n");
2082 return WINED3DERR_INVALIDCALL;
2085 /* Map the surface. */
2086 hr = wined3d_surface_map(surface, &lock, NULL, 0);
2088 ERR("Map failed, hr %#x.\n", hr);
2093 static HRESULT gdi_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override)
2095 TRACE("surface %p, override %p.\n", surface, override);
2100 static HRESULT gdi_surface_set_mem(struct wined3d_surface *surface, void *mem)
2102 TRACE("surface %p, mem %p.\n", surface, mem);
2104 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
2105 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2107 ERR("Not supported on render targets.\n");
2108 return WINED3DERR_INVALIDCALL;
2111 if (mem && mem != surface->resource.allocatedMemory)
2113 void *release = NULL;
2115 /* Do I have to copy the old surface content? */
2116 if (surface->flags & SFLAG_DIBSECTION)
2118 SelectObject(surface->hDC, surface->dib.holdbitmap);
2119 DeleteDC(surface->hDC);
2120 /* Release the DIB section. */
2121 DeleteObject(surface->dib.DIBsection);
2122 surface->dib.bitmap_data = NULL;
2123 surface->resource.allocatedMemory = NULL;
2124 surface->hDC = NULL;
2125 surface->flags &= ~SFLAG_DIBSECTION;
2127 else if (!(surface->flags & SFLAG_USERPTR))
2129 release = surface->resource.allocatedMemory;
2131 surface->resource.allocatedMemory = mem;
2132 surface->flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2134 /* Now free the old memory, if any. */
2135 HeapFree(GetProcessHeap(), 0, release);
2137 else if (surface->flags & SFLAG_USERPTR)
2139 /* Map() and GetDC() will re-create the dib section and allocated memory. */
2140 surface->resource.allocatedMemory = NULL;
2141 surface->flags &= ~SFLAG_USERPTR;
2147 static const struct wined3d_surface_ops gdi_surface_ops =
2149 gdi_surface_private_setup,
2150 surface_gdi_cleanup,
2151 gdi_surface_realize_palette,
2152 gdi_surface_draw_overlay,
2153 gdi_surface_preload,
2158 gdi_surface_set_mem,
2161 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2166 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2170 name = &surface->texture_name_srgb;
2171 flag = SFLAG_INSRGBTEX;
2175 name = &surface->texture_name;
2176 flag = SFLAG_INTEXTURE;
2179 if (!*name && new_name)
2181 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2182 * surface has no texture name yet. See if we can get rid of this. */
2183 if (surface->flags & flag)
2184 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2185 surface_modify_location(surface, flag, FALSE);
2189 surface_force_reload(surface);
2192 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2194 TRACE("surface %p, target %#x.\n", surface, target);
2196 if (surface->texture_target != target)
2198 if (target == GL_TEXTURE_RECTANGLE_ARB)
2200 surface->flags &= ~SFLAG_NORMCOORD;
2202 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2204 surface->flags |= SFLAG_NORMCOORD;
2207 surface->texture_target = target;
2208 surface_force_reload(surface);
2211 /* Context activation is done by the caller. */
2212 void surface_bind(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
2214 TRACE("surface %p, gl_info %p, srgb %#x.\n", surface, gl_info, srgb);
2216 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2218 struct wined3d_texture *texture = surface->container.u.texture;
2220 TRACE("Passing to container (%p).\n", texture);
2221 texture->texture_ops->texture_bind(texture, gl_info, srgb);
2225 if (surface->texture_level)
2227 ERR("Standalone surface %p is non-zero texture level %u.\n",
2228 surface, surface->texture_level);
2232 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2236 if (!surface->texture_name)
2238 glGenTextures(1, &surface->texture_name);
2239 checkGLcall("glGenTextures");
2241 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2243 glBindTexture(surface->texture_target, surface->texture_name);
2244 checkGLcall("glBindTexture");
2245 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2246 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2247 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2248 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2249 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2250 checkGLcall("glTexParameteri");
2254 glBindTexture(surface->texture_target, surface->texture_name);
2255 checkGLcall("glBindTexture");
2262 /* This call just downloads data, the caller is responsible for binding the
2263 * correct texture. */
2264 /* Context activation is done by the caller. */
2265 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2267 const struct wined3d_format *format = surface->resource.format;
2269 /* Only support read back of converted P8 surfaces. */
2270 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2272 FIXME("Readback conversion not supported for format %s.\n", debug_d3dformat(format->id));
2278 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2280 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2281 surface, surface->texture_level, format->glFormat, format->glType,
2282 surface->resource.allocatedMemory);
2284 if (surface->flags & SFLAG_PBO)
2286 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2287 checkGLcall("glBindBufferARB");
2288 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2289 checkGLcall("glGetCompressedTexImageARB");
2290 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2291 checkGLcall("glBindBufferARB");
2295 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2296 surface->texture_level, surface->resource.allocatedMemory));
2297 checkGLcall("glGetCompressedTexImageARB");
2305 GLenum gl_format = format->glFormat;
2306 GLenum gl_type = format->glType;
2310 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2311 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2313 gl_format = GL_ALPHA;
2314 gl_type = GL_UNSIGNED_BYTE;
2317 if (surface->flags & SFLAG_NONPOW2)
2319 unsigned char alignment = surface->resource.device->surface_alignment;
2320 src_pitch = format->byte_count * surface->pow2Width;
2321 dst_pitch = wined3d_surface_get_pitch(surface);
2322 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2323 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2327 mem = surface->resource.allocatedMemory;
2330 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2331 surface, surface->texture_level, gl_format, gl_type, mem);
2333 if (surface->flags & SFLAG_PBO)
2335 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2336 checkGLcall("glBindBufferARB");
2338 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2339 checkGLcall("glGetTexImage");
2341 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2342 checkGLcall("glBindBufferARB");
2346 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2347 checkGLcall("glGetTexImage");
2351 if (surface->flags & SFLAG_NONPOW2)
2353 const BYTE *src_data;
2357 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2358 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2359 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2361 * We're doing this...
2363 * instead of boxing the texture :
2364 * |<-texture width ->| -->pow2width| /\
2365 * |111111111111111111| | |
2366 * |222 Texture 222222| boxed empty | texture height
2367 * |3333 Data 33333333| | |
2368 * |444444444444444444| | \/
2369 * ----------------------------------- |
2370 * | boxed empty | boxed empty | pow2height
2372 * -----------------------------------
2375 * we're repacking the data to the expected texture width
2377 * |<-texture width ->| -->pow2width| /\
2378 * |111111111111111111222222222222222| |
2379 * |222333333333333333333444444444444| texture height
2383 * | empty | pow2height
2385 * -----------------------------------
2389 * |<-texture width ->| /\
2390 * |111111111111111111|
2391 * |222222222222222222|texture height
2392 * |333333333333333333|
2393 * |444444444444444444| \/
2394 * --------------------
2396 * this also means that any references to allocatedMemory should work with the data as if were a
2397 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2399 * internally the texture is still stored in a boxed format so any references to textureName will
2400 * get a boxed texture with width pow2width and not a texture of width resource.width.
2402 * Performance should not be an issue, because applications normally do not lock the surfaces when
2403 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2404 * and doesn't have to be re-read. */
2406 dst_data = surface->resource.allocatedMemory;
2407 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2408 for (y = 1; y < surface->resource.height; ++y)
2410 /* skip the first row */
2411 src_data += src_pitch;
2412 dst_data += dst_pitch;
2413 memcpy(dst_data, src_data, dst_pitch);
2416 HeapFree(GetProcessHeap(), 0, mem);
2420 /* Surface has now been downloaded */
2421 surface->flags |= SFLAG_INSYSMEM;
2424 /* This call just uploads data, the caller is responsible for binding the
2425 * correct texture. */
2426 /* Context activation is done by the caller. */
2427 void surface_upload_data(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2428 const struct wined3d_format *format, const RECT *src_rect, UINT src_w, const POINT *dst_point,
2429 BOOL srgb, const struct wined3d_bo_address *data)
2431 UINT update_w = src_rect->right - src_rect->left;
2432 UINT update_h = src_rect->bottom - src_rect->top;
2434 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_w %u, dst_point %p, srgb %#x, data {%#x:%p}.\n",
2435 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_w,
2436 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2438 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2439 update_h *= format->heightscale;
2443 if (data->buffer_object)
2445 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2446 checkGLcall("glBindBufferARB");
2449 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2451 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2452 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2453 UINT src_pitch = wined3d_format_calculate_size(format, 1, src_w, 1);
2454 const BYTE *addr = data->addr;
2457 addr += (src_rect->top / format->block_height) * src_pitch;
2458 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2461 internal = format->glGammaInternal;
2462 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2463 internal = format->rtInternal;
2465 internal = format->glInternal;
2467 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2468 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2469 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2471 if (row_length == src_pitch)
2473 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2474 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2480 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2481 * can't use the unpack row length like below. */
2482 for (row = 0, y = dst_point->y; row < row_count; ++row)
2484 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2485 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2486 y += format->block_height;
2490 checkGLcall("glCompressedTexSubImage2DARB");
2494 const BYTE *addr = data->addr;
2496 addr += src_rect->top * src_w * format->byte_count;
2497 addr += src_rect->left * format->byte_count;
2499 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2500 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2501 update_w, update_h, format->glFormat, format->glType, addr);
2503 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_w);
2504 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2505 update_w, update_h, format->glFormat, format->glType, addr);
2506 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2507 checkGLcall("glTexSubImage2D");
2510 if (data->buffer_object)
2512 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2513 checkGLcall("glBindBufferARB");
2518 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2520 struct wined3d_device *device = surface->resource.device;
2523 for (i = 0; i < device->context_count; ++i)
2525 context_surface_update(device->contexts[i], surface);
2530 /* This call just allocates the texture, the caller is responsible for binding
2531 * the correct texture. */
2532 /* Context activation is done by the caller. */
2533 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2534 const struct wined3d_format *format, BOOL srgb)
2536 BOOL enable_client_storage = FALSE;
2537 GLsizei width = surface->pow2Width;
2538 GLsizei height = surface->pow2Height;
2539 const BYTE *mem = NULL;
2544 internal = format->glGammaInternal;
2546 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2548 internal = format->rtInternal;
2552 internal = format->glInternal;
2555 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2557 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",
2558 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2559 internal, width, height, format->glFormat, format->glType);
2563 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2565 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2566 || !surface->resource.allocatedMemory)
2568 /* In some cases we want to disable client storage.
2569 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2570 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2571 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2572 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2574 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2575 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2576 surface->flags &= ~SFLAG_CLIENT;
2577 enable_client_storage = TRUE;
2581 surface->flags |= SFLAG_CLIENT;
2583 /* Point OpenGL to our allocated texture memory. Do not use
2584 * resource.allocatedMemory here because it might point into a
2585 * PBO. Instead use heapMemory, but get the alignment right. */
2586 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2587 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2591 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2593 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2594 internal, width, height, 0, surface->resource.size, mem));
2595 checkGLcall("glCompressedTexImage2DARB");
2599 glTexImage2D(surface->texture_target, surface->texture_level,
2600 internal, width, height, 0, format->glFormat, format->glType, mem);
2601 checkGLcall("glTexImage2D");
2604 if(enable_client_storage) {
2605 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2606 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2611 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2612 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2613 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2614 /* GL locking is done by the caller */
2615 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2617 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2618 struct wined3d_renderbuffer_entry *entry;
2619 GLuint renderbuffer = 0;
2620 unsigned int src_width, src_height;
2621 unsigned int width, height;
2623 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2625 width = rt->pow2Width;
2626 height = rt->pow2Height;
2630 width = surface->pow2Width;
2631 height = surface->pow2Height;
2634 src_width = surface->pow2Width;
2635 src_height = surface->pow2Height;
2637 /* A depth stencil smaller than the render target is not valid */
2638 if (width > src_width || height > src_height) return;
2640 /* Remove any renderbuffer set if the sizes match */
2641 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2642 || (width == src_width && height == src_height))
2644 surface->current_renderbuffer = NULL;
2648 /* Look if we've already got a renderbuffer of the correct dimensions */
2649 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2651 if (entry->width == width && entry->height == height)
2653 renderbuffer = entry->id;
2654 surface->current_renderbuffer = entry;
2661 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2662 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2663 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2664 surface->resource.format->glInternal, width, height);
2666 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2667 entry->width = width;
2668 entry->height = height;
2669 entry->id = renderbuffer;
2670 list_add_head(&surface->renderbuffers, &entry->entry);
2672 surface->current_renderbuffer = entry;
2675 checkGLcall("set_compatible_renderbuffer");
2678 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2680 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2682 TRACE("surface %p.\n", surface);
2684 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2686 ERR("Surface %p is not on a swapchain.\n", surface);
2690 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2692 if (swapchain->render_to_fbo)
2694 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2695 return GL_COLOR_ATTACHMENT0;
2697 TRACE("Returning GL_BACK\n");
2700 else if (surface == swapchain->front_buffer)
2702 TRACE("Returning GL_FRONT\n");
2706 FIXME("Higher back buffer, returning GL_BACK\n");
2710 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2711 void surface_add_dirty_rect(struct wined3d_surface *surface, const WINED3DBOX *dirty_rect)
2713 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2715 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2716 /* No partial locking for textures yet. */
2717 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2719 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2722 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->Left);
2723 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->Top);
2724 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->Right);
2725 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->Bottom);
2729 surface->dirtyRect.left = 0;
2730 surface->dirtyRect.top = 0;
2731 surface->dirtyRect.right = surface->resource.width;
2732 surface->dirtyRect.bottom = surface->resource.height;
2735 /* if the container is a texture then mark it dirty. */
2736 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2738 TRACE("Passing to container.\n");
2739 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2743 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2745 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2748 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2750 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2752 ERR("Not supported on scratch surfaces.\n");
2753 return WINED3DERR_INVALIDCALL;
2756 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2758 /* Reload if either the texture and sysmem have different ideas about the
2759 * color key, or the actual key values changed. */
2760 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2761 && (surface->glCKey.dwColorSpaceLowValue != surface->SrcBltCKey.dwColorSpaceLowValue
2762 || surface->glCKey.dwColorSpaceHighValue != surface->SrcBltCKey.dwColorSpaceHighValue)))
2764 TRACE("Reloading because of color keying\n");
2765 /* To perform the color key conversion we need a sysmem copy of
2766 * the surface. Make sure we have it. */
2768 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2769 /* Make sure the texture is reloaded because of the color key change,
2770 * this kills performance though :( */
2771 /* TODO: This is not necessarily needed with hw palettized texture support. */
2772 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2773 /* Switching color keying on / off may change the internal format. */
2775 surface_force_reload(surface);
2777 else if (!(surface->flags & flag))
2779 TRACE("Reloading because surface is dirty.\n");
2783 TRACE("surface is already in texture\n");
2787 /* No partial locking for textures yet. */
2788 surface_load_location(surface, flag, NULL);
2789 surface_evict_sysmem(surface);
2794 /* See also float_16_to_32() in wined3d_private.h */
2795 static inline unsigned short float_32_to_16(const float *in)
2798 float tmp = fabsf(*in);
2799 unsigned int mantissa;
2802 /* Deal with special numbers */
2808 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2810 if (tmp < powf(2, 10))
2816 } while (tmp < powf(2, 10));
2818 else if (tmp >= powf(2, 11))
2824 } while (tmp >= powf(2, 11));
2827 mantissa = (unsigned int)tmp;
2828 if (tmp - mantissa >= 0.5f)
2829 ++mantissa; /* Round to nearest, away from zero. */
2831 exp += 10; /* Normalize the mantissa. */
2832 exp += 15; /* Exponent is encoded with excess 15. */
2834 if (exp > 30) /* too big */
2836 ret = 0x7c00; /* INF */
2840 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2843 mantissa = mantissa >> 1;
2846 ret = mantissa & 0x3ff;
2850 ret = (exp << 10) | (mantissa & 0x3ff);
2853 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2857 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2861 TRACE("Surface %p, container %p of type %#x.\n",
2862 surface, surface->container.u.base, surface->container.type);
2864 switch (surface->container.type)
2866 case WINED3D_CONTAINER_TEXTURE:
2867 return wined3d_texture_incref(surface->container.u.texture);
2869 case WINED3D_CONTAINER_SWAPCHAIN:
2870 return wined3d_swapchain_incref(surface->container.u.swapchain);
2873 ERR("Unhandled container type %#x.\n", surface->container.type);
2874 case WINED3D_CONTAINER_NONE:
2878 refcount = InterlockedIncrement(&surface->resource.ref);
2879 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2884 /* Do not call while under the GL lock. */
2885 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2889 TRACE("Surface %p, container %p of type %#x.\n",
2890 surface, surface->container.u.base, surface->container.type);
2892 switch (surface->container.type)
2894 case WINED3D_CONTAINER_TEXTURE:
2895 return wined3d_texture_decref(surface->container.u.texture);
2897 case WINED3D_CONTAINER_SWAPCHAIN:
2898 return wined3d_swapchain_decref(surface->container.u.swapchain);
2901 ERR("Unhandled container type %#x.\n", surface->container.type);
2902 case WINED3D_CONTAINER_NONE:
2906 refcount = InterlockedDecrement(&surface->resource.ref);
2907 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2911 surface->surface_ops->surface_cleanup(surface);
2912 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2914 TRACE("Destroyed surface %p.\n", surface);
2915 HeapFree(GetProcessHeap(), 0, surface);
2921 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2923 return resource_set_priority(&surface->resource, priority);
2926 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2928 return resource_get_priority(&surface->resource);
2931 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2933 TRACE("surface %p.\n", surface);
2935 surface->surface_ops->surface_preload(surface);
2938 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2940 TRACE("surface %p.\n", surface);
2942 return surface->resource.parent;
2945 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2947 TRACE("surface %p.\n", surface);
2949 return &surface->resource;
2952 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2954 TRACE("surface %p, flags %#x.\n", surface, flags);
2958 case WINEDDGBS_CANBLT:
2959 case WINEDDGBS_ISBLTDONE:
2963 return WINED3DERR_INVALIDCALL;
2967 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2969 TRACE("surface %p, flags %#x.\n", surface, flags);
2971 /* XXX: DDERR_INVALIDSURFACETYPE */
2975 case WINEDDGFS_CANFLIP:
2976 case WINEDDGFS_ISFLIPDONE:
2980 return WINED3DERR_INVALIDCALL;
2984 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2986 TRACE("surface %p.\n", surface);
2988 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2989 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2992 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2994 TRACE("surface %p.\n", surface);
2996 /* So far we don't lose anything :) */
2997 surface->flags &= ~SFLAG_LOST;
3001 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
3003 TRACE("surface %p, palette %p.\n", surface, palette);
3005 if (surface->palette == palette)
3007 TRACE("Nop palette change.\n");
3011 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
3012 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3014 surface->palette = palette;
3018 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3019 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
3021 surface->surface_ops->surface_realize_palette(surface);
3027 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
3028 DWORD flags, const WINEDDCOLORKEY *color_key)
3030 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3032 if (flags & WINEDDCKEY_COLORSPACE)
3034 FIXME(" colorkey value not supported (%08x) !\n", flags);
3035 return WINED3DERR_INVALIDCALL;
3038 /* Dirtify the surface, but only if a key was changed. */
3041 switch (flags & ~WINEDDCKEY_COLORSPACE)
3043 case WINEDDCKEY_DESTBLT:
3044 surface->DestBltCKey = *color_key;
3045 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3048 case WINEDDCKEY_DESTOVERLAY:
3049 surface->DestOverlayCKey = *color_key;
3050 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3053 case WINEDDCKEY_SRCOVERLAY:
3054 surface->SrcOverlayCKey = *color_key;
3055 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3058 case WINEDDCKEY_SRCBLT:
3059 surface->SrcBltCKey = *color_key;
3060 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3066 switch (flags & ~WINEDDCKEY_COLORSPACE)
3068 case WINEDDCKEY_DESTBLT:
3069 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3072 case WINEDDCKEY_DESTOVERLAY:
3073 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3076 case WINEDDCKEY_SRCOVERLAY:
3077 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3080 case WINEDDCKEY_SRCBLT:
3081 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3089 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3091 TRACE("surface %p.\n", surface);
3093 return surface->palette;
3096 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3098 const struct wined3d_format *format = surface->resource.format;
3101 TRACE("surface %p.\n", surface);
3103 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3105 /* Since compressed formats are block based, pitch means the amount of
3106 * bytes to the next row of block rather than the next row of pixels. */
3107 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3108 pitch = row_block_count * format->block_byte_count;
3112 unsigned char alignment = surface->resource.device->surface_alignment;
3113 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3114 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3117 TRACE("Returning %u.\n", pitch);
3122 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3124 TRACE("surface %p, mem %p.\n", surface, mem);
3126 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3128 WARN("Surface is locked or the DC is in use.\n");
3129 return WINED3DERR_INVALIDCALL;
3132 return surface->surface_ops->surface_set_mem(surface, mem);
3135 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3139 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3141 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3143 WARN("Not an overlay surface.\n");
3144 return WINEDDERR_NOTAOVERLAYSURFACE;
3147 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3148 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3149 surface->overlay_destrect.left = x;
3150 surface->overlay_destrect.top = y;
3151 surface->overlay_destrect.right = x + w;
3152 surface->overlay_destrect.bottom = y + h;
3154 surface->surface_ops->surface_draw_overlay(surface);
3159 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3161 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3163 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3165 TRACE("Not an overlay surface.\n");
3166 return WINEDDERR_NOTAOVERLAYSURFACE;
3169 if (!surface->overlay_dest)
3171 TRACE("Overlay not visible.\n");
3174 return WINEDDERR_OVERLAYNOTVISIBLE;
3177 *x = surface->overlay_destrect.left;
3178 *y = surface->overlay_destrect.top;
3180 TRACE("Returning position %d, %d.\n", *x, *y);
3185 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3186 DWORD flags, struct wined3d_surface *ref)
3188 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3190 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3192 TRACE("Not an overlay surface.\n");
3193 return WINEDDERR_NOTAOVERLAYSURFACE;
3199 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3200 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3202 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3203 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3205 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3207 WARN("Not an overlay surface.\n");
3208 return WINEDDERR_NOTAOVERLAYSURFACE;
3210 else if (!dst_surface)
3212 WARN("Dest surface is NULL.\n");
3213 return WINED3DERR_INVALIDCALL;
3218 surface->overlay_srcrect = *src_rect;
3222 surface->overlay_srcrect.left = 0;
3223 surface->overlay_srcrect.top = 0;
3224 surface->overlay_srcrect.right = surface->resource.width;
3225 surface->overlay_srcrect.bottom = surface->resource.height;
3230 surface->overlay_destrect = *dst_rect;
3234 surface->overlay_destrect.left = 0;
3235 surface->overlay_destrect.top = 0;
3236 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3237 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3240 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3242 list_remove(&surface->overlay_entry);
3245 if (flags & WINEDDOVER_SHOW)
3247 if (surface->overlay_dest != dst_surface)
3249 surface->overlay_dest = dst_surface;
3250 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3253 else if (flags & WINEDDOVER_HIDE)
3255 /* tests show that the rectangles are erased on hide */
3256 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3257 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3258 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3259 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3260 surface->overlay_dest = NULL;
3263 surface->surface_ops->surface_draw_overlay(surface);
3268 HRESULT CDECL wined3d_surface_set_clipper(struct wined3d_surface *surface, struct wined3d_clipper *clipper)
3270 TRACE("surface %p, clipper %p.\n", surface, clipper);
3272 surface->clipper = clipper;
3277 struct wined3d_clipper * CDECL wined3d_surface_get_clipper(const struct wined3d_surface *surface)
3279 TRACE("surface %p.\n", surface);
3281 return surface->clipper;
3284 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3286 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3288 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3290 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3292 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3293 return WINED3DERR_INVALIDCALL;
3296 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3297 surface->pow2Width, surface->pow2Height);
3298 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3299 surface->resource.format = format;
3301 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3302 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3303 format->glFormat, format->glInternal, format->glType);
3308 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3309 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3311 unsigned short *dst_s;
3315 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3317 for (y = 0; y < h; ++y)
3319 src_f = (const float *)(src + y * pitch_in);
3320 dst_s = (unsigned short *) (dst + y * pitch_out);
3321 for (x = 0; x < w; ++x)
3323 dst_s[x] = float_32_to_16(src_f + x);
3328 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3329 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3331 static const unsigned char convert_5to8[] =
3333 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3334 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3335 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3336 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3338 static const unsigned char convert_6to8[] =
3340 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3341 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3342 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3343 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3344 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3345 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3346 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3347 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3351 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3353 for (y = 0; y < h; ++y)
3355 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3356 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3357 for (x = 0; x < w; ++x)
3359 WORD pixel = src_line[x];
3360 dst_line[x] = 0xff000000
3361 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3362 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3363 | convert_5to8[(pixel & 0x001f)];
3368 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3369 * in both cases we're just setting the X / Alpha channel to 0xff. */
3370 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3371 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3375 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3377 for (y = 0; y < h; ++y)
3379 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3380 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3382 for (x = 0; x < w; ++x)
3384 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3389 static inline BYTE cliptobyte(int x)
3391 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3394 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3395 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3397 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3400 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3402 for (y = 0; y < h; ++y)
3404 const BYTE *src_line = src + y * pitch_in;
3405 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3406 for (x = 0; x < w; ++x)
3408 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3409 * C = Y - 16; D = U - 128; E = V - 128;
3410 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3411 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3412 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3413 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3414 * U and V are shared between the pixels. */
3415 if (!(x & 1)) /* For every even pixel, read new U and V. */
3417 d = (int) src_line[1] - 128;
3418 e = (int) src_line[3] - 128;
3420 g2 = - 100 * d - 208 * e + 128;
3423 c2 = 298 * ((int) src_line[0] - 16);
3424 dst_line[x] = 0xff000000
3425 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3426 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3427 | cliptobyte((c2 + b2) >> 8); /* blue */
3428 /* Scale RGB values to 0..255 range,
3429 * then clip them if still not in range (may be negative),
3430 * then shift them within DWORD if necessary. */
3436 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3437 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3440 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3442 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3444 for (y = 0; y < h; ++y)
3446 const BYTE *src_line = src + y * pitch_in;
3447 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3448 for (x = 0; x < w; ++x)
3450 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3451 * C = Y - 16; D = U - 128; E = V - 128;
3452 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3453 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3454 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3455 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3456 * U and V are shared between the pixels. */
3457 if (!(x & 1)) /* For every even pixel, read new U and V. */
3459 d = (int) src_line[1] - 128;
3460 e = (int) src_line[3] - 128;
3462 g2 = - 100 * d - 208 * e + 128;
3465 c2 = 298 * ((int) src_line[0] - 16);
3466 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3467 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3468 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3469 /* Scale RGB values to 0..255 range,
3470 * then clip them if still not in range (may be negative),
3471 * then shift them within DWORD if necessary. */
3477 struct d3dfmt_convertor_desc
3479 enum wined3d_format_id from, to;
3480 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3483 static const struct d3dfmt_convertor_desc convertors[] =
3485 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3486 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3487 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3488 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3489 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3490 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3493 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3494 enum wined3d_format_id to)
3498 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3500 if (convertors[i].from == from && convertors[i].to == to)
3501 return &convertors[i];
3507 /*****************************************************************************
3508 * surface_convert_format
3510 * Creates a duplicate of a surface in a different format. Is used by Blt to
3511 * blit between surfaces with different formats.
3514 * source: Source surface
3515 * fmt: Requested destination format
3517 *****************************************************************************/
3518 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3520 const struct d3dfmt_convertor_desc *conv;
3521 WINED3DLOCKED_RECT lock_src, lock_dst;
3522 struct wined3d_surface *ret = NULL;
3525 conv = find_convertor(source->resource.format->id, to_fmt);
3528 FIXME("Cannot find a conversion function from format %s to %s.\n",
3529 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3533 wined3d_surface_create(source->resource.device, source->resource.width,
3534 source->resource.height, to_fmt, TRUE /* lockable */, TRUE /* discard */, 0 /* level */,
3535 0 /* usage */, WINED3DPOOL_SCRATCH, WINED3DMULTISAMPLE_NONE /* TODO: Multisampled conversion */,
3536 0 /* MultiSampleQuality */, source->surface_type, NULL /* parent */, &wined3d_null_parent_ops, &ret);
3539 ERR("Failed to create a destination surface for conversion.\n");
3543 memset(&lock_src, 0, sizeof(lock_src));
3544 memset(&lock_dst, 0, sizeof(lock_dst));
3546 hr = wined3d_surface_map(source, &lock_src, NULL, WINED3DLOCK_READONLY);
3549 ERR("Failed to lock the source surface.\n");
3550 wined3d_surface_decref(ret);
3553 hr = wined3d_surface_map(ret, &lock_dst, NULL, WINED3DLOCK_READONLY);
3556 ERR("Failed to lock the destination surface.\n");
3557 wined3d_surface_unmap(source);
3558 wined3d_surface_decref(ret);
3562 conv->convert(lock_src.pBits, lock_dst.pBits, lock_src.Pitch, lock_dst.Pitch,
3563 source->resource.width, source->resource.height);
3565 wined3d_surface_unmap(ret);
3566 wined3d_surface_unmap(source);
3571 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3572 unsigned int bpp, UINT pitch, DWORD color)
3579 #define COLORFILL_ROW(type) \
3581 type *d = (type *)buf; \
3582 for (x = 0; x < width; ++x) \
3583 d[x] = (type)color; \
3589 COLORFILL_ROW(BYTE);
3593 COLORFILL_ROW(WORD);
3599 for (x = 0; x < width; ++x, d += 3)
3601 d[0] = (color ) & 0xFF;
3602 d[1] = (color >> 8) & 0xFF;
3603 d[2] = (color >> 16) & 0xFF;
3608 COLORFILL_ROW(DWORD);
3612 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3613 return WINED3DERR_NOTAVAILABLE;
3616 #undef COLORFILL_ROW
3618 /* Now copy first row. */
3620 for (y = 1; y < height; ++y)
3623 memcpy(buf, first, width * bpp);
3629 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3631 TRACE("surface %p.\n", surface);
3633 if (!(surface->flags & SFLAG_LOCKED))
3635 WARN("Trying to unmap unmapped surface.\n");
3636 return WINEDDERR_NOTLOCKED;
3638 surface->flags &= ~SFLAG_LOCKED;
3640 surface->surface_ops->surface_unmap(surface);
3645 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3646 WINED3DLOCKED_RECT *locked_rect, const RECT *rect, DWORD flags)
3648 TRACE("surface %p, locked_rect %p, rect %s, flags %#x.\n",
3649 surface, locked_rect, wine_dbgstr_rect(rect), flags);
3651 if (surface->flags & SFLAG_LOCKED)
3653 WARN("Surface is already mapped.\n");
3654 return WINED3DERR_INVALIDCALL;
3656 surface->flags |= SFLAG_LOCKED;
3658 if (!(surface->flags & SFLAG_LOCKABLE))
3659 WARN("Trying to lock unlockable surface.\n");
3661 surface->surface_ops->surface_map(surface, rect, flags);
3663 locked_rect->Pitch = wined3d_surface_get_pitch(surface);
3667 locked_rect->pBits = surface->resource.allocatedMemory;
3668 surface->lockedRect.left = 0;
3669 surface->lockedRect.top = 0;
3670 surface->lockedRect.right = surface->resource.width;
3671 surface->lockedRect.bottom = surface->resource.height;
3675 const struct wined3d_format *format = surface->resource.format;
3677 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3679 /* Compressed textures are block based, so calculate the offset of
3680 * the block that contains the top-left pixel of the locked rectangle. */
3681 locked_rect->pBits = surface->resource.allocatedMemory
3682 + ((rect->top / format->block_height) * locked_rect->Pitch)
3683 + ((rect->left / format->block_width) * format->block_byte_count);
3687 locked_rect->pBits = surface->resource.allocatedMemory
3688 + (locked_rect->Pitch * rect->top)
3689 + (rect->left * format->byte_count);
3691 surface->lockedRect.left = rect->left;
3692 surface->lockedRect.top = rect->top;
3693 surface->lockedRect.right = rect->right;
3694 surface->lockedRect.bottom = rect->bottom;
3697 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3698 TRACE("Returning memory %p, pitch %u.\n", locked_rect->pBits, locked_rect->Pitch);
3703 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3707 TRACE("surface %p, dc %p.\n", surface, dc);
3709 if (surface->flags & SFLAG_USERPTR)
3711 ERR("Not supported on surfaces with application-provided memory.\n");
3712 return WINEDDERR_NODC;
3715 /* Give more detailed info for ddraw. */
3716 if (surface->flags & SFLAG_DCINUSE)
3717 return WINEDDERR_DCALREADYCREATED;
3719 /* Can't GetDC if the surface is locked. */
3720 if (surface->flags & SFLAG_LOCKED)
3721 return WINED3DERR_INVALIDCALL;
3723 hr = surface->surface_ops->surface_getdc(surface);
3727 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3728 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3730 /* GetDC on palettized formats is unsupported in D3D9, and the method
3731 * is missing in D3D8, so this should only be used for DX <=7
3732 * surfaces (with non-device palettes). */
3733 const PALETTEENTRY *pal = NULL;
3735 if (surface->palette)
3737 pal = surface->palette->palents;
3741 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3742 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3744 if (dds_primary && dds_primary->palette)
3745 pal = dds_primary->palette->palents;
3753 for (i = 0; i < 256; ++i)
3755 col[i].rgbRed = pal[i].peRed;
3756 col[i].rgbGreen = pal[i].peGreen;
3757 col[i].rgbBlue = pal[i].peBlue;
3758 col[i].rgbReserved = 0;
3760 SetDIBColorTable(surface->hDC, 0, 256, col);
3764 surface->flags |= SFLAG_DCINUSE;
3767 TRACE("Returning dc %p.\n", *dc);
3772 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3774 TRACE("surface %p, dc %p.\n", surface, dc);
3776 if (!(surface->flags & SFLAG_DCINUSE))
3777 return WINEDDERR_NODC;
3779 if (surface->hDC != dc)
3781 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3783 return WINEDDERR_NODC;
3786 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3788 /* Copy the contents of the DIB over to the PBO. */
3789 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->dib.bitmap_size);
3792 /* We locked first, so unlock now. */
3793 wined3d_surface_unmap(surface);
3795 surface->flags &= ~SFLAG_DCINUSE;
3800 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3802 struct wined3d_swapchain *swapchain;
3805 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3807 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
3809 ERR("Flipped surface is not on a swapchain.\n");
3810 return WINEDDERR_NOTFLIPPABLE;
3812 swapchain = surface->container.u.swapchain;
3814 hr = surface->surface_ops->surface_flip(surface, override);
3818 /* Just overwrite the swapchain presentation interval. This is ok because
3819 * only ddraw apps can call Flip, and only d3d8 and d3d9 applications
3820 * specify the presentation interval. */
3821 if (!(flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)))
3822 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
3823 else if (flags & WINEDDFLIP_NOVSYNC)
3824 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3825 else if (flags & WINEDDFLIP_INTERVAL2)
3826 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
3827 else if (flags & WINEDDFLIP_INTERVAL3)
3828 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
3830 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3832 return wined3d_swapchain_present(swapchain, NULL, NULL, swapchain->win_handle, NULL, 0);
3835 /* Do not call while under the GL lock. */
3836 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3838 struct wined3d_device *device = surface->resource.device;
3840 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3842 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3844 struct wined3d_texture *texture = surface->container.u.texture;
3846 TRACE("Passing to container (%p).\n", texture);
3847 texture->texture_ops->texture_preload(texture, srgb);
3851 struct wined3d_context *context = NULL;
3853 TRACE("(%p) : About to load surface\n", surface);
3855 if (!device->isInDraw) context = context_acquire(device, NULL);
3857 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3858 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3860 if (palette9_changed(surface))
3862 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
3863 /* TODO: This is not necessarily needed with hw palettized texture support */
3864 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3865 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
3866 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
3870 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3872 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3874 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3878 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3882 if (context) context_release(context);
3886 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3888 if (!surface->resource.allocatedMemory)
3890 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3891 surface->resource.size + RESOURCE_ALIGNMENT);
3892 if (!surface->resource.heapMemory)
3894 ERR("Out of memory\n");
3897 surface->resource.allocatedMemory =
3898 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
3902 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
3905 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3910 /* Read the framebuffer back into the surface */
3911 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
3913 struct wined3d_device *device = surface->resource.device;
3914 const struct wined3d_gl_info *gl_info;
3915 struct wined3d_context *context;
3919 BYTE *row, *top, *bottom;
3923 BOOL srcIsUpsideDown;
3928 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
3929 static BOOL warned = FALSE;
3931 ERR("The application tries to lock the render target, but render target locking is disabled\n");
3937 context = context_acquire(device, surface);
3938 context_apply_blit_state(context, device);
3939 gl_info = context->gl_info;
3943 /* Select the correct read buffer, and give some debug output.
3944 * There is no need to keep track of the current read buffer or reset it, every part of the code
3945 * that reads sets the read buffer as desired.
3947 if (surface_is_offscreen(surface))
3949 /* Mapping the primary render target which is not on a swapchain.
3950 * Read from the back buffer. */
3951 TRACE("Mapping offscreen render target.\n");
3952 glReadBuffer(device->offscreenBuffer);
3953 srcIsUpsideDown = TRUE;
3957 /* Onscreen surfaces are always part of a swapchain */
3958 GLenum buffer = surface_get_gl_buffer(surface);
3959 TRACE("Mapping %#x buffer.\n", buffer);
3960 glReadBuffer(buffer);
3961 checkGLcall("glReadBuffer");
3962 srcIsUpsideDown = FALSE;
3965 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
3968 local_rect.left = 0;
3970 local_rect.right = surface->resource.width;
3971 local_rect.bottom = surface->resource.height;
3977 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
3979 switch (surface->resource.format->id)
3981 case WINED3DFMT_P8_UINT:
3983 if (primary_render_target_is_p8(device))
3985 /* In case of P8 render targets the index is stored in the alpha component */
3987 type = GL_UNSIGNED_BYTE;
3989 bpp = surface->resource.format->byte_count;
3993 /* GL can't return palettized data, so read ARGB pixels into a
3994 * separate block of memory and convert them into palettized format
3995 * in software. Slow, but if the app means to use palettized render
3996 * targets and locks it...
3998 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
3999 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4000 * for the color channels when palettizing the colors.
4003 type = GL_UNSIGNED_BYTE;
4005 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4008 ERR("Out of memory\n");
4012 bpp = surface->resource.format->byte_count * 3;
4019 fmt = surface->resource.format->glFormat;
4020 type = surface->resource.format->glType;
4021 bpp = surface->resource.format->byte_count;
4024 if (surface->flags & SFLAG_PBO)
4026 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4027 checkGLcall("glBindBufferARB");
4030 ERR("mem not null for pbo -- unexpected\n");
4035 /* Save old pixel store pack state */
4036 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4037 checkGLcall("glGetIntegerv");
4038 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4039 checkGLcall("glGetIntegerv");
4040 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4041 checkGLcall("glGetIntegerv");
4043 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4044 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4045 checkGLcall("glPixelStorei");
4046 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4047 checkGLcall("glPixelStorei");
4048 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4049 checkGLcall("glPixelStorei");
4051 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4052 local_rect.right - local_rect.left,
4053 local_rect.bottom - local_rect.top,
4055 checkGLcall("glReadPixels");
4057 /* Reset previous pixel store pack state */
4058 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4059 checkGLcall("glPixelStorei");
4060 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4061 checkGLcall("glPixelStorei");
4062 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4063 checkGLcall("glPixelStorei");
4065 if (surface->flags & SFLAG_PBO)
4067 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4068 checkGLcall("glBindBufferARB");
4070 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4071 * to get a pointer to it and perform the flipping in software. This is a lot
4072 * faster than calling glReadPixels for each line. In case we want more speed
4073 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4074 if (!srcIsUpsideDown)
4076 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4077 checkGLcall("glBindBufferARB");
4079 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4080 checkGLcall("glMapBufferARB");
4084 /* TODO: Merge this with the palettization loop below for P8 targets */
4085 if(!srcIsUpsideDown) {
4087 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4088 Flip the lines in software */
4089 len = (local_rect.right - local_rect.left) * bpp;
4090 off = local_rect.left * bpp;
4092 row = HeapAlloc(GetProcessHeap(), 0, len);
4094 ERR("Out of memory\n");
4095 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4096 HeapFree(GetProcessHeap(), 0, mem);
4101 top = mem + pitch * local_rect.top;
4102 bottom = mem + pitch * (local_rect.bottom - 1);
4103 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4104 memcpy(row, top + off, len);
4105 memcpy(top + off, bottom + off, len);
4106 memcpy(bottom + off, row, len);
4110 HeapFree(GetProcessHeap(), 0, row);
4112 /* Unmap the temp PBO buffer */
4113 if (surface->flags & SFLAG_PBO)
4115 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4116 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4121 context_release(context);
4123 /* For P8 textures we need to perform an inverse palette lookup. This is
4124 * done by searching for a palette index which matches the RGB value.
4125 * Note this isn't guaranteed to work when there are multiple entries for
4126 * the same color but we have no choice. In case of P8 render targets,
4127 * the index is stored in the alpha component so no conversion is needed. */
4128 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4130 const PALETTEENTRY *pal = NULL;
4131 DWORD width = pitch / 3;
4134 if (surface->palette)
4136 pal = surface->palette->palents;
4140 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4141 HeapFree(GetProcessHeap(), 0, mem);
4145 for(y = local_rect.top; y < local_rect.bottom; y++) {
4146 for(x = local_rect.left; x < local_rect.right; x++) {
4147 /* start lines pixels */
4148 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4149 const BYTE *green = blue + 1;
4150 const BYTE *red = green + 1;
4152 for(c = 0; c < 256; c++) {
4153 if(*red == pal[c].peRed &&
4154 *green == pal[c].peGreen &&
4155 *blue == pal[c].peBlue)
4157 *((BYTE *) dest + y * width + x) = c;
4163 HeapFree(GetProcessHeap(), 0, mem);
4167 /* Read the framebuffer contents into a texture */
4168 static void read_from_framebuffer_texture(struct wined3d_surface *surface, BOOL srgb)
4170 struct wined3d_device *device = surface->resource.device;
4171 const struct wined3d_gl_info *gl_info;
4172 struct wined3d_context *context;
4174 if (!surface_is_offscreen(surface))
4176 /* We would need to flip onscreen surfaces, but there's no efficient
4177 * way to do that here. It makes more sense for the caller to
4178 * explicitly go through sysmem. */
4179 ERR("Not supported for onscreen targets.\n");
4183 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
4184 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
4185 * states in the stateblock, and no driver was found yet that had bugs in that regard.
4187 context = context_acquire(device, surface);
4188 gl_info = context->gl_info;
4189 device_invalidate_state(device, STATE_FRAMEBUFFER);
4191 surface_prepare_texture(surface, gl_info, srgb);
4192 surface_bind_and_dirtify(surface, gl_info, srgb);
4194 TRACE("Reading back offscreen render target %p.\n", surface);
4198 glReadBuffer(device->offscreenBuffer);
4199 checkGLcall("glReadBuffer");
4201 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4202 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4203 checkGLcall("glCopyTexSubImage2D");
4207 context_release(context);
4210 /* Context activation is done by the caller. */
4211 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4212 const struct wined3d_gl_info *gl_info, BOOL srgb)
4214 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4215 CONVERT_TYPES convert;
4216 struct wined3d_format format;
4218 if (surface->flags & alloc_flag) return;
4220 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4221 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4222 else surface->flags &= ~SFLAG_CONVERTED;
4224 surface_bind_and_dirtify(surface, gl_info, srgb);
4225 surface_allocate_surface(surface, gl_info, &format, srgb);
4226 surface->flags |= alloc_flag;
4229 /* Context activation is done by the caller. */
4230 void surface_prepare_texture(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
4232 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4234 struct wined3d_texture *texture = surface->container.u.texture;
4235 UINT sub_count = texture->level_count * texture->layer_count;
4238 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4240 for (i = 0; i < sub_count; ++i)
4242 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4243 surface_prepare_texture_internal(s, gl_info, srgb);
4249 surface_prepare_texture_internal(surface, gl_info, srgb);
4252 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4253 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4255 struct wined3d_device *device = surface->resource.device;
4256 UINT pitch = wined3d_surface_get_pitch(surface);
4257 const struct wined3d_gl_info *gl_info;
4258 struct wined3d_context *context;
4262 surface_get_rect(surface, rect, &local_rect);
4264 mem += local_rect.top * pitch + local_rect.left * bpp;
4265 w = local_rect.right - local_rect.left;
4266 h = local_rect.bottom - local_rect.top;
4268 /* Activate the correct context for the render target */
4269 context = context_acquire(device, surface);
4270 context_apply_blit_state(context, device);
4271 gl_info = context->gl_info;
4275 if (!surface_is_offscreen(surface))
4277 GLenum buffer = surface_get_gl_buffer(surface);
4278 TRACE("Unlocking %#x buffer.\n", buffer);
4279 context_set_draw_buffer(context, buffer);
4281 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4282 glPixelZoom(1.0f, -1.0f);
4286 /* Primary offscreen render target */
4287 TRACE("Offscreen render target.\n");
4288 context_set_draw_buffer(context, device->offscreenBuffer);
4290 glPixelZoom(1.0f, 1.0f);
4293 glRasterPos3i(local_rect.left, local_rect.top, 1);
4294 checkGLcall("glRasterPos3i");
4296 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4297 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4299 if (surface->flags & SFLAG_PBO)
4301 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4302 checkGLcall("glBindBufferARB");
4305 glDrawPixels(w, h, fmt, type, mem);
4306 checkGLcall("glDrawPixels");
4308 if (surface->flags & SFLAG_PBO)
4310 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4311 checkGLcall("glBindBufferARB");
4314 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4315 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4319 if (wined3d_settings.strict_draw_ordering
4320 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4321 && surface->container.u.swapchain->front_buffer == surface))
4324 context_release(context);
4327 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4328 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4330 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4331 const struct wined3d_device *device = surface->resource.device;
4332 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4333 BOOL blit_supported = FALSE;
4335 /* Copy the default values from the surface. Below we might perform fixups */
4336 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4337 *format = *surface->resource.format;
4338 *convert = NO_CONVERSION;
4340 /* Ok, now look if we have to do any conversion */
4341 switch (surface->resource.format->id)
4343 case WINED3DFMT_P8_UINT:
4344 /* Below the call to blit_supported is disabled for Wine 1.2
4345 * because the function isn't operating correctly yet. At the
4346 * moment 8-bit blits are handled in software and if certain GL
4347 * extensions are around, surface conversion is performed at
4348 * upload time. The blit_supported call recognizes it as a
4349 * destination fixup. This type of upload 'fixup' and 8-bit to
4350 * 8-bit blits need to be handled by the blit_shader.
4351 * TODO: get rid of this #if 0. */
4353 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4354 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4355 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4357 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4359 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4360 * texturing. Further also use conversion in case of color keying.
4361 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4362 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4363 * conflicts with this.
4365 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4366 || colorkey_active || !use_texturing)
4368 format->glFormat = GL_RGBA;
4369 format->glInternal = GL_RGBA;
4370 format->glType = GL_UNSIGNED_BYTE;
4371 format->conv_byte_count = 4;
4372 if (colorkey_active)
4373 *convert = CONVERT_PALETTED_CK;
4375 *convert = CONVERT_PALETTED;
4379 case WINED3DFMT_B2G3R3_UNORM:
4380 /* **********************
4381 GL_UNSIGNED_BYTE_3_3_2
4382 ********************** */
4383 if (colorkey_active) {
4384 /* This texture format will never be used.. So do not care about color keying
4385 up until the point in time it will be needed :-) */
4386 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4390 case WINED3DFMT_B5G6R5_UNORM:
4391 if (colorkey_active)
4393 *convert = CONVERT_CK_565;
4394 format->glFormat = GL_RGBA;
4395 format->glInternal = GL_RGB5_A1;
4396 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4397 format->conv_byte_count = 2;
4401 case WINED3DFMT_B5G5R5X1_UNORM:
4402 if (colorkey_active)
4404 *convert = CONVERT_CK_5551;
4405 format->glFormat = GL_BGRA;
4406 format->glInternal = GL_RGB5_A1;
4407 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4408 format->conv_byte_count = 2;
4412 case WINED3DFMT_B8G8R8_UNORM:
4413 if (colorkey_active)
4415 *convert = CONVERT_CK_RGB24;
4416 format->glFormat = GL_RGBA;
4417 format->glInternal = GL_RGBA8;
4418 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4419 format->conv_byte_count = 4;
4423 case WINED3DFMT_B8G8R8X8_UNORM:
4424 if (colorkey_active)
4426 *convert = CONVERT_RGB32_888;
4427 format->glFormat = GL_RGBA;
4428 format->glInternal = GL_RGBA8;
4429 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4430 format->conv_byte_count = 4;
4441 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4443 const struct wined3d_device *device = surface->resource.device;
4444 const struct wined3d_palette *pal = surface->palette;
4445 BOOL index_in_alpha = FALSE;
4448 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4449 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4450 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4451 * duplicate entries. Store the color key in the unused alpha component to speed the
4452 * download up and to make conversion unneeded. */
4453 index_in_alpha = primary_render_target_is_p8(device);
4457 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
4458 if (device->wined3d->flags & WINED3D_PALETTE_PER_SURFACE)
4460 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4463 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4464 * there's no palette at this time. */
4465 for (i = 0; i < 256; i++) table[i][3] = i;
4470 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
4471 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
4472 * capability flag is present (wine does advertise this capability) */
4473 for (i = 0; i < 256; ++i)
4475 table[i][0] = device->palettes[device->currentPalette][i].peRed;
4476 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
4477 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
4478 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
4484 TRACE("Using surface palette %p\n", pal);
4485 /* Get the surface's palette */
4486 for (i = 0; i < 256; ++i)
4488 table[i][0] = pal->palents[i].peRed;
4489 table[i][1] = pal->palents[i].peGreen;
4490 table[i][2] = pal->palents[i].peBlue;
4492 /* When index_in_alpha is set the palette index is stored in the
4493 * alpha component. In case of a readback we can then read
4494 * GL_ALPHA. Color keying is handled in BltOverride using a
4495 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4496 * color key itself is passed to glAlphaFunc in other cases the
4497 * alpha component of pixels that should be masked away is set to 0. */
4502 else if (colorkey && (i >= surface->SrcBltCKey.dwColorSpaceLowValue)
4503 && (i <= surface->SrcBltCKey.dwColorSpaceHighValue))
4507 else if (pal->flags & WINEDDPCAPS_ALPHA)
4509 table[i][3] = pal->palents[i].peFlags;
4519 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4520 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4524 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4529 memcpy(dst, src, pitch * height);
4532 case CONVERT_PALETTED:
4533 case CONVERT_PALETTED_CK:
4538 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4540 for (y = 0; y < height; y++)
4542 source = src + pitch * y;
4543 dest = dst + outpitch * y;
4544 /* This is an 1 bpp format, using the width here is fine */
4545 for (x = 0; x < width; x++) {
4546 BYTE color = *source++;
4547 *dest++ = table[color][0];
4548 *dest++ = table[color][1];
4549 *dest++ = table[color][2];
4550 *dest++ = table[color][3];
4556 case CONVERT_CK_565:
4558 /* Converting the 565 format in 5551 packed to emulate color-keying.
4560 Note : in all these conversion, it would be best to average the averaging
4561 pixels to get the color of the pixel that will be color-keyed to
4562 prevent 'color bleeding'. This will be done later on if ever it is
4565 Note2: Nvidia documents say that their driver does not support alpha + color keying
4566 on the same surface and disables color keying in such a case
4572 TRACE("Color keyed 565\n");
4574 for (y = 0; y < height; y++) {
4575 Source = (const WORD *)(src + y * pitch);
4576 Dest = (WORD *) (dst + y * outpitch);
4577 for (x = 0; x < width; x++ ) {
4578 WORD color = *Source++;
4579 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4580 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4581 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4589 case CONVERT_CK_5551:
4591 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4595 TRACE("Color keyed 5551\n");
4596 for (y = 0; y < height; y++) {
4597 Source = (const WORD *)(src + y * pitch);
4598 Dest = (WORD *) (dst + y * outpitch);
4599 for (x = 0; x < width; x++ ) {
4600 WORD color = *Source++;
4602 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4603 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4606 *Dest &= ~(1 << 15);
4613 case CONVERT_CK_RGB24:
4615 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4617 for (y = 0; y < height; y++)
4619 source = src + pitch * y;
4620 dest = dst + outpitch * y;
4621 for (x = 0; x < width; x++) {
4622 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4623 DWORD dstcolor = color << 8;
4624 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4625 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4627 *(DWORD*)dest = dstcolor;
4635 case CONVERT_RGB32_888:
4637 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4639 for (y = 0; y < height; y++)
4641 source = src + pitch * y;
4642 dest = dst + outpitch * y;
4643 for (x = 0; x < width; x++) {
4644 DWORD color = 0xffffff & *(const DWORD*)source;
4645 DWORD dstcolor = color << 8;
4646 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4647 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4649 *(DWORD*)dest = dstcolor;
4658 ERR("Unsupported conversion type %#x.\n", convert);
4663 BOOL palette9_changed(struct wined3d_surface *surface)
4665 struct wined3d_device *device = surface->resource.device;
4667 if (surface->palette || (surface->resource.format->id != WINED3DFMT_P8_UINT
4668 && surface->resource.format->id != WINED3DFMT_P8_UINT_A8_UNORM))
4670 /* If a ddraw-style palette is attached assume no d3d9 palette change.
4671 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
4676 if (surface->palette9)
4678 if (!memcmp(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
4685 surface->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
4687 memcpy(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
4692 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4694 /* Flip the surface contents */
4699 front->hDC = back->hDC;
4703 /* Flip the DIBsection */
4706 BOOL hasDib = front->flags & SFLAG_DIBSECTION;
4707 tmp = front->dib.DIBsection;
4708 front->dib.DIBsection = back->dib.DIBsection;
4709 back->dib.DIBsection = tmp;
4711 if (back->flags & SFLAG_DIBSECTION) front->flags |= SFLAG_DIBSECTION;
4712 else front->flags &= ~SFLAG_DIBSECTION;
4713 if (hasDib) back->flags |= SFLAG_DIBSECTION;
4714 else back->flags &= ~SFLAG_DIBSECTION;
4717 /* Flip the surface data */
4721 tmp = front->dib.bitmap_data;
4722 front->dib.bitmap_data = back->dib.bitmap_data;
4723 back->dib.bitmap_data = tmp;
4725 tmp = front->resource.allocatedMemory;
4726 front->resource.allocatedMemory = back->resource.allocatedMemory;
4727 back->resource.allocatedMemory = tmp;
4729 tmp = front->resource.heapMemory;
4730 front->resource.heapMemory = back->resource.heapMemory;
4731 back->resource.heapMemory = tmp;
4736 GLuint tmp_pbo = front->pbo;
4737 front->pbo = back->pbo;
4738 back->pbo = tmp_pbo;
4741 /* client_memory should not be different, but just in case */
4744 tmp = front->dib.client_memory;
4745 front->dib.client_memory = back->dib.client_memory;
4746 back->dib.client_memory = tmp;
4749 /* Flip the opengl texture */
4753 tmp = back->texture_name;
4754 back->texture_name = front->texture_name;
4755 front->texture_name = tmp;
4757 tmp = back->texture_name_srgb;
4758 back->texture_name_srgb = front->texture_name_srgb;
4759 front->texture_name_srgb = tmp;
4761 resource_unload(&back->resource);
4762 resource_unload(&front->resource);
4766 DWORD tmp_flags = back->flags;
4767 back->flags = front->flags;
4768 front->flags = tmp_flags;
4772 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4773 * pixel copy calls. */
4774 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4775 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4777 struct wined3d_device *device = dst_surface->resource.device;
4780 struct wined3d_context *context;
4781 BOOL upsidedown = FALSE;
4782 RECT dst_rect = *dst_rect_in;
4784 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4785 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4787 if(dst_rect.top > dst_rect.bottom) {
4788 UINT tmp = dst_rect.bottom;
4789 dst_rect.bottom = dst_rect.top;
4794 context = context_acquire(device, src_surface);
4795 context_apply_blit_state(context, device);
4796 surface_internal_preload(dst_surface, SRGB_RGB);
4799 /* Bind the target texture */
4800 glBindTexture(dst_surface->texture_target, dst_surface->texture_name);
4801 checkGLcall("glBindTexture");
4802 if (surface_is_offscreen(src_surface))
4804 TRACE("Reading from an offscreen target\n");
4805 upsidedown = !upsidedown;
4806 glReadBuffer(device->offscreenBuffer);
4810 glReadBuffer(surface_get_gl_buffer(src_surface));
4812 checkGLcall("glReadBuffer");
4814 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4815 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4817 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4819 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4821 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4822 ERR("Texture filtering not supported in direct blit\n");
4825 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4826 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4828 ERR("Texture filtering not supported in direct blit\n");
4832 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4833 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4835 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4837 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4838 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4839 src_rect->left, src_surface->resource.height - src_rect->bottom,
4840 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4844 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4845 /* I have to process this row by row to swap the image,
4846 * otherwise it would be upside down, so stretching in y direction
4847 * doesn't cost extra time
4849 * However, stretching in x direction can be avoided if not necessary
4851 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4852 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4854 /* Well, that stuff works, but it's very slow.
4855 * find a better way instead
4859 for (col = dst_rect.left; col < dst_rect.right; ++col)
4861 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4862 dst_rect.left + col /* x offset */, row /* y offset */,
4863 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4868 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4869 dst_rect.left /* x offset */, row /* y offset */,
4870 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4874 checkGLcall("glCopyTexSubImage2D");
4877 context_release(context);
4879 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4880 * path is never entered
4882 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4885 /* Uses the hardware to stretch and flip the image */
4886 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4887 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4889 struct wined3d_device *device = dst_surface->resource.device;
4890 struct wined3d_swapchain *src_swapchain = NULL;
4891 GLuint src, backup = 0;
4892 float left, right, top, bottom; /* Texture coordinates */
4893 UINT fbwidth = src_surface->resource.width;
4894 UINT fbheight = src_surface->resource.height;
4895 struct wined3d_context *context;
4896 GLenum drawBuffer = GL_BACK;
4897 GLenum texture_target;
4898 BOOL noBackBufferBackup;
4900 BOOL upsidedown = FALSE;
4901 RECT dst_rect = *dst_rect_in;
4903 TRACE("Using hwstretch blit\n");
4904 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4905 context = context_acquire(device, src_surface);
4906 context_apply_blit_state(context, device);
4907 surface_internal_preload(dst_surface, SRGB_RGB);
4909 src_offscreen = surface_is_offscreen(src_surface);
4910 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4911 if (!noBackBufferBackup && !src_surface->texture_name)
4913 /* Get it a description */
4914 surface_internal_preload(src_surface, SRGB_RGB);
4918 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4919 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4921 if (context->aux_buffers >= 2)
4923 /* Got more than one aux buffer? Use the 2nd aux buffer */
4924 drawBuffer = GL_AUX1;
4926 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4928 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4929 drawBuffer = GL_AUX0;
4932 if(noBackBufferBackup) {
4933 glGenTextures(1, &backup);
4934 checkGLcall("glGenTextures");
4935 glBindTexture(GL_TEXTURE_2D, backup);
4936 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
4937 texture_target = GL_TEXTURE_2D;
4939 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4940 * we are reading from the back buffer, the backup can be used as source texture
4942 texture_target = src_surface->texture_target;
4943 glBindTexture(texture_target, src_surface->texture_name);
4944 checkGLcall("glBindTexture(texture_target, src_surface->texture_name)");
4945 glEnable(texture_target);
4946 checkGLcall("glEnable(texture_target)");
4948 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4949 src_surface->flags &= ~SFLAG_INTEXTURE;
4952 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4953 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4955 if(dst_rect.top > dst_rect.bottom) {
4956 UINT tmp = dst_rect.bottom;
4957 dst_rect.bottom = dst_rect.top;
4964 TRACE("Reading from an offscreen target\n");
4965 upsidedown = !upsidedown;
4966 glReadBuffer(device->offscreenBuffer);
4970 glReadBuffer(surface_get_gl_buffer(src_surface));
4973 /* TODO: Only back up the part that will be overwritten */
4974 glCopyTexSubImage2D(texture_target, 0,
4975 0, 0 /* read offsets */,
4980 checkGLcall("glCopyTexSubImage2D");
4982 /* No issue with overriding these - the sampler is dirty due to blit usage */
4983 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
4984 wined3d_gl_mag_filter(magLookup, Filter));
4985 checkGLcall("glTexParameteri");
4986 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
4987 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
4988 checkGLcall("glTexParameteri");
4990 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
4991 src_swapchain = src_surface->container.u.swapchain;
4992 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
4994 src = backup ? backup : src_surface->texture_name;
4998 glReadBuffer(GL_FRONT);
4999 checkGLcall("glReadBuffer(GL_FRONT)");
5001 glGenTextures(1, &src);
5002 checkGLcall("glGenTextures(1, &src)");
5003 glBindTexture(GL_TEXTURE_2D, src);
5004 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
5006 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5007 * out for power of 2 sizes
5009 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5010 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5011 checkGLcall("glTexImage2D");
5012 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5013 0, 0 /* read offsets */,
5018 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5019 checkGLcall("glTexParameteri");
5020 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5021 checkGLcall("glTexParameteri");
5023 glReadBuffer(GL_BACK);
5024 checkGLcall("glReadBuffer(GL_BACK)");
5026 if(texture_target != GL_TEXTURE_2D) {
5027 glDisable(texture_target);
5028 glEnable(GL_TEXTURE_2D);
5029 texture_target = GL_TEXTURE_2D;
5032 checkGLcall("glEnd and previous");
5034 left = src_rect->left;
5035 right = src_rect->right;
5039 top = src_surface->resource.height - src_rect->top;
5040 bottom = src_surface->resource.height - src_rect->bottom;
5044 top = src_surface->resource.height - src_rect->bottom;
5045 bottom = src_surface->resource.height - src_rect->top;
5048 if (src_surface->flags & SFLAG_NORMCOORD)
5050 left /= src_surface->pow2Width;
5051 right /= src_surface->pow2Width;
5052 top /= src_surface->pow2Height;
5053 bottom /= src_surface->pow2Height;
5056 /* draw the source texture stretched and upside down. The correct surface is bound already */
5057 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5058 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5060 context_set_draw_buffer(context, drawBuffer);
5061 glReadBuffer(drawBuffer);
5065 glTexCoord2f(left, bottom);
5069 glTexCoord2f(left, top);
5070 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5073 glTexCoord2f(right, top);
5074 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5077 glTexCoord2f(right, bottom);
5078 glVertex2i(dst_rect.right - dst_rect.left, 0);
5080 checkGLcall("glEnd and previous");
5082 if (texture_target != dst_surface->texture_target)
5084 glDisable(texture_target);
5085 glEnable(dst_surface->texture_target);
5086 texture_target = dst_surface->texture_target;
5089 /* Now read the stretched and upside down image into the destination texture */
5090 glBindTexture(texture_target, dst_surface->texture_name);
5091 checkGLcall("glBindTexture");
5092 glCopyTexSubImage2D(texture_target,
5094 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5095 0, 0, /* We blitted the image to the origin */
5096 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5097 checkGLcall("glCopyTexSubImage2D");
5099 if(drawBuffer == GL_BACK) {
5100 /* Write the back buffer backup back */
5102 if(texture_target != GL_TEXTURE_2D) {
5103 glDisable(texture_target);
5104 glEnable(GL_TEXTURE_2D);
5105 texture_target = GL_TEXTURE_2D;
5107 glBindTexture(GL_TEXTURE_2D, backup);
5108 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
5112 if (texture_target != src_surface->texture_target)
5114 glDisable(texture_target);
5115 glEnable(src_surface->texture_target);
5116 texture_target = src_surface->texture_target;
5118 glBindTexture(src_surface->texture_target, src_surface->texture_name);
5119 checkGLcall("glBindTexture(src_surface->texture_target, src_surface->texture_name)");
5124 glTexCoord2f(0.0f, 0.0f);
5125 glVertex2i(0, fbheight);
5128 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5132 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5133 (float)fbheight / (float)src_surface->pow2Height);
5134 glVertex2i(fbwidth, 0);
5137 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5138 glVertex2i(fbwidth, fbheight);
5141 glDisable(texture_target);
5142 checkGLcall("glDisable(texture_target)");
5145 if (src != src_surface->texture_name && src != backup)
5147 glDeleteTextures(1, &src);
5148 checkGLcall("glDeleteTextures(1, &src)");
5151 glDeleteTextures(1, &backup);
5152 checkGLcall("glDeleteTextures(1, &backup)");
5157 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5159 context_release(context);
5161 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5162 * path is never entered
5164 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5167 /* Front buffer coordinates are always full screen coordinates, but our GL
5168 * drawable is limited to the window's client area. The sysmem and texture
5169 * copies do have the full screen size. Note that GL has a bottom-left
5170 * origin, while D3D has a top-left origin. */
5171 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5173 UINT drawable_height;
5175 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5176 && surface == surface->container.u.swapchain->front_buffer)
5178 POINT offset = {0, 0};
5181 ScreenToClient(window, &offset);
5182 OffsetRect(rect, offset.x, offset.y);
5184 GetClientRect(window, &windowsize);
5185 drawable_height = windowsize.bottom - windowsize.top;
5189 drawable_height = surface->resource.height;
5192 rect->top = drawable_height - rect->top;
5193 rect->bottom = drawable_height - rect->bottom;
5196 static void surface_blt_to_drawable(struct wined3d_device *device,
5197 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5198 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5199 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5201 struct wined3d_context *context;
5202 RECT src_rect, dst_rect;
5204 src_rect = *src_rect_in;
5205 dst_rect = *dst_rect_in;
5207 /* Make sure the surface is up-to-date. This should probably use
5208 * surface_load_location() and worry about the destination surface too,
5209 * unless we're overwriting it completely. */
5210 surface_internal_preload(src_surface, SRGB_RGB);
5212 /* Activate the destination context, set it up for blitting */
5213 context = context_acquire(device, dst_surface);
5214 context_apply_blit_state(context, device);
5216 if (!surface_is_offscreen(dst_surface))
5217 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5219 device->blitter->set_shader(device->blit_priv, context->gl_info, src_surface);
5225 glEnable(GL_ALPHA_TEST);
5226 checkGLcall("glEnable(GL_ALPHA_TEST)");
5228 /* When the primary render target uses P8, the alpha component
5229 * contains the palette index. Which means that the colorkey is one of
5230 * the palette entries. In other cases pixels that should be masked
5231 * away have alpha set to 0. */
5232 if (primary_render_target_is_p8(device))
5233 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
5235 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5236 checkGLcall("glAlphaFunc");
5240 glDisable(GL_ALPHA_TEST);
5241 checkGLcall("glDisable(GL_ALPHA_TEST)");
5244 draw_textured_quad(src_surface, &src_rect, &dst_rect, filter);
5248 glDisable(GL_ALPHA_TEST);
5249 checkGLcall("glDisable(GL_ALPHA_TEST)");
5254 /* Leave the opengl state valid for blitting */
5255 device->blitter->unset_shader(context->gl_info);
5257 if (wined3d_settings.strict_draw_ordering
5258 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5259 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5260 wglFlush(); /* Flush to ensure ordering across contexts. */
5262 context_release(context);
5265 /* Do not call while under the GL lock. */
5266 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const WINED3DCOLORVALUE *color)
5268 struct wined3d_device *device = s->resource.device;
5269 const struct blit_shader *blitter;
5271 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5272 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5275 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5276 return WINED3DERR_INVALIDCALL;
5279 return blitter->color_fill(device, s, rect, color);
5282 /* Do not call while under the GL lock. */
5283 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5284 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5285 WINED3DTEXTUREFILTERTYPE Filter)
5287 struct wined3d_device *device = dst_surface->resource.device;
5288 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5289 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5291 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5292 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5293 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5295 /* Get the swapchain. One of the surfaces has to be a primary surface */
5296 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5298 WARN("Destination is in sysmem, rejecting gl blt\n");
5299 return WINED3DERR_INVALIDCALL;
5302 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5303 dstSwapchain = dst_surface->container.u.swapchain;
5307 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5309 WARN("Src is in sysmem, rejecting gl blt\n");
5310 return WINED3DERR_INVALIDCALL;
5313 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5314 srcSwapchain = src_surface->container.u.swapchain;
5317 /* Early sort out of cases where no render target is used */
5318 if (!dstSwapchain && !srcSwapchain
5319 && src_surface != device->fb.render_targets[0]
5320 && dst_surface != device->fb.render_targets[0])
5322 TRACE("No surface is render target, not using hardware blit.\n");
5323 return WINED3DERR_INVALIDCALL;
5326 /* No destination color keying supported */
5327 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5329 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5330 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5331 return WINED3DERR_INVALIDCALL;
5334 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
5335 if (dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->back_buffers
5336 && dst_surface == dstSwapchain->front_buffer
5337 && src_surface == dstSwapchain->back_buffers[0])
5339 /* Half-Life does a Blt from the back buffer to the front buffer,
5340 * Full surface size, no flags... Use present instead
5342 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
5345 /* Check rects - wined3d_swapchain_present() doesn't handle them. */
5348 TRACE("Looking if a Present can be done...\n");
5349 /* Source Rectangle must be full surface */
5350 if (src_rect->left || src_rect->top
5351 || src_rect->right != src_surface->resource.width
5352 || src_rect->bottom != src_surface->resource.height)
5354 TRACE("No, Source rectangle doesn't match\n");
5358 /* No stretching may occur */
5359 if (src_rect->right != dst_rect->right - dst_rect->left
5360 || src_rect->bottom != dst_rect->bottom - dst_rect->top)
5362 TRACE("No, stretching is done\n");
5366 /* Destination must be full surface or match the clipping rectangle */
5367 if (dst_surface->clipper && dst_surface->clipper->hWnd)
5371 GetClientRect(dst_surface->clipper->hWnd, &cliprect);
5372 pos[0].x = dst_rect->left;
5373 pos[0].y = dst_rect->top;
5374 pos[1].x = dst_rect->right;
5375 pos[1].y = dst_rect->bottom;
5376 MapWindowPoints(GetDesktopWindow(), dst_surface->clipper->hWnd, pos, 2);
5378 if (pos[0].x != cliprect.left || pos[0].y != cliprect.top
5379 || pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
5381 TRACE("No, dest rectangle doesn't match(clipper)\n");
5382 TRACE("Clip rect at %s\n", wine_dbgstr_rect(&cliprect));
5383 TRACE("Blt dest: %s\n", wine_dbgstr_rect(dst_rect));
5387 else if (dst_rect->left || dst_rect->top
5388 || dst_rect->right != dst_surface->resource.width
5389 || dst_rect->bottom != dst_surface->resource.height)
5391 TRACE("No, dest rectangle doesn't match(surface size)\n");
5397 /* These flags are unimportant for the flag check, remove them */
5398 if (!(flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)))
5400 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
5402 /* The idea behind this is that a glReadPixels and a glDrawPixels call
5403 * take very long, while a flip is fast.
5404 * This applies to Half-Life, which does such Blts every time it finished
5405 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
5406 * menu. This is also used by all apps when they do windowed rendering
5408 * The problem is that flipping is not really the same as copying. After a
5409 * Blt the front buffer is a copy of the back buffer, and the back buffer is
5410 * untouched. Therefore it's necessary to override the swap effect
5411 * and to set it back after the flip.
5413 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
5417 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
5418 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
5420 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead.\n");
5421 wined3d_swapchain_present(dstSwapchain, NULL, NULL, dstSwapchain->win_handle, NULL, 0);
5423 dstSwapchain->presentParms.SwapEffect = orig_swap;
5430 TRACE("Unsupported blit between buffers on the same swapchain\n");
5431 return WINED3DERR_INVALIDCALL;
5432 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
5433 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5434 return WINED3DERR_INVALIDCALL;
5435 } else if(dstSwapchain && srcSwapchain) {
5436 FIXME("Implement hardware blit between two different swapchains\n");
5437 return WINED3DERR_INVALIDCALL;
5439 else if (dstSwapchain)
5441 /* Handled with regular texture -> swapchain blit */
5442 if (src_surface == device->fb.render_targets[0])
5443 TRACE("Blit from active render target to a swapchain\n");
5445 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5447 FIXME("Implement blit from a swapchain to the active render target\n");
5448 return WINED3DERR_INVALIDCALL;
5451 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5453 /* Blit from render target to texture */
5456 /* P8 read back is not implemented */
5457 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5458 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5460 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5461 return WINED3DERR_INVALIDCALL;
5464 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5466 TRACE("Color keying not supported by frame buffer to texture blit\n");
5467 return WINED3DERR_INVALIDCALL;
5468 /* Destination color key is checked above */
5471 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5476 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5477 * flip the image nor scale it.
5479 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5480 * -> If the app wants a image width an unscaled width, copy it line per line
5481 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5482 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5483 * back buffer. This is slower than reading line per line, thus not used for flipping
5484 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5485 * pixel by pixel. */
5486 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5487 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5489 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5490 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, Filter);
5492 TRACE("Using hardware stretching to flip / stretch the texture\n");
5493 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5496 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5498 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5499 dst_surface->resource.allocatedMemory = NULL;
5500 dst_surface->resource.heapMemory = NULL;
5504 dst_surface->flags &= ~SFLAG_INSYSMEM;
5509 else if (src_surface)
5511 /* Blit from offscreen surface to render target */
5512 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5513 WINEDDCOLORKEY oldBltCKey = src_surface->SrcBltCKey;
5515 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5517 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5518 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5519 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5521 FIXME("Unsupported blit operation falling back to software\n");
5522 return WINED3DERR_INVALIDCALL;
5525 /* Color keying: Check if we have to do a color keyed blt,
5526 * and if not check if a color key is activated.
5528 * Just modify the color keying parameters in the surface and restore them afterwards
5529 * The surface keeps track of the color key last used to load the opengl surface.
5530 * PreLoad will catch the change to the flags and color key and reload if necessary.
5532 if (flags & WINEDDBLT_KEYSRC)
5534 /* Use color key from surface */
5536 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5538 /* Use color key from DDBltFx */
5539 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5540 src_surface->SrcBltCKey = DDBltFx->ddckSrcColorkey;
5544 /* Do not use color key */
5545 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5548 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5549 src_surface, src_rect, dst_surface, dst_rect);
5551 /* Restore the color key parameters */
5552 src_surface->CKeyFlags = oldCKeyFlags;
5553 src_surface->SrcBltCKey = oldBltCKey;
5555 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
5560 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5561 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5562 return WINED3DERR_INVALIDCALL;
5565 /* GL locking is done by the caller */
5566 static void surface_depth_blt(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
5567 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5569 struct wined3d_device *device = surface->resource.device;
5570 GLint compare_mode = GL_NONE;
5571 struct blt_info info;
5572 GLint old_binding = 0;
5575 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5577 glDisable(GL_CULL_FACE);
5578 glDisable(GL_BLEND);
5579 glDisable(GL_ALPHA_TEST);
5580 glDisable(GL_SCISSOR_TEST);
5581 glDisable(GL_STENCIL_TEST);
5582 glEnable(GL_DEPTH_TEST);
5583 glDepthFunc(GL_ALWAYS);
5584 glDepthMask(GL_TRUE);
5585 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5586 glViewport(x, y, w, h);
5588 SetRect(&rect, 0, h, w, 0);
5589 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5590 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
5591 glGetIntegerv(info.binding, &old_binding);
5592 glBindTexture(info.bind_target, texture);
5593 if (gl_info->supported[ARB_SHADOW])
5595 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5596 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5599 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5600 gl_info, info.tex_type, &surface->ds_current_size);
5602 glBegin(GL_TRIANGLE_STRIP);
5603 glTexCoord3fv(info.coords[0]);
5604 glVertex2f(-1.0f, -1.0f);
5605 glTexCoord3fv(info.coords[1]);
5606 glVertex2f(1.0f, -1.0f);
5607 glTexCoord3fv(info.coords[2]);
5608 glVertex2f(-1.0f, 1.0f);
5609 glTexCoord3fv(info.coords[3]);
5610 glVertex2f(1.0f, 1.0f);
5613 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5614 glBindTexture(info.bind_target, old_binding);
5618 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5621 void surface_modify_ds_location(struct wined3d_surface *surface,
5622 DWORD location, UINT w, UINT h)
5624 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5626 if (location & ~SFLAG_DS_LOCATIONS)
5627 FIXME("Invalid location (%#x) specified.\n", location);
5629 surface->ds_current_size.cx = w;
5630 surface->ds_current_size.cy = h;
5631 surface->flags &= ~SFLAG_DS_LOCATIONS;
5632 surface->flags |= location;
5635 /* Context activation is done by the caller. */
5636 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5638 struct wined3d_device *device = surface->resource.device;
5639 const struct wined3d_gl_info *gl_info = context->gl_info;
5642 TRACE("surface %p, new location %#x.\n", surface, location);
5644 /* TODO: Make this work for modes other than FBO */
5645 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5647 if (!(surface->flags & location))
5649 w = surface->ds_current_size.cx;
5650 h = surface->ds_current_size.cy;
5651 surface->ds_current_size.cx = 0;
5652 surface->ds_current_size.cy = 0;
5656 w = surface->resource.width;
5657 h = surface->resource.height;
5660 if (surface->ds_current_size.cx == surface->resource.width
5661 && surface->ds_current_size.cy == surface->resource.height)
5663 TRACE("Location (%#x) is already up to date.\n", location);
5667 if (surface->current_renderbuffer)
5669 FIXME("Not supported with fixed up depth stencil.\n");
5673 if (!(surface->flags & SFLAG_DS_LOCATIONS))
5675 /* This mostly happens when a depth / stencil is used without being
5676 * cleared first. In principle we could upload from sysmem, or
5677 * explicitly clear before first usage. For the moment there don't
5678 * appear to be a lot of applications depending on this, so a FIXME
5680 FIXME("No up to date depth stencil location.\n");
5681 surface->flags |= location;
5682 surface->ds_current_size.cx = surface->resource.width;
5683 surface->ds_current_size.cy = surface->resource.height;
5687 if (location == SFLAG_DS_OFFSCREEN)
5689 GLint old_binding = 0;
5692 /* The render target is allowed to be smaller than the depth/stencil
5693 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5694 * than the offscreen surface. Don't overwrite the offscreen surface
5695 * with undefined data. */
5696 w = min(w, context->swapchain->presentParms.BackBufferWidth);
5697 h = min(h, context->swapchain->presentParms.BackBufferHeight);
5699 TRACE("Copying onscreen depth buffer to depth texture.\n");
5703 if (!device->depth_blt_texture)
5705 glGenTextures(1, &device->depth_blt_texture);
5708 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5709 * directly on the FBO texture. That's because we need to flip. */
5710 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5711 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5712 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5714 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5715 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5719 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5720 bind_target = GL_TEXTURE_2D;
5722 glBindTexture(bind_target, device->depth_blt_texture);
5723 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5724 * internal format, because the internal format might include stencil
5725 * data. In principle we should copy stencil data as well, but unless
5726 * the driver supports stencil export it's hard to do, and doesn't
5727 * seem to be needed in practice. If the hardware doesn't support
5728 * writing stencil data, the glCopyTexImage2D() call might trigger
5729 * software fallbacks. */
5730 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5731 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5732 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5733 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5734 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5735 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5736 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5737 glBindTexture(bind_target, old_binding);
5739 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5740 NULL, surface, SFLAG_INTEXTURE);
5741 context_set_draw_buffer(context, GL_NONE);
5742 glReadBuffer(GL_NONE);
5744 /* Do the actual blit */
5745 surface_depth_blt(surface, gl_info, device->depth_blt_texture, 0, 0, w, h, bind_target);
5746 checkGLcall("depth_blt");
5748 context_invalidate_state(context, STATE_FRAMEBUFFER);
5752 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5754 else if (location == SFLAG_DS_ONSCREEN)
5756 TRACE("Copying depth texture to onscreen depth buffer.\n");
5760 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5761 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5762 surface_depth_blt(surface, gl_info, surface->texture_name,
5763 0, surface->pow2Height - h, w, h, surface->texture_target);
5764 checkGLcall("depth_blt");
5766 context_invalidate_state(context, STATE_FRAMEBUFFER);
5770 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5774 ERR("Invalid location (%#x) specified.\n", location);
5777 surface->flags |= location;
5778 surface->ds_current_size.cx = surface->resource.width;
5779 surface->ds_current_size.cy = surface->resource.height;
5782 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5784 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5785 struct wined3d_surface *overlay;
5787 TRACE("surface %p, location %s, persistent %#x.\n",
5788 surface, debug_surflocation(location), persistent);
5790 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5792 if (surface_is_offscreen(surface))
5794 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same
5795 * for offscreen targets. */
5796 if (location & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))
5797 location |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
5801 TRACE("Surface %p is an onscreen surface.\n", surface);
5805 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5806 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5807 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5811 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5812 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5814 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5816 TRACE("Passing to container.\n");
5817 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5820 surface->flags &= ~SFLAG_LOCATIONS;
5821 surface->flags |= location;
5823 /* Redraw emulated overlays, if any */
5824 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5826 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5828 overlay->surface_ops->surface_draw_overlay(overlay);
5834 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5836 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5838 TRACE("Passing to container\n");
5839 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5842 surface->flags &= ~location;
5845 if (!(surface->flags & SFLAG_LOCATIONS))
5847 ERR("Surface %p does not have any up to date location.\n", surface);
5851 static DWORD resource_access_from_location(DWORD location)
5855 case SFLAG_INSYSMEM:
5856 return WINED3D_RESOURCE_ACCESS_CPU;
5858 case SFLAG_INDRAWABLE:
5859 case SFLAG_INSRGBTEX:
5860 case SFLAG_INTEXTURE:
5861 return WINED3D_RESOURCE_ACCESS_GPU;
5864 FIXME("Unhandled location %#x.\n", location);
5869 static void surface_load_sysmem(struct wined3d_surface *surface,
5870 const struct wined3d_gl_info *gl_info, const RECT *rect)
5872 surface_prepare_system_memory(surface);
5874 /* Download the surface to system memory. */
5875 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5877 struct wined3d_device *device = surface->resource.device;
5878 struct wined3d_context *context = NULL;
5880 if (!device->isInDraw)
5881 context = context_acquire(device, NULL);
5883 surface_bind_and_dirtify(surface, gl_info, !(surface->flags & SFLAG_INTEXTURE));
5884 surface_download_data(surface, gl_info);
5887 context_release(context);
5892 /* Note: It might be faster to download into a texture first. */
5893 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5894 wined3d_surface_get_pitch(surface));
5897 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5898 const struct wined3d_gl_info *gl_info, const RECT *rect)
5900 struct wined3d_device *device = surface->resource.device;
5901 struct wined3d_format format;
5902 CONVERT_TYPES convert;
5906 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5907 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5909 if (surface->flags & SFLAG_INTEXTURE)
5913 surface_get_rect(surface, rect, &r);
5914 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5919 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5921 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5922 * path through sysmem. */
5923 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5926 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5928 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5929 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5931 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5933 struct wined3d_context *context = NULL;
5935 TRACE("Removing the pbo attached to surface %p.\n", surface);
5937 if (!device->isInDraw)
5938 context = context_acquire(device, NULL);
5940 surface_remove_pbo(surface, gl_info);
5943 context_release(context);
5946 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5948 UINT height = surface->resource.height;
5949 UINT width = surface->resource.width;
5950 UINT src_pitch, dst_pitch;
5952 byte_count = format.conv_byte_count;
5953 src_pitch = wined3d_surface_get_pitch(surface);
5955 /* Stick to the alignment for the converted surface too, makes it
5956 * easier to load the surface. */
5957 dst_pitch = width * byte_count;
5958 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5960 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5962 ERR("Out of memory (%u).\n", dst_pitch * height);
5963 return E_OUTOFMEMORY;
5966 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5967 src_pitch, width, height, dst_pitch, convert, surface);
5969 surface->flags |= SFLAG_CONVERTED;
5973 surface->flags &= ~SFLAG_CONVERTED;
5974 mem = surface->resource.allocatedMemory;
5975 byte_count = format.byte_count;
5978 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5980 /* Don't delete PBO memory. */
5981 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5982 HeapFree(GetProcessHeap(), 0, mem);
5987 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5988 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5990 const DWORD attach_flags = WINED3DFMT_FLAG_FBO_ATTACHABLE | WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB;
5991 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5992 struct wined3d_device *device = surface->resource.device;
5993 struct wined3d_context *context = NULL;
5994 UINT width, src_pitch, dst_pitch;
5995 struct wined3d_bo_address data;
5996 struct wined3d_format format;
5997 POINT dst_point = {0, 0};
5998 CONVERT_TYPES convert;
6001 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
6002 && surface_is_offscreen(surface)
6003 && (surface->flags & SFLAG_INDRAWABLE))
6005 read_from_framebuffer_texture(surface, srgb);
6010 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6011 && (surface->resource.format->flags & attach_flags) == attach_flags
6012 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6013 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6014 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6017 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
6018 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6020 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
6021 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6026 /* Upload from system memory */
6028 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6029 TRUE /* We will use textures */, &format, &convert);
6033 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6035 /* Performance warning... */
6036 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6037 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6042 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6044 /* Performance warning... */
6045 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6046 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6050 if (!(surface->flags & SFLAG_INSYSMEM))
6052 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6053 /* Lets hope we get it from somewhere... */
6054 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6057 if (!device->isInDraw)
6058 context = context_acquire(device, NULL);
6060 surface_prepare_texture(surface, gl_info, srgb);
6061 surface_bind_and_dirtify(surface, gl_info, srgb);
6063 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6065 surface->flags |= SFLAG_GLCKEY;
6066 surface->glCKey = surface->SrcBltCKey;
6068 else surface->flags &= ~SFLAG_GLCKEY;
6070 width = surface->resource.width;
6071 src_pitch = wined3d_surface_get_pitch(surface);
6073 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6074 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6076 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6078 TRACE("Removing the pbo attached to surface %p.\n", surface);
6079 surface_remove_pbo(surface, gl_info);
6084 /* This code is entered for texture formats which need a fixup. */
6085 UINT height = surface->resource.height;
6087 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6088 dst_pitch = width * format.conv_byte_count;
6089 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6091 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6093 ERR("Out of memory (%u).\n", dst_pitch * height);
6095 context_release(context);
6096 return E_OUTOFMEMORY;
6098 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6100 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6102 /* This code is only entered for color keying fixups */
6103 UINT height = surface->resource.height;
6105 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6106 dst_pitch = width * format.conv_byte_count;
6107 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6109 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6111 ERR("Out of memory (%u).\n", dst_pitch * height);
6113 context_release(context);
6114 return E_OUTOFMEMORY;
6116 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6117 width, height, dst_pitch, convert, surface);
6121 mem = surface->resource.allocatedMemory;
6124 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6126 surface_upload_data(surface, gl_info, &format, &src_rect, width, &dst_point, srgb, &data);
6129 context_release(context);
6131 /* Don't delete PBO memory. */
6132 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6133 HeapFree(GetProcessHeap(), 0, mem);
6138 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6140 struct wined3d_device *device = surface->resource.device;
6141 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6142 BOOL in_fbo = FALSE;
6145 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6147 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6149 if (location == SFLAG_INTEXTURE)
6151 struct wined3d_context *context = context_acquire(device, NULL);
6152 surface_load_ds_location(surface, context, SFLAG_DS_OFFSCREEN);
6153 context_release(context);
6158 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6159 return WINED3DERR_INVALIDCALL;
6163 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6165 if (surface_is_offscreen(surface))
6167 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same
6168 * for offscreen targets. Prefer SFLAG_INTEXTURE. */
6169 if (location == SFLAG_INDRAWABLE)
6170 location = SFLAG_INTEXTURE;
6175 TRACE("Surface %p is an onscreen surface.\n", surface);
6179 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6180 location = SFLAG_INTEXTURE;
6182 if (surface->flags & location)
6184 TRACE("Location already up to date.\n");
6188 if (WARN_ON(d3d_surface))
6190 DWORD required_access = resource_access_from_location(location);
6191 if ((surface->resource.access_flags & required_access) != required_access)
6192 WARN("Operation requires %#x access, but surface only has %#x.\n",
6193 required_access, surface->resource.access_flags);
6196 if (!(surface->flags & SFLAG_LOCATIONS))
6198 ERR("Surface %p does not have any up to date location.\n", surface);
6199 surface->flags |= SFLAG_LOST;
6200 return WINED3DERR_DEVICELOST;
6205 case SFLAG_INSYSMEM:
6206 surface_load_sysmem(surface, gl_info, rect);
6209 case SFLAG_INDRAWABLE:
6210 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6214 case SFLAG_INTEXTURE:
6215 case SFLAG_INSRGBTEX:
6216 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6221 ERR("Don't know how to handle location %#x.\n", location);
6227 surface->flags |= location;
6229 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6230 surface_evict_sysmem(surface);
6233 if (in_fbo && (surface->flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)))
6235 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
6236 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
6239 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6240 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6242 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6248 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6250 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6252 /* Not on a swapchain - must be offscreen */
6253 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6255 /* The front buffer is always onscreen */
6256 if (surface == swapchain->front_buffer) return FALSE;
6258 /* If the swapchain is rendered to an FBO, the backbuffer is
6259 * offscreen, otherwise onscreen */
6260 return swapchain->render_to_fbo;
6263 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6264 /* Context activation is done by the caller. */
6265 static void ffp_blit_free(struct wined3d_device *device) { }
6267 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6268 /* Context activation is done by the caller. */
6269 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6272 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6274 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6276 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6278 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6282 /* Context activation is done by the caller. */
6283 static HRESULT ffp_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, struct wined3d_surface *surface)
6285 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6287 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6288 * else the surface is converted in software at upload time in LoadLocation.
6290 if(fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6291 ffp_blit_p8_upload_palette(surface, gl_info);
6294 glEnable(surface->texture_target);
6295 checkGLcall("glEnable(surface->texture_target)");
6300 /* Context activation is done by the caller. */
6301 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6304 glDisable(GL_TEXTURE_2D);
6305 checkGLcall("glDisable(GL_TEXTURE_2D)");
6306 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6308 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6309 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6311 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6313 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6314 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6319 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6320 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6321 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6323 enum complex_fixup src_fixup;
6327 case WINED3D_BLIT_OP_COLOR_BLIT:
6328 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
6331 src_fixup = get_complex_fixup(src_format->color_fixup);
6332 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6334 TRACE("Checking support for fixup:\n");
6335 dump_color_fixup_desc(src_format->color_fixup);
6338 if (!is_identity_fixup(dst_format->color_fixup))
6340 TRACE("Destination fixups are not supported\n");
6344 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6346 TRACE("P8 fixup supported\n");
6350 /* We only support identity conversions. */
6351 if (is_identity_fixup(src_format->color_fixup))
6357 TRACE("[FAILED]\n");
6360 case WINED3D_BLIT_OP_COLOR_FILL:
6361 if (dst_pool == WINED3DPOOL_SYSTEMMEM)
6364 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6366 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6369 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6371 TRACE("Color fill not supported\n");
6375 /* FIXME: We should reject color fills on formats with fixups,
6376 * but this would break P8 color fills for example. */
6380 case WINED3D_BLIT_OP_DEPTH_FILL:
6384 TRACE("Unsupported blit_op=%d\n", blit_op);
6389 /* Do not call while under the GL lock. */
6390 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6391 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
6393 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6394 struct wined3d_fb_state fb = {&dst_surface, NULL};
6396 return device_clear_render_targets(device, 1, &fb,
6397 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6400 /* Do not call while under the GL lock. */
6401 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6402 struct wined3d_surface *surface, const RECT *rect, float depth)
6404 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6405 struct wined3d_fb_state fb = {NULL, surface};
6407 return device_clear_render_targets(device, 0, &fb,
6408 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6411 const struct blit_shader ffp_blit = {
6417 ffp_blit_color_fill,
6418 ffp_blit_depth_fill,
6421 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6426 /* Context activation is done by the caller. */
6427 static void cpu_blit_free(struct wined3d_device *device)
6431 /* Context activation is done by the caller. */
6432 static HRESULT cpu_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, struct wined3d_surface *surface)
6437 /* Context activation is done by the caller. */
6438 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6442 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6443 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6444 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6446 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6454 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6455 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6456 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6458 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6459 const struct wined3d_format *src_format, *dst_format;
6460 struct wined3d_surface *orig_src = src_surface;
6461 WINED3DLOCKED_RECT dlock, slock;
6462 HRESULT hr = WINED3D_OK;
6468 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6469 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6470 flags, fx, debug_d3dtexturefiltertype(filter));
6480 full_rect.right = dst_surface->resource.width;
6481 full_rect.bottom = dst_surface->resource.height;
6482 IntersectRect(&xdst, &full_rect, dst_rect);
6486 BOOL clip_horiz, clip_vert;
6489 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6490 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6492 if (clip_vert || clip_horiz)
6494 /* Now check if this is a special case or not... */
6495 if ((flags & WINEDDBLT_DDFX)
6496 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6497 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6499 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6507 xsrc.left -= xdst.left;
6510 if (xdst.right > dst_surface->resource.width)
6512 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6513 xdst.right = (int)dst_surface->resource.width;
6521 xsrc.top -= xdst.top;
6524 if (xdst.bottom > dst_surface->resource.height)
6526 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6527 xdst.bottom = (int)dst_surface->resource.height;
6531 /* And check if after clipping something is still to be done... */
6532 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6533 || (xdst.left >= (int)dst_surface->resource.width)
6534 || (xdst.top >= (int)dst_surface->resource.height)
6535 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6536 || (xsrc.left >= (int)src_surface->resource.width)
6537 || (xsrc.top >= (int)src_surface->resource.height))
6539 TRACE("Nothing to be done after clipping.\n");
6545 if (src_surface == dst_surface)
6547 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6549 src_format = dst_surface->resource.format;
6550 dst_format = src_format;
6554 dst_format = dst_surface->resource.format;
6557 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6559 src_surface = surface_convert_format(src_surface, dst_format->id);
6562 /* The conv function writes a FIXME */
6563 WARN("Cannot convert source surface format to dest format.\n");
6567 wined3d_surface_map(src_surface, &slock, NULL, WINED3DLOCK_READONLY);
6568 src_format = src_surface->resource.format;
6572 src_format = dst_format;
6575 wined3d_surface_map(dst_surface, &dlock, &xdst, 0);
6577 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6580 bpp = dst_surface->resource.format->byte_count;
6581 srcheight = xsrc.bottom - xsrc.top;
6582 srcwidth = xsrc.right - xsrc.left;
6583 dstheight = xdst.bottom - xdst.top;
6584 dstwidth = xdst.right - xdst.left;
6585 width = (xdst.right - xdst.left) * bpp;
6587 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_COMPRESSED)
6589 UINT row_block_count;
6591 if (flags || src_surface == dst_surface)
6593 FIXME("Only plain blits supported on compressed surfaces.\n");
6598 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6600 if (srcheight != dstheight || srcwidth != dstwidth)
6602 WARN("Stretching not supported on compressed surfaces.\n");
6603 hr = WINED3DERR_INVALIDCALL;
6610 row_block_count = (dstwidth + dst_format->block_width - 1) / dst_format->block_width;
6611 for (y = 0; y < dstheight; y += dst_format->block_height)
6613 memcpy(dbuf, sbuf, row_block_count * dst_format->block_byte_count);
6614 dbuf += dlock.Pitch;
6615 sbuf += slock.Pitch;
6621 if (dst_rect && src_surface != dst_surface)
6624 dbuf = (BYTE*)dlock.pBits+(xdst.top*dlock.Pitch)+(xdst.left*bpp);
6626 /* First, all the 'source-less' blits */
6627 if (flags & WINEDDBLT_COLORFILL)
6629 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dlock.Pitch, fx->u5.dwFillColor);
6630 flags &= ~WINEDDBLT_COLORFILL;
6633 if (flags & WINEDDBLT_DEPTHFILL)
6635 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6637 if (flags & WINEDDBLT_ROP)
6639 /* Catch some degenerate cases here. */
6643 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,0);
6645 case 0xAA0029: /* No-op */
6648 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,~0);
6650 case SRCCOPY: /* Well, we do that below? */
6653 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6656 flags &= ~WINEDDBLT_ROP;
6658 if (flags & WINEDDBLT_DDROPS)
6660 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6662 /* Now the 'with source' blits. */
6666 int sx, xinc, sy, yinc;
6668 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6671 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6672 && (srcwidth != dstwidth || srcheight != dstheight))
6674 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6675 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6678 sbase = (BYTE*)slock.pBits+(xsrc.top*slock.Pitch)+xsrc.left*bpp;
6679 xinc = (srcwidth << 16) / dstwidth;
6680 yinc = (srcheight << 16) / dstheight;
6684 /* No effects, we can cheat here. */
6685 if (dstwidth == srcwidth)
6687 if (dstheight == srcheight)
6689 /* No stretching in either direction. This needs to be as
6690 * fast as possible. */
6693 /* Check for overlapping surfaces. */
6694 if (src_surface != dst_surface || xdst.top < xsrc.top
6695 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6697 /* No overlap, or dst above src, so copy from top downwards. */
6698 for (y = 0; y < dstheight; ++y)
6700 memcpy(dbuf, sbuf, width);
6701 sbuf += slock.Pitch;
6702 dbuf += dlock.Pitch;
6705 else if (xdst.top > xsrc.top)
6707 /* Copy from bottom upwards. */
6708 sbuf += (slock.Pitch*dstheight);
6709 dbuf += (dlock.Pitch*dstheight);
6710 for (y = 0; y < dstheight; ++y)
6712 sbuf -= slock.Pitch;
6713 dbuf -= dlock.Pitch;
6714 memcpy(dbuf, sbuf, width);
6719 /* Src and dst overlapping on the same line, use memmove. */
6720 for (y = 0; y < dstheight; ++y)
6722 memmove(dbuf, sbuf, width);
6723 sbuf += slock.Pitch;
6724 dbuf += dlock.Pitch;
6730 /* Stretching in y direction only. */
6731 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6733 sbuf = sbase + (sy >> 16) * slock.Pitch;
6734 memcpy(dbuf, sbuf, width);
6735 dbuf += dlock.Pitch;
6741 /* Stretching in X direction. */
6743 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6745 sbuf = sbase + (sy >> 16) * slock.Pitch;
6747 if ((sy >> 16) == (last_sy >> 16))
6749 /* This source row is the same as last source row -
6750 * Copy the already stretched row. */
6751 memcpy(dbuf, dbuf - dlock.Pitch, width);
6755 #define STRETCH_ROW(type) \
6757 const type *s = (const type *)sbuf; \
6758 type *d = (type *)dbuf; \
6759 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6760 d[x] = s[sx >> 16]; \
6778 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6782 s = sbuf + 3 * (sx >> 16);
6783 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6784 d[0] = (pixel ) & 0xff;
6785 d[1] = (pixel >> 8) & 0xff;
6786 d[2] = (pixel >> 16) & 0xff;
6792 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6793 hr = WINED3DERR_NOTAVAILABLE;
6798 dbuf += dlock.Pitch;
6805 LONG dstyinc = dlock.Pitch, dstxinc = bpp;
6806 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6807 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6808 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6810 /* The color keying flags are checked for correctness in ddraw */
6811 if (flags & WINEDDBLT_KEYSRC)
6813 keylow = src_surface->SrcBltCKey.dwColorSpaceLowValue;
6814 keyhigh = src_surface->SrcBltCKey.dwColorSpaceHighValue;
6816 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6818 keylow = fx->ddckSrcColorkey.dwColorSpaceLowValue;
6819 keyhigh = fx->ddckSrcColorkey.dwColorSpaceHighValue;
6822 if (flags & WINEDDBLT_KEYDEST)
6824 /* Destination color keys are taken from the source surface! */
6825 destkeylow = src_surface->DestBltCKey.dwColorSpaceLowValue;
6826 destkeyhigh = src_surface->DestBltCKey.dwColorSpaceHighValue;
6828 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6830 destkeylow = fx->ddckDestColorkey.dwColorSpaceLowValue;
6831 destkeyhigh = fx->ddckDestColorkey.dwColorSpaceHighValue;
6840 keymask = src_format->red_mask
6841 | src_format->green_mask
6842 | src_format->blue_mask;
6844 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6847 if (flags & WINEDDBLT_DDFX)
6849 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6852 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6853 dBottomLeft = dTopLeft + ((dstheight - 1) * dlock.Pitch);
6854 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6856 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6858 /* I don't think we need to do anything about this flag */
6859 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6861 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6864 dTopRight = dTopLeft;
6867 dBottomRight = dBottomLeft;
6869 dstxinc = dstxinc * -1;
6871 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6874 dTopLeft = dBottomLeft;
6877 dTopRight = dBottomRight;
6879 dstyinc = dstyinc * -1;
6881 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6883 /* I don't think we need to do anything about this flag */
6884 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6886 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6889 dBottomRight = dTopLeft;
6892 dBottomLeft = dTopRight;
6894 dstxinc = dstxinc * -1;
6895 dstyinc = dstyinc * -1;
6897 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6900 dTopLeft = dBottomLeft;
6901 dBottomLeft = dBottomRight;
6902 dBottomRight = dTopRight;
6907 dstxinc = dstxinc * -1;
6909 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6912 dTopLeft = dTopRight;
6913 dTopRight = dBottomRight;
6914 dBottomRight = dBottomLeft;
6919 dstyinc = dstyinc * -1;
6921 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6923 /* I don't think we need to do anything about this flag */
6924 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6927 flags &= ~(WINEDDBLT_DDFX);
6930 #define COPY_COLORKEY_FX(type) \
6933 type *d = (type *)dbuf, *dx, tmp; \
6934 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6936 s = (const type *)(sbase + (sy >> 16) * slock.Pitch); \
6938 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6940 tmp = s[sx >> 16]; \
6941 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6942 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6946 dx = (type *)(((BYTE *)dx) + dstxinc); \
6948 d = (type *)(((BYTE *)d) + dstyinc); \
6955 COPY_COLORKEY_FX(BYTE);
6958 COPY_COLORKEY_FX(WORD);
6961 COPY_COLORKEY_FX(DWORD);
6966 BYTE *d = dbuf, *dx;
6967 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6969 sbuf = sbase + (sy >> 16) * slock.Pitch;
6971 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
6973 DWORD pixel, dpixel = 0;
6974 s = sbuf + 3 * (sx>>16);
6975 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6976 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
6977 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
6978 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
6980 dx[0] = (pixel ) & 0xff;
6981 dx[1] = (pixel >> 8) & 0xff;
6982 dx[2] = (pixel >> 16) & 0xff;
6991 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
6992 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
6993 hr = WINED3DERR_NOTAVAILABLE;
6995 #undef COPY_COLORKEY_FX
7001 if (flags && FIXME_ON(d3d_surface))
7003 FIXME("\tUnsupported flags: %#x.\n", flags);
7007 wined3d_surface_unmap(dst_surface);
7008 if (src_surface && src_surface != dst_surface)
7009 wined3d_surface_unmap(src_surface);
7010 /* Release the converted surface, if any. */
7011 if (src_surface && src_surface != orig_src)
7012 wined3d_surface_decref(src_surface);
7017 /* Do not call while under the GL lock. */
7018 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7019 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
7021 static const RECT src_rect;
7024 memset(&BltFx, 0, sizeof(BltFx));
7025 BltFx.dwSize = sizeof(BltFx);
7026 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7027 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7028 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7031 /* Do not call while under the GL lock. */
7032 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7033 struct wined3d_surface *surface, const RECT *rect, float depth)
7035 FIXME("Depth filling not implemented by cpu_blit.\n");
7036 return WINED3DERR_INVALIDCALL;
7039 const struct blit_shader cpu_blit = {
7045 cpu_blit_color_fill,
7046 cpu_blit_depth_fill,
7049 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7050 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
7051 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7052 WINED3DPOOL pool, void *parent, const struct wined3d_parent_ops *parent_ops)
7054 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7055 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7056 unsigned int resource_size;
7059 if (multisample_quality > 0)
7061 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7062 multisample_quality = 0;
7065 /* Quick lockable sanity check.
7066 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7067 * this function is too deep to need to care about things like this.
7068 * Levels need to be checked too, since they all affect what can be done. */
7071 case WINED3DPOOL_SCRATCH:
7074 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7075 "which are mutually exclusive, setting lockable to TRUE.\n");
7080 case WINED3DPOOL_SYSTEMMEM:
7082 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7085 case WINED3DPOOL_MANAGED:
7086 if (usage & WINED3DUSAGE_DYNAMIC)
7087 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7090 case WINED3DPOOL_DEFAULT:
7091 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7092 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7096 FIXME("Unknown pool %#x.\n", pool);
7100 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7101 FIXME("Trying to create a render target that isn't in the default pool.\n");
7103 /* FIXME: Check that the format is supported by the device. */
7105 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7107 return WINED3DERR_INVALIDCALL;
7109 surface->surface_type = surface_type;
7111 switch (surface_type)
7113 case SURFACE_OPENGL:
7114 surface->surface_ops = &surface_ops;
7118 surface->surface_ops = &gdi_surface_ops;
7122 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7123 return WINED3DERR_INVALIDCALL;
7126 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7127 multisample_type, multisample_quality, usage, pool, width, height, 1,
7128 resource_size, parent, parent_ops, &surface_resource_ops);
7131 WARN("Failed to initialize resource, returning %#x.\n", hr);
7135 /* "Standalone" surface. */
7136 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7138 surface->texture_level = level;
7139 list_init(&surface->overlays);
7142 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7144 surface->flags |= SFLAG_DISCARD;
7145 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7146 surface->flags |= SFLAG_LOCKABLE;
7147 /* I'm not sure if this qualifies as a hack or as an optimization. It
7148 * seems reasonable to assume that lockable render targets will get
7149 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7150 * creation. However, the other reason we want to do this is that several
7151 * ddraw applications access surface memory while the surface isn't
7152 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7153 * future locks prevents these from crashing. */
7154 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7155 surface->flags |= SFLAG_DYNLOCK;
7157 /* Mark the texture as dirty so that it gets loaded first time around. */
7158 surface_add_dirty_rect(surface, NULL);
7159 list_init(&surface->renderbuffers);
7161 TRACE("surface %p, memory %p, size %u\n",
7162 surface, surface->resource.allocatedMemory, surface->resource.size);
7164 /* Call the private setup routine */
7165 hr = surface->surface_ops->surface_private_setup(surface);
7168 ERR("Private setup failed, returning %#x\n", hr);
7169 surface->surface_ops->surface_cleanup(surface);
7176 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7177 enum wined3d_format_id format_id, BOOL lockable, BOOL discard, UINT level, DWORD usage, WINED3DPOOL pool,
7178 WINED3DMULTISAMPLE_TYPE multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7179 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7181 struct wined3d_surface *object;
7184 TRACE("device %p, width %u, height %u, format %s, lockable %#x, discard %#x, level %u\n",
7185 device, width, height, debug_d3dformat(format_id), lockable, discard, level);
7186 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7187 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7188 TRACE("surface_type %#x, parent %p, parent_ops %p.\n", surface_type, parent, parent_ops);
7190 if (surface_type == SURFACE_OPENGL && !device->adapter)
7192 ERR("OpenGL surfaces are not available without OpenGL.\n");
7193 return WINED3DERR_NOTAVAILABLE;
7196 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7199 ERR("Failed to allocate surface memory.\n");
7200 return WINED3DERR_OUTOFVIDEOMEMORY;
7203 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level, lockable,
7204 discard, multisample_type, multisample_quality, device, usage, format_id, pool, parent, parent_ops);
7207 WARN("Failed to initialize surface, returning %#x.\n", hr);
7208 HeapFree(GetProcessHeap(), 0, object);
7212 TRACE("Created surface %p.\n", object);