wined3d: Reuse convert_a8r8g8b8_x8r8g8b8() for B8G8R8X8 -> B8G8R8A8 conversions.
[wine.git] / dlls / wined3d / surface.c
blob88e8a1dbd0b4a18f85c612b534b26ad6fb4b7180
1 /*
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
29 #include "config.h"
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;
56 ENTER_GL();
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);
77 LEAVE_GL();
79 context_release(context);
82 if (surface->flags & SFLAG_DIBSECTION)
84 /* Release the DC. */
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;
114 else
116 switch (wined3d_settings.offscreen_rendering_mode)
118 case ORM_FBO:
119 surface->get_drawable_size = get_drawable_size_fbo;
120 break;
122 case ORM_BACKBUFFER:
123 surface->get_drawable_size = get_drawable_size_backbuffer;
124 break;
126 default:
127 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
128 return;
132 surface->container.type = type;
133 surface->container.u.base = container;
136 struct blt_info
138 GLenum binding;
139 GLenum bind_target;
140 enum tex_types tex_type;
141 GLfloat coords[4][3];
144 struct float_rect
146 float l;
147 float t;
148 float r;
149 float b;
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;
163 struct float_rect f;
165 switch (target)
167 default:
168 FIXME("Unsupported texture target %#x\n", target);
169 /* Fall back to GL_TEXTURE_2D */
170 case 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;
176 coords[0][2] = 0.0f;
178 coords[1][0] = (float)rect->right / w;
179 coords[1][1] = (float)rect->top / h;
180 coords[1][2] = 0.0f;
182 coords[2][0] = (float)rect->left / w;
183 coords[2][1] = (float)rect->bottom / h;
184 coords[2][2] = 0.0f;
186 coords[3][0] = (float)rect->right / w;
187 coords[3][1] = (float)rect->bottom / h;
188 coords[3][2] = 0.0f;
189 break;
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;
199 break;
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;
211 break;
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;
223 break;
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;
235 break;
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;
247 break;
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;
259 break;
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;
271 break;
275 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
277 if (rect_in)
278 *rect_out = *rect_in;
279 else
281 rect_out->left = 0;
282 rect_out->top = 0;
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");
315 /* Draw a quad */
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);
328 glEnd();
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;
348 SYSTEM_INFO sysInfo;
349 BITMAPINFO *b_info;
350 int extraline = 0;
351 DWORD *masks;
352 UINT usage;
353 HDC dc;
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)
365 case 2:
366 case 4:
367 /* Allocate extra space to store the RGB bit masks. */
368 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
369 break;
371 case 3:
372 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
373 break;
375 default:
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)));
379 break;
382 if (!b_info)
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)
393 extraline = 1;
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;
418 break;
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:
432 usage = 0;
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;
437 break;
439 default:
440 /* Don't know palette */
441 b_info->bmiHeader.biCompression = BI_RGB;
442 usage = 0;
443 break;
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);
456 ReleaseDC(0, dc);
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));
472 else
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;
492 return WINED3D_OK;
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
517 * surfaces. */
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;
523 GLenum error;
525 context = context_acquire(device, NULL);
526 ENTER_GL();
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;
553 LEAVE_GL();
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)
574 return;
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
601 * glGet(). */
603 ENTER_GL();
604 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
605 LEAVE_GL();
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);
622 ENTER_GL();
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);
637 LEAVE_GL();
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;
663 else
665 /* Find the nearest pow2 match */
666 pow2Width = pow2Height = 1;
667 while (pow2Width < surface->resource.width)
668 pow2Width <<= 1;
669 while (pow2Height < surface->resource.height)
670 pow2Height <<= 1;
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
698 * potentially slow)
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);
715 else
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)
736 case ORM_FBO:
737 surface->get_drawable_size = get_drawable_size_fbo;
738 break;
740 case ORM_BACKBUFFER:
741 surface->get_drawable_size = get_drawable_size_backbuffer;
742 break;
744 default:
745 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
746 return WINED3DERR_INVALIDCALL;
749 surface->flags |= SFLAG_INSYSMEM;
751 return WINED3D_OK;
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);
775 else
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)
788 RGBQUAD col[256];
789 unsigned int i;
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)
810 HRESULT hr;
812 /* If there's no destination surface there is nothing to do. */
813 if (!surface->overlay_dest)
814 return WINED3D_OK;
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)
819 return WINED3D_OK;
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;
826 return hr;
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);
850 else
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)
857 pass_rect = NULL;
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;
873 ENTER_GL();
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
886 * calls. */
887 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
888 checkGLcall("glBindBufferARB");
890 LEAVE_GL();
891 context_release(context);
894 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
896 if (!rect)
897 surface_add_dirty_rect(surface, NULL);
898 else
900 WINED3DBOX b;
902 b.Left = rect->left;
903 b.Top = rect->top;
904 b.Right = rect->right;
905 b.Bottom = rect->bottom;
906 b.Front = 0;
907 b.Back = 1;
908 surface_add_dirty_rect(surface, &b);
913 static void surface_unmap(struct wined3d_surface *surface)
915 struct wined3d_device *device = surface->resource.device;
916 BOOL fullsurface;
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;
932 ENTER_GL();
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");
937 LEAVE_GL();
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");
948 goto done;
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;
957 if (!warned)
959 ERR("The application tries to write to the render target, but render target locking is disabled.\n");
960 warned = TRUE;
962 goto done;
965 if (!surface->dirtyRect.left && !surface->dirtyRect.top
966 && surface->dirtyRect.right == surface->resource.width
967 && surface->dirtyRect.bottom == surface->resource.height)
969 fullsurface = TRUE;
971 else
973 /* TODO: Proper partial rectangle tracking. */
974 fullsurface = FALSE;
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(). */
987 if (!fullsurface)
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");
1003 done:
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;
1012 HRESULT hr;
1014 TRACE("surface %p.\n", surface);
1016 /* Create a DIB section if there isn't a dc yet. */
1017 if (!surface->hDC)
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);
1025 if (FAILED(hr))
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);
1035 if (FAILED(hr))
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);
1043 return hr;
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);
1064 else
1065 return WINED3D_OK;
1068 return WINED3D_OK;
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)
1074 return FALSE;
1075 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1076 return FALSE;
1077 return TRUE;
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;
1086 GLbitfield gl_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));
1100 return;
1103 if (!src_mask)
1105 ERR("Not a depth / stencil format: %s.\n",
1106 debug_d3dformat(src_surface->resource.format->id));
1107 return;
1110 gl_mask = 0;
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");
1127 return;
1130 gl_info = context->gl_info;
1132 ENTER_GL();
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));
1155 glStencilMask(~0U);
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()");
1166 LEAVE_GL();
1168 if (wined3d_settings.strict_draw_ordering)
1169 wglFlush(); /* Flush to ensure ordering across contexts. */
1171 context_release(context);
1174 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1175 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
1176 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
1178 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1179 return FALSE;
1181 /* Source and/or destination need to be on the GL side */
1182 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
1183 return FALSE;
1185 switch (blit_op)
1187 case WINED3D_BLIT_OP_COLOR_BLIT:
1188 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1189 return FALSE;
1190 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1191 return FALSE;
1192 break;
1194 case WINED3D_BLIT_OP_DEPTH_BLIT:
1195 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1196 return FALSE;
1197 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1198 return FALSE;
1199 break;
1201 default:
1202 return FALSE;
1205 if (!(src_format->id == dst_format->id
1206 || (is_identity_fixup(src_format->color_fixup)
1207 && is_identity_fixup(dst_format->color_fixup))))
1208 return FALSE;
1210 return TRUE;
1213 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1215 const struct wined3d_format *format = surface->resource.format;
1217 switch (format->id)
1219 case WINED3DFMT_S1_UINT_D15_UNORM:
1220 *float_depth = depth / (float)0x00007fff;
1221 break;
1223 case WINED3DFMT_D16_UNORM:
1224 *float_depth = depth / (float)0x0000ffff;
1225 break;
1227 case WINED3DFMT_D24_UNORM_S8_UINT:
1228 case WINED3DFMT_X8D24_UNORM:
1229 *float_depth = depth / (float)0x00ffffff;
1230 break;
1232 case WINED3DFMT_D32_UNORM:
1233 *float_depth = depth / (float)0xffffffff;
1234 break;
1236 default:
1237 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1238 return FALSE;
1241 return TRUE;
1244 /* Do not call while under the GL lock. */
1245 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1247 const struct wined3d_resource *resource = &surface->resource;
1248 struct wined3d_device *device = resource->device;
1249 const struct blit_shader *blitter;
1251 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1252 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1253 if (!blitter)
1255 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1256 return WINED3DERR_INVALIDCALL;
1259 return blitter->depth_fill(device, surface, rect, depth);
1262 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1263 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1265 struct wined3d_device *device = src_surface->resource.device;
1267 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1268 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1269 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1270 return WINED3DERR_INVALIDCALL;
1272 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1274 surface_modify_ds_location(dst_surface, SFLAG_DS_OFFSCREEN,
1275 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1276 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
1278 return WINED3D_OK;
1281 /* Do not call while under the GL lock. */
1282 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1283 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1284 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1286 const struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1287 struct wined3d_device *device = dst_surface->resource.device;
1288 DWORD src_ds_flags, dst_ds_flags;
1289 RECT src_rect, dst_rect;
1291 static const DWORD simple_blit = WINEDDBLT_ASYNC
1292 | WINEDDBLT_COLORFILL
1293 | WINEDDBLT_WAIT
1294 | WINEDDBLT_DEPTHFILL
1295 | WINEDDBLT_DONOTWAIT;
1297 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1298 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1299 flags, fx, debug_d3dtexturefiltertype(filter));
1300 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1302 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1304 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1305 return WINEDDERR_SURFACEBUSY;
1308 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1310 /* The destination rect can be out of bounds on the condition
1311 * that a clipper is set for the surface. */
1312 if (!dst_surface->clipper && (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1313 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1314 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1315 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1316 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0))
1318 WARN("Application gave us bad destination rectangle for blit without a clipper set.\n");
1319 return WINEDDERR_INVALIDRECT;
1322 if (src_surface)
1324 surface_get_rect(src_surface, src_rect_in, &src_rect);
1326 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1327 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1328 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1329 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1330 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1332 WARN("Application gave us bad source rectangle for Blt.\n");
1333 return WINEDDERR_INVALIDRECT;
1336 else
1338 memset(&src_rect, 0, sizeof(src_rect));
1341 if (!fx || !(fx->dwDDFX))
1342 flags &= ~WINEDDBLT_DDFX;
1344 if (flags & WINEDDBLT_WAIT)
1345 flags &= ~WINEDDBLT_WAIT;
1347 if (flags & WINEDDBLT_ASYNC)
1349 static unsigned int once;
1351 if (!once++)
1352 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1353 flags &= ~WINEDDBLT_ASYNC;
1356 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1357 if (flags & WINEDDBLT_DONOTWAIT)
1359 static unsigned int once;
1361 if (!once++)
1362 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1363 flags &= ~WINEDDBLT_DONOTWAIT;
1366 if (!device->d3d_initialized)
1368 WARN("D3D not initialized, using fallback.\n");
1369 goto cpu;
1372 if (flags & ~simple_blit)
1374 WARN("Using fallback for complex blit (%#x).\n", flags);
1375 goto fallback;
1378 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1379 src_swapchain = src_surface->container.u.swapchain;
1380 else
1381 src_swapchain = NULL;
1383 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1384 dst_swapchain = dst_surface->container.u.swapchain;
1385 else
1386 dst_swapchain = NULL;
1388 /* This isn't strictly needed. FBO blits for example could deal with
1389 * cross-swapchain blits by first downloading the source to a texture
1390 * before switching to the destination context. We just have this here to
1391 * not have to deal with the issue, since cross-swapchain blits should be
1392 * rare. */
1393 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1395 FIXME("Using fallback for cross-swapchain blit.\n");
1396 goto fallback;
1399 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1400 if (src_surface)
1401 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1402 else
1403 src_ds_flags = 0;
1405 if (src_ds_flags || dst_ds_flags)
1407 if (flags & WINEDDBLT_DEPTHFILL)
1409 float depth;
1411 TRACE("Depth fill.\n");
1413 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1414 return WINED3DERR_INVALIDCALL;
1416 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1417 return WINED3D_OK;
1419 else
1421 /* Accessing depth / stencil surfaces is supposed to fail while in
1422 * a scene, except for fills, which seem to work. */
1423 if (device->inScene)
1425 WARN("Rejecting depth / stencil access while in scene.\n");
1426 return WINED3DERR_INVALIDCALL;
1429 if (src_ds_flags != dst_ds_flags)
1431 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1432 return WINED3DERR_INVALIDCALL;
1435 if (src_rect.top || src_rect.left
1436 || src_rect.bottom != src_surface->resource.height
1437 || src_rect.right != src_surface->resource.width)
1439 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1440 wine_dbgstr_rect(&src_rect));
1441 return WINED3DERR_INVALIDCALL;
1444 if (dst_rect.top || dst_rect.left
1445 || dst_rect.bottom != dst_surface->resource.height
1446 || dst_rect.right != dst_surface->resource.width)
1448 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1449 wine_dbgstr_rect(&src_rect));
1450 return WINED3DERR_INVALIDCALL;
1453 if (src_surface->resource.height != dst_surface->resource.height
1454 || src_surface->resource.width != dst_surface->resource.width)
1456 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1457 return WINED3DERR_INVALIDCALL;
1460 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1461 return WINED3D_OK;
1465 fallback:
1467 /* Special cases for render targets. */
1468 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1469 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1471 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1472 src_surface, &src_rect, flags, fx, filter)))
1473 return WINED3D_OK;
1476 cpu:
1478 /* For the rest call the X11 surface implementation. For render targets
1479 * this should be implemented OpenGL accelerated in BltOverride, other
1480 * blits are rather rare. */
1481 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1484 /* Do not call while under the GL lock. */
1485 HRESULT CDECL wined3d_surface_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
1486 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD trans)
1488 RECT src_rect, dst_rect;
1489 DWORD flags = 0;
1491 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect_in %s, trans %#x.\n",
1492 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect_in), trans);
1494 surface_get_rect(src_surface, src_rect_in, &src_rect);
1496 dst_rect.left = dst_x;
1497 dst_rect.top = dst_y;
1498 dst_rect.right = dst_x + src_rect.right - src_rect.left;
1499 dst_rect.bottom = dst_y + src_rect.bottom - src_rect.top;
1501 if (trans & WINEDDBLTFAST_SRCCOLORKEY)
1502 flags |= WINEDDBLT_KEYSRC;
1503 if (trans & WINEDDBLTFAST_DESTCOLORKEY)
1504 flags |= WINEDDBLT_KEYDEST;
1505 if (trans & WINEDDBLTFAST_WAIT)
1506 flags |= WINEDDBLT_WAIT;
1507 if (trans & WINEDDBLTFAST_DONOTWAIT)
1508 flags |= WINEDDBLT_DONOTWAIT;
1510 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, NULL, WINED3DTEXF_POINT);
1513 static HRESULT surface_set_mem(struct wined3d_surface *surface, void *mem)
1515 TRACE("surface %p, mem %p.\n", surface, mem);
1517 if (mem && mem != surface->resource.allocatedMemory)
1519 void *release = NULL;
1521 /* Do I have to copy the old surface content? */
1522 if (surface->flags & SFLAG_DIBSECTION)
1524 SelectObject(surface->hDC, surface->dib.holdbitmap);
1525 DeleteDC(surface->hDC);
1526 /* Release the DIB section. */
1527 DeleteObject(surface->dib.DIBsection);
1528 surface->dib.bitmap_data = NULL;
1529 surface->resource.allocatedMemory = NULL;
1530 surface->hDC = NULL;
1531 surface->flags &= ~SFLAG_DIBSECTION;
1533 else if (!(surface->flags & SFLAG_USERPTR))
1535 release = surface->resource.heapMemory;
1536 surface->resource.heapMemory = NULL;
1538 surface->resource.allocatedMemory = mem;
1539 surface->flags |= SFLAG_USERPTR;
1541 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
1542 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1544 /* For client textures OpenGL has to be notified. */
1545 if (surface->flags & SFLAG_CLIENT)
1546 surface_release_client_storage(surface);
1548 /* Now free the old memory if any. */
1549 HeapFree(GetProcessHeap(), 0, release);
1551 else if (surface->flags & SFLAG_USERPTR)
1553 /* Map and GetDC will re-create the dib section and allocated memory. */
1554 surface->resource.allocatedMemory = NULL;
1555 /* HeapMemory should be NULL already. */
1556 if (surface->resource.heapMemory)
1557 ERR("User pointer surface has heap memory allocated.\n");
1558 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
1560 if (surface->flags & SFLAG_CLIENT)
1561 surface_release_client_storage(surface);
1563 surface_prepare_system_memory(surface);
1564 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1567 return WINED3D_OK;
1570 /* Context activation is done by the caller. */
1571 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1573 if (!surface->resource.heapMemory)
1575 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1576 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1577 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1580 ENTER_GL();
1581 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1582 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1583 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1584 surface->resource.size, surface->resource.allocatedMemory));
1585 checkGLcall("glGetBufferSubDataARB");
1586 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1587 checkGLcall("glDeleteBuffersARB");
1588 LEAVE_GL();
1590 surface->pbo = 0;
1591 surface->flags &= ~SFLAG_PBO;
1594 /* Do not call while under the GL lock. */
1595 static void surface_unload(struct wined3d_resource *resource)
1597 struct wined3d_surface *surface = surface_from_resource(resource);
1598 struct wined3d_renderbuffer_entry *entry, *entry2;
1599 struct wined3d_device *device = resource->device;
1600 const struct wined3d_gl_info *gl_info;
1601 struct wined3d_context *context;
1603 TRACE("surface %p.\n", surface);
1605 if (resource->pool == WINED3DPOOL_DEFAULT)
1607 /* Default pool resources are supposed to be destroyed before Reset is called.
1608 * Implicit resources stay however. So this means we have an implicit render target
1609 * or depth stencil. The content may be destroyed, but we still have to tear down
1610 * opengl resources, so we cannot leave early.
1612 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1613 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1614 * or the depth stencil into an FBO the texture or render buffer will be removed
1615 * and all flags get lost
1617 surface_init_sysmem(surface);
1619 else
1621 /* Load the surface into system memory */
1622 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1623 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
1625 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1626 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1627 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1629 context = context_acquire(device, NULL);
1630 gl_info = context->gl_info;
1632 /* Destroy PBOs, but load them into real sysmem before */
1633 if (surface->flags & SFLAG_PBO)
1634 surface_remove_pbo(surface, gl_info);
1636 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1637 * all application-created targets the application has to release the surface
1638 * before calling _Reset
1640 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1642 ENTER_GL();
1643 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1644 LEAVE_GL();
1645 list_remove(&entry->entry);
1646 HeapFree(GetProcessHeap(), 0, entry);
1648 list_init(&surface->renderbuffers);
1649 surface->current_renderbuffer = NULL;
1651 /* If we're in a texture, the texture name belongs to the texture.
1652 * Otherwise, destroy it. */
1653 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1655 ENTER_GL();
1656 glDeleteTextures(1, &surface->texture_name);
1657 surface->texture_name = 0;
1658 glDeleteTextures(1, &surface->texture_name_srgb);
1659 surface->texture_name_srgb = 0;
1660 LEAVE_GL();
1663 context_release(context);
1665 resource_unload(resource);
1668 static const struct wined3d_resource_ops surface_resource_ops =
1670 surface_unload,
1673 static const struct wined3d_surface_ops surface_ops =
1675 surface_private_setup,
1676 surface_cleanup,
1677 surface_realize_palette,
1678 surface_draw_overlay,
1679 surface_preload,
1680 surface_map,
1681 surface_unmap,
1682 surface_getdc,
1683 surface_flip,
1684 surface_set_mem,
1687 /*****************************************************************************
1688 * Initializes the GDI surface, aka creates the DIB section we render to
1689 * The DIB section creation is done by calling GetDC, which will create the
1690 * section and releasing the dc to allow the app to use it. The dib section
1691 * will stay until the surface is released
1693 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1694 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1695 * avoid confusion in the shared surface code.
1697 * Returns:
1698 * WINED3D_OK on success
1699 * The return values of called methods on failure
1701 *****************************************************************************/
1702 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1704 HRESULT hr;
1706 TRACE("surface %p.\n", surface);
1708 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1710 ERR("Overlays not yet supported by GDI surfaces.\n");
1711 return WINED3DERR_INVALIDCALL;
1714 /* Sysmem textures have memory already allocated - release it,
1715 * this avoids an unnecessary memcpy. */
1716 hr = surface_create_dib_section(surface);
1717 if (SUCCEEDED(hr))
1719 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1720 surface->resource.heapMemory = NULL;
1721 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1724 /* We don't mind the nonpow2 stuff in GDI. */
1725 surface->pow2Width = surface->resource.width;
1726 surface->pow2Height = surface->resource.height;
1728 return WINED3D_OK;
1731 static void surface_gdi_cleanup(struct wined3d_surface *surface)
1733 TRACE("surface %p.\n", surface);
1735 if (surface->flags & SFLAG_DIBSECTION)
1737 /* Release the DC. */
1738 SelectObject(surface->hDC, surface->dib.holdbitmap);
1739 DeleteDC(surface->hDC);
1740 /* Release the DIB section. */
1741 DeleteObject(surface->dib.DIBsection);
1742 surface->dib.bitmap_data = NULL;
1743 surface->resource.allocatedMemory = NULL;
1746 if (surface->flags & SFLAG_USERPTR)
1747 wined3d_surface_set_mem(surface, NULL);
1748 if (surface->overlay_dest)
1749 list_remove(&surface->overlay_entry);
1751 HeapFree(GetProcessHeap(), 0, surface->palette9);
1753 resource_cleanup(&surface->resource);
1756 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1758 struct wined3d_palette *palette = surface->palette;
1760 TRACE("surface %p.\n", surface);
1762 if (!palette) return;
1764 if (surface->flags & SFLAG_DIBSECTION)
1766 RGBQUAD col[256];
1767 unsigned int i;
1769 TRACE("Updating the DC's palette.\n");
1771 for (i = 0; i < 256; ++i)
1773 col[i].rgbRed = palette->palents[i].peRed;
1774 col[i].rgbGreen = palette->palents[i].peGreen;
1775 col[i].rgbBlue = palette->palents[i].peBlue;
1776 col[i].rgbReserved = 0;
1778 SetDIBColorTable(surface->hDC, 0, 256, col);
1781 /* Update the image because of the palette change. Some games like e.g.
1782 * Red Alert call SetEntries a lot to implement fading. */
1783 /* Tell the swapchain to update the screen. */
1784 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1786 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1787 if (surface == swapchain->front_buffer)
1789 x11_copy_to_screen(swapchain, NULL);
1794 static HRESULT gdi_surface_draw_overlay(struct wined3d_surface *surface)
1796 FIXME("GDI surfaces can't draw overlays yet.\n");
1797 return E_FAIL;
1800 static void gdi_surface_preload(struct wined3d_surface *surface)
1802 TRACE("surface %p.\n", surface);
1804 ERR("Preloading GDI surfaces is not supported.\n");
1807 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
1809 TRACE("surface %p, rect %s, flags %#x.\n",
1810 surface, wine_dbgstr_rect(rect), flags);
1812 if (!surface->resource.allocatedMemory)
1814 /* This happens on gdi surfaces if the application set a user pointer
1815 * and resets it. Recreate the DIB section. */
1816 surface_create_dib_section(surface);
1817 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1821 static void gdi_surface_unmap(struct wined3d_surface *surface)
1823 TRACE("surface %p.\n", surface);
1825 /* Tell the swapchain to update the screen. */
1826 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1828 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1829 if (surface == swapchain->front_buffer)
1831 x11_copy_to_screen(swapchain, &surface->lockedRect);
1835 memset(&surface->lockedRect, 0, sizeof(RECT));
1838 static HRESULT gdi_surface_getdc(struct wined3d_surface *surface)
1840 WINED3DLOCKED_RECT lock;
1841 HRESULT hr;
1843 TRACE("surface %p.\n", surface);
1845 /* Should have a DIB section already. */
1846 if (!(surface->flags & SFLAG_DIBSECTION))
1848 WARN("DC not supported on this surface\n");
1849 return WINED3DERR_INVALIDCALL;
1852 /* Map the surface. */
1853 hr = wined3d_surface_map(surface, &lock, NULL, 0);
1854 if (FAILED(hr))
1855 ERR("Map failed, hr %#x.\n", hr);
1857 return hr;
1860 static HRESULT gdi_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override)
1862 TRACE("surface %p, override %p.\n", surface, override);
1864 return WINED3D_OK;
1867 static HRESULT gdi_surface_set_mem(struct wined3d_surface *surface, void *mem)
1869 TRACE("surface %p, mem %p.\n", surface, mem);
1871 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
1872 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1874 ERR("Not supported on render targets.\n");
1875 return WINED3DERR_INVALIDCALL;
1878 if (mem && mem != surface->resource.allocatedMemory)
1880 void *release = NULL;
1882 /* Do I have to copy the old surface content? */
1883 if (surface->flags & SFLAG_DIBSECTION)
1885 SelectObject(surface->hDC, surface->dib.holdbitmap);
1886 DeleteDC(surface->hDC);
1887 /* Release the DIB section. */
1888 DeleteObject(surface->dib.DIBsection);
1889 surface->dib.bitmap_data = NULL;
1890 surface->resource.allocatedMemory = NULL;
1891 surface->hDC = NULL;
1892 surface->flags &= ~SFLAG_DIBSECTION;
1894 else if (!(surface->flags & SFLAG_USERPTR))
1896 release = surface->resource.allocatedMemory;
1898 surface->resource.allocatedMemory = mem;
1899 surface->flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
1901 /* Now free the old memory, if any. */
1902 HeapFree(GetProcessHeap(), 0, release);
1904 else if (surface->flags & SFLAG_USERPTR)
1906 /* Map() and GetDC() will re-create the dib section and allocated memory. */
1907 surface->resource.allocatedMemory = NULL;
1908 surface->flags &= ~SFLAG_USERPTR;
1911 return WINED3D_OK;
1914 static const struct wined3d_surface_ops gdi_surface_ops =
1916 gdi_surface_private_setup,
1917 surface_gdi_cleanup,
1918 gdi_surface_realize_palette,
1919 gdi_surface_draw_overlay,
1920 gdi_surface_preload,
1921 gdi_surface_map,
1922 gdi_surface_unmap,
1923 gdi_surface_getdc,
1924 gdi_surface_flip,
1925 gdi_surface_set_mem,
1928 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
1930 GLuint *name;
1931 DWORD flag;
1933 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
1935 if(srgb)
1937 name = &surface->texture_name_srgb;
1938 flag = SFLAG_INSRGBTEX;
1940 else
1942 name = &surface->texture_name;
1943 flag = SFLAG_INTEXTURE;
1946 if (!*name && new_name)
1948 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
1949 * surface has no texture name yet. See if we can get rid of this. */
1950 if (surface->flags & flag)
1951 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
1952 surface_modify_location(surface, flag, FALSE);
1955 *name = new_name;
1956 surface_force_reload(surface);
1959 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
1961 TRACE("surface %p, target %#x.\n", surface, target);
1963 if (surface->texture_target != target)
1965 if (target == GL_TEXTURE_RECTANGLE_ARB)
1967 surface->flags &= ~SFLAG_NORMCOORD;
1969 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
1971 surface->flags |= SFLAG_NORMCOORD;
1974 surface->texture_target = target;
1975 surface_force_reload(surface);
1978 /* Context activation is done by the caller. */
1979 void surface_bind(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
1981 TRACE("surface %p, gl_info %p, srgb %#x.\n", surface, gl_info, srgb);
1983 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
1985 struct wined3d_texture *texture = surface->container.u.texture;
1987 TRACE("Passing to container (%p).\n", texture);
1988 texture->texture_ops->texture_bind(texture, gl_info, srgb);
1990 else
1992 if (surface->texture_level)
1994 ERR("Standalone surface %p is non-zero texture level %u.\n",
1995 surface, surface->texture_level);
1998 if (srgb)
1999 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2001 ENTER_GL();
2003 if (!surface->texture_name)
2005 glGenTextures(1, &surface->texture_name);
2006 checkGLcall("glGenTextures");
2008 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2010 glBindTexture(surface->texture_target, surface->texture_name);
2011 checkGLcall("glBindTexture");
2012 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2013 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2014 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2015 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2016 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2017 checkGLcall("glTexParameteri");
2019 else
2021 glBindTexture(surface->texture_target, surface->texture_name);
2022 checkGLcall("glBindTexture");
2025 LEAVE_GL();
2029 /* This function checks if the primary render target uses the 8bit paletted format. */
2030 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
2032 if (device->fb.render_targets && device->fb.render_targets[0])
2034 const struct wined3d_surface *render_target = device->fb.render_targets[0];
2035 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
2036 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
2037 return TRUE;
2039 return FALSE;
2042 /* This call just downloads data, the caller is responsible for binding the
2043 * correct texture. */
2044 /* Context activation is done by the caller. */
2045 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2047 const struct wined3d_format *format = surface->resource.format;
2049 /* Only support read back of converted P8 surfaces. */
2050 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2052 FIXME("Readback conversion not supported for format %s.\n", debug_d3dformat(format->id));
2053 return;
2056 ENTER_GL();
2058 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2060 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2061 surface, surface->texture_level, format->glFormat, format->glType,
2062 surface->resource.allocatedMemory);
2064 if (surface->flags & SFLAG_PBO)
2066 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2067 checkGLcall("glBindBufferARB");
2068 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2069 checkGLcall("glGetCompressedTexImageARB");
2070 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2071 checkGLcall("glBindBufferARB");
2073 else
2075 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2076 surface->texture_level, surface->resource.allocatedMemory));
2077 checkGLcall("glGetCompressedTexImageARB");
2080 LEAVE_GL();
2082 else
2084 void *mem;
2085 GLenum gl_format = format->glFormat;
2086 GLenum gl_type = format->glType;
2087 int src_pitch = 0;
2088 int dst_pitch = 0;
2090 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2091 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2093 gl_format = GL_ALPHA;
2094 gl_type = GL_UNSIGNED_BYTE;
2097 if (surface->flags & SFLAG_NONPOW2)
2099 unsigned char alignment = surface->resource.device->surface_alignment;
2100 src_pitch = format->byte_count * surface->pow2Width;
2101 dst_pitch = wined3d_surface_get_pitch(surface);
2102 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2103 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2105 else
2107 mem = surface->resource.allocatedMemory;
2110 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2111 surface, surface->texture_level, gl_format, gl_type, mem);
2113 if (surface->flags & SFLAG_PBO)
2115 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2116 checkGLcall("glBindBufferARB");
2118 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2119 checkGLcall("glGetTexImage");
2121 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2122 checkGLcall("glBindBufferARB");
2124 else
2126 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2127 checkGLcall("glGetTexImage");
2129 LEAVE_GL();
2131 if (surface->flags & SFLAG_NONPOW2)
2133 const BYTE *src_data;
2134 BYTE *dst_data;
2135 UINT y;
2137 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2138 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2139 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2141 * We're doing this...
2143 * instead of boxing the texture :
2144 * |<-texture width ->| -->pow2width| /\
2145 * |111111111111111111| | |
2146 * |222 Texture 222222| boxed empty | texture height
2147 * |3333 Data 33333333| | |
2148 * |444444444444444444| | \/
2149 * ----------------------------------- |
2150 * | boxed empty | boxed empty | pow2height
2151 * | | | \/
2152 * -----------------------------------
2155 * we're repacking the data to the expected texture width
2157 * |<-texture width ->| -->pow2width| /\
2158 * |111111111111111111222222222222222| |
2159 * |222333333333333333333444444444444| texture height
2160 * |444444 | |
2161 * | | \/
2162 * | | |
2163 * | empty | pow2height
2164 * | | \/
2165 * -----------------------------------
2167 * == is the same as
2169 * |<-texture width ->| /\
2170 * |111111111111111111|
2171 * |222222222222222222|texture height
2172 * |333333333333333333|
2173 * |444444444444444444| \/
2174 * --------------------
2176 * this also means that any references to allocatedMemory should work with the data as if were a
2177 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2179 * internally the texture is still stored in a boxed format so any references to textureName will
2180 * get a boxed texture with width pow2width and not a texture of width resource.width.
2182 * Performance should not be an issue, because applications normally do not lock the surfaces when
2183 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2184 * and doesn't have to be re-read. */
2185 src_data = mem;
2186 dst_data = surface->resource.allocatedMemory;
2187 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2188 for (y = 1; y < surface->resource.height; ++y)
2190 /* skip the first row */
2191 src_data += src_pitch;
2192 dst_data += dst_pitch;
2193 memcpy(dst_data, src_data, dst_pitch);
2196 HeapFree(GetProcessHeap(), 0, mem);
2200 /* Surface has now been downloaded */
2201 surface->flags |= SFLAG_INSYSMEM;
2204 /* This call just uploads data, the caller is responsible for binding the
2205 * correct texture. */
2206 /* Context activation is done by the caller. */
2207 void surface_upload_data(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2208 const struct wined3d_format *format, const RECT *src_rect, UINT src_w, const POINT *dst_point,
2209 BOOL srgb, const struct wined3d_bo_address *data)
2211 UINT update_w = src_rect->right - src_rect->left;
2212 UINT update_h = src_rect->bottom - src_rect->top;
2214 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_w %u, dst_point %p, srgb %#x, data {%#x:%p}.\n",
2215 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_w,
2216 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2218 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2219 update_h *= format->heightscale;
2221 ENTER_GL();
2223 if (data->buffer_object)
2225 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2226 checkGLcall("glBindBufferARB");
2229 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2231 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2232 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2233 UINT src_pitch = wined3d_format_calculate_size(format, 1, src_w, 1);
2234 const BYTE *addr = data->addr;
2235 GLenum internal;
2237 addr += (src_rect->top / format->block_height) * src_pitch;
2238 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2240 if (srgb)
2241 internal = format->glGammaInternal;
2242 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2243 internal = format->rtInternal;
2244 else
2245 internal = format->glInternal;
2247 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2248 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2249 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2251 if (row_length == src_pitch)
2253 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2254 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2256 else
2258 UINT row, y;
2260 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2261 * can't use the unpack row length like below. */
2262 for (row = 0, y = dst_point->y; row < row_count; ++row)
2264 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2265 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2266 y += format->block_height;
2267 addr += src_pitch;
2270 checkGLcall("glCompressedTexSubImage2DARB");
2272 else
2274 const BYTE *addr = data->addr;
2276 addr += src_rect->top * src_w * format->byte_count;
2277 addr += src_rect->left * format->byte_count;
2279 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2280 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2281 update_w, update_h, format->glFormat, format->glType, addr);
2283 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_w);
2284 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2285 update_w, update_h, format->glFormat, format->glType, addr);
2286 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2287 checkGLcall("glTexSubImage2D");
2290 if (data->buffer_object)
2292 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2293 checkGLcall("glBindBufferARB");
2296 LEAVE_GL();
2298 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2300 struct wined3d_device *device = surface->resource.device;
2301 unsigned int i;
2303 for (i = 0; i < device->context_count; ++i)
2305 context_surface_update(device->contexts[i], surface);
2310 /* This call just allocates the texture, the caller is responsible for binding
2311 * the correct texture. */
2312 /* Context activation is done by the caller. */
2313 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2314 const struct wined3d_format *format, BOOL srgb)
2316 BOOL enable_client_storage = FALSE;
2317 GLsizei width = surface->pow2Width;
2318 GLsizei height = surface->pow2Height;
2319 const BYTE *mem = NULL;
2320 GLenum internal;
2322 if (srgb)
2324 internal = format->glGammaInternal;
2326 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2328 internal = format->rtInternal;
2330 else
2332 internal = format->glInternal;
2335 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2337 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",
2338 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2339 internal, width, height, format->glFormat, format->glType);
2341 ENTER_GL();
2343 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2345 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2346 || !surface->resource.allocatedMemory)
2348 /* In some cases we want to disable client storage.
2349 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2350 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2351 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2352 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2354 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2355 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2356 surface->flags &= ~SFLAG_CLIENT;
2357 enable_client_storage = TRUE;
2359 else
2361 surface->flags |= SFLAG_CLIENT;
2363 /* Point OpenGL to our allocated texture memory. Do not use
2364 * resource.allocatedMemory here because it might point into a
2365 * PBO. Instead use heapMemory, but get the alignment right. */
2366 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2367 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2371 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2373 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2374 internal, width, height, 0, surface->resource.size, mem));
2375 checkGLcall("glCompressedTexImage2DARB");
2377 else
2379 glTexImage2D(surface->texture_target, surface->texture_level,
2380 internal, width, height, 0, format->glFormat, format->glType, mem);
2381 checkGLcall("glTexImage2D");
2384 if(enable_client_storage) {
2385 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2386 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2388 LEAVE_GL();
2391 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2392 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2393 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2394 /* GL locking is done by the caller */
2395 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2397 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2398 struct wined3d_renderbuffer_entry *entry;
2399 GLuint renderbuffer = 0;
2400 unsigned int src_width, src_height;
2401 unsigned int width, height;
2403 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2405 width = rt->pow2Width;
2406 height = rt->pow2Height;
2408 else
2410 width = surface->pow2Width;
2411 height = surface->pow2Height;
2414 src_width = surface->pow2Width;
2415 src_height = surface->pow2Height;
2417 /* A depth stencil smaller than the render target is not valid */
2418 if (width > src_width || height > src_height) return;
2420 /* Remove any renderbuffer set if the sizes match */
2421 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2422 || (width == src_width && height == src_height))
2424 surface->current_renderbuffer = NULL;
2425 return;
2428 /* Look if we've already got a renderbuffer of the correct dimensions */
2429 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2431 if (entry->width == width && entry->height == height)
2433 renderbuffer = entry->id;
2434 surface->current_renderbuffer = entry;
2435 break;
2439 if (!renderbuffer)
2441 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2442 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2443 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2444 surface->resource.format->glInternal, width, height);
2446 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2447 entry->width = width;
2448 entry->height = height;
2449 entry->id = renderbuffer;
2450 list_add_head(&surface->renderbuffers, &entry->entry);
2452 surface->current_renderbuffer = entry;
2455 checkGLcall("set_compatible_renderbuffer");
2458 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2460 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2462 TRACE("surface %p.\n", surface);
2464 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2466 ERR("Surface %p is not on a swapchain.\n", surface);
2467 return GL_NONE;
2470 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2472 if (swapchain->render_to_fbo)
2474 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2475 return GL_COLOR_ATTACHMENT0;
2477 TRACE("Returning GL_BACK\n");
2478 return GL_BACK;
2480 else if (surface == swapchain->front_buffer)
2482 TRACE("Returning GL_FRONT\n");
2483 return GL_FRONT;
2486 FIXME("Higher back buffer, returning GL_BACK\n");
2487 return GL_BACK;
2490 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2491 void surface_add_dirty_rect(struct wined3d_surface *surface, const WINED3DBOX *dirty_rect)
2493 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2495 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2496 /* No partial locking for textures yet. */
2497 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2499 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2500 if (dirty_rect)
2502 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->Left);
2503 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->Top);
2504 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->Right);
2505 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->Bottom);
2507 else
2509 surface->dirtyRect.left = 0;
2510 surface->dirtyRect.top = 0;
2511 surface->dirtyRect.right = surface->resource.width;
2512 surface->dirtyRect.bottom = surface->resource.height;
2515 /* if the container is a texture then mark it dirty. */
2516 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2518 TRACE("Passing to container.\n");
2519 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2523 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
2524 DWORD color, WINED3DCOLORVALUE *float_color)
2526 const struct wined3d_format *format = surface->resource.format;
2527 const struct wined3d_device *device = surface->resource.device;
2529 switch (format->id)
2531 case WINED3DFMT_P8_UINT:
2532 if (surface->palette)
2534 float_color->r = surface->palette->palents[color].peRed / 255.0f;
2535 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
2536 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
2538 else
2540 float_color->r = 0.0f;
2541 float_color->g = 0.0f;
2542 float_color->b = 0.0f;
2544 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
2545 break;
2547 case WINED3DFMT_B5G6R5_UNORM:
2548 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
2549 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
2550 float_color->b = (color & 0x1f) / 31.0f;
2551 float_color->a = 1.0f;
2552 break;
2554 case WINED3DFMT_B8G8R8_UNORM:
2555 case WINED3DFMT_B8G8R8X8_UNORM:
2556 float_color->r = D3DCOLOR_R(color);
2557 float_color->g = D3DCOLOR_G(color);
2558 float_color->b = D3DCOLOR_B(color);
2559 float_color->a = 1.0f;
2560 break;
2562 case WINED3DFMT_B8G8R8A8_UNORM:
2563 float_color->r = D3DCOLOR_R(color);
2564 float_color->g = D3DCOLOR_G(color);
2565 float_color->b = D3DCOLOR_B(color);
2566 float_color->a = D3DCOLOR_A(color);
2567 break;
2569 default:
2570 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
2571 return FALSE;
2574 return TRUE;
2577 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2579 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2580 BOOL ck_changed;
2582 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2584 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2586 ERR("Not supported on scratch surfaces.\n");
2587 return WINED3DERR_INVALIDCALL;
2590 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2592 /* Reload if either the texture and sysmem have different ideas about the
2593 * color key, or the actual key values changed. */
2594 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2595 && (surface->glCKey.dwColorSpaceLowValue != surface->SrcBltCKey.dwColorSpaceLowValue
2596 || surface->glCKey.dwColorSpaceHighValue != surface->SrcBltCKey.dwColorSpaceHighValue)))
2598 TRACE("Reloading because of color keying\n");
2599 /* To perform the color key conversion we need a sysmem copy of
2600 * the surface. Make sure we have it. */
2602 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2603 /* Make sure the texture is reloaded because of the color key change,
2604 * this kills performance though :( */
2605 /* TODO: This is not necessarily needed with hw palettized texture support. */
2606 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2607 /* Switching color keying on / off may change the internal format. */
2608 if (ck_changed)
2609 surface_force_reload(surface);
2611 else if (!(surface->flags & flag))
2613 TRACE("Reloading because surface is dirty.\n");
2615 else
2617 TRACE("surface is already in texture\n");
2618 return WINED3D_OK;
2621 /* No partial locking for textures yet. */
2622 surface_load_location(surface, flag, NULL);
2623 surface_evict_sysmem(surface);
2625 return WINED3D_OK;
2628 /* See also float_16_to_32() in wined3d_private.h */
2629 static inline unsigned short float_32_to_16(const float *in)
2631 int exp = 0;
2632 float tmp = fabsf(*in);
2633 unsigned int mantissa;
2634 unsigned short ret;
2636 /* Deal with special numbers */
2637 if (*in == 0.0f)
2638 return 0x0000;
2639 if (isnan(*in))
2640 return 0x7c01;
2641 if (isinf(*in))
2642 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2644 if (tmp < powf(2, 10))
2648 tmp = tmp * 2.0f;
2649 exp--;
2650 } while (tmp < powf(2, 10));
2652 else if (tmp >= powf(2, 11))
2656 tmp /= 2.0f;
2657 exp++;
2658 } while (tmp >= powf(2, 11));
2661 mantissa = (unsigned int)tmp;
2662 if (tmp - mantissa >= 0.5f)
2663 ++mantissa; /* Round to nearest, away from zero. */
2665 exp += 10; /* Normalize the mantissa. */
2666 exp += 15; /* Exponent is encoded with excess 15. */
2668 if (exp > 30) /* too big */
2670 ret = 0x7c00; /* INF */
2672 else if (exp <= 0)
2674 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2675 while (exp <= 0)
2677 mantissa = mantissa >> 1;
2678 ++exp;
2680 ret = mantissa & 0x3ff;
2682 else
2684 ret = (exp << 10) | (mantissa & 0x3ff);
2687 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2688 return ret;
2691 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2693 ULONG refcount;
2695 TRACE("Surface %p, container %p of type %#x.\n",
2696 surface, surface->container.u.base, surface->container.type);
2698 switch (surface->container.type)
2700 case WINED3D_CONTAINER_TEXTURE:
2701 return wined3d_texture_incref(surface->container.u.texture);
2703 case WINED3D_CONTAINER_SWAPCHAIN:
2704 return wined3d_swapchain_incref(surface->container.u.swapchain);
2706 default:
2707 ERR("Unhandled container type %#x.\n", surface->container.type);
2708 case WINED3D_CONTAINER_NONE:
2709 break;
2712 refcount = InterlockedIncrement(&surface->resource.ref);
2713 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2715 return refcount;
2718 /* Do not call while under the GL lock. */
2719 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2721 ULONG refcount;
2723 TRACE("Surface %p, container %p of type %#x.\n",
2724 surface, surface->container.u.base, surface->container.type);
2726 switch (surface->container.type)
2728 case WINED3D_CONTAINER_TEXTURE:
2729 return wined3d_texture_decref(surface->container.u.texture);
2731 case WINED3D_CONTAINER_SWAPCHAIN:
2732 return wined3d_swapchain_decref(surface->container.u.swapchain);
2734 default:
2735 ERR("Unhandled container type %#x.\n", surface->container.type);
2736 case WINED3D_CONTAINER_NONE:
2737 break;
2740 refcount = InterlockedDecrement(&surface->resource.ref);
2741 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2743 if (!refcount)
2745 surface->surface_ops->surface_cleanup(surface);
2746 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2748 TRACE("Destroyed surface %p.\n", surface);
2749 HeapFree(GetProcessHeap(), 0, surface);
2752 return refcount;
2755 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2757 return resource_set_priority(&surface->resource, priority);
2760 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2762 return resource_get_priority(&surface->resource);
2765 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2767 TRACE("surface %p.\n", surface);
2769 surface->surface_ops->surface_preload(surface);
2772 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2774 TRACE("surface %p.\n", surface);
2776 return surface->resource.parent;
2779 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2781 TRACE("surface %p.\n", surface);
2783 return &surface->resource;
2786 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2788 TRACE("surface %p, flags %#x.\n", surface, flags);
2790 switch (flags)
2792 case WINEDDGBS_CANBLT:
2793 case WINEDDGBS_ISBLTDONE:
2794 return WINED3D_OK;
2796 default:
2797 return WINED3DERR_INVALIDCALL;
2801 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2803 TRACE("surface %p, flags %#x.\n", surface, flags);
2805 /* XXX: DDERR_INVALIDSURFACETYPE */
2807 switch (flags)
2809 case WINEDDGFS_CANFLIP:
2810 case WINEDDGFS_ISFLIPDONE:
2811 return WINED3D_OK;
2813 default:
2814 return WINED3DERR_INVALIDCALL;
2818 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2820 TRACE("surface %p.\n", surface);
2822 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2823 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2826 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2828 TRACE("surface %p.\n", surface);
2830 /* So far we don't lose anything :) */
2831 surface->flags &= ~SFLAG_LOST;
2832 return WINED3D_OK;
2835 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2837 TRACE("surface %p, palette %p.\n", surface, palette);
2839 if (surface->palette == palette)
2841 TRACE("Nop palette change.\n");
2842 return WINED3D_OK;
2845 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2846 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2848 surface->palette = palette;
2850 if (palette)
2852 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2853 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
2855 surface->surface_ops->surface_realize_palette(surface);
2858 return WINED3D_OK;
2861 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
2862 DWORD flags, const WINEDDCOLORKEY *color_key)
2864 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
2866 if (flags & WINEDDCKEY_COLORSPACE)
2868 FIXME(" colorkey value not supported (%08x) !\n", flags);
2869 return WINED3DERR_INVALIDCALL;
2872 /* Dirtify the surface, but only if a key was changed. */
2873 if (color_key)
2875 switch (flags & ~WINEDDCKEY_COLORSPACE)
2877 case WINEDDCKEY_DESTBLT:
2878 surface->DestBltCKey = *color_key;
2879 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
2880 break;
2882 case WINEDDCKEY_DESTOVERLAY:
2883 surface->DestOverlayCKey = *color_key;
2884 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
2885 break;
2887 case WINEDDCKEY_SRCOVERLAY:
2888 surface->SrcOverlayCKey = *color_key;
2889 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
2890 break;
2892 case WINEDDCKEY_SRCBLT:
2893 surface->SrcBltCKey = *color_key;
2894 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
2895 break;
2898 else
2900 switch (flags & ~WINEDDCKEY_COLORSPACE)
2902 case WINEDDCKEY_DESTBLT:
2903 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
2904 break;
2906 case WINEDDCKEY_DESTOVERLAY:
2907 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
2908 break;
2910 case WINEDDCKEY_SRCOVERLAY:
2911 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
2912 break;
2914 case WINEDDCKEY_SRCBLT:
2915 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
2916 break;
2920 return WINED3D_OK;
2923 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
2925 TRACE("surface %p.\n", surface);
2927 return surface->palette;
2930 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2932 const struct wined3d_format *format = surface->resource.format;
2933 DWORD pitch;
2935 TRACE("surface %p.\n", surface);
2937 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
2939 /* Since compressed formats are block based, pitch means the amount of
2940 * bytes to the next row of block rather than the next row of pixels. */
2941 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
2942 pitch = row_block_count * format->block_byte_count;
2944 else
2946 unsigned char alignment = surface->resource.device->surface_alignment;
2947 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
2948 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2951 TRACE("Returning %u.\n", pitch);
2953 return pitch;
2956 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
2958 TRACE("surface %p, mem %p.\n", surface, mem);
2960 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
2962 WARN("Surface is locked or the DC is in use.\n");
2963 return WINED3DERR_INVALIDCALL;
2966 return surface->surface_ops->surface_set_mem(surface, mem);
2969 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2971 LONG w, h;
2973 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2975 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2977 WARN("Not an overlay surface.\n");
2978 return WINEDDERR_NOTAOVERLAYSURFACE;
2981 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2982 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2983 surface->overlay_destrect.left = x;
2984 surface->overlay_destrect.top = y;
2985 surface->overlay_destrect.right = x + w;
2986 surface->overlay_destrect.bottom = y + h;
2988 surface->surface_ops->surface_draw_overlay(surface);
2990 return WINED3D_OK;
2993 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2995 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2997 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2999 TRACE("Not an overlay surface.\n");
3000 return WINEDDERR_NOTAOVERLAYSURFACE;
3003 if (!surface->overlay_dest)
3005 TRACE("Overlay not visible.\n");
3006 *x = 0;
3007 *y = 0;
3008 return WINEDDERR_OVERLAYNOTVISIBLE;
3011 *x = surface->overlay_destrect.left;
3012 *y = surface->overlay_destrect.top;
3014 TRACE("Returning position %d, %d.\n", *x, *y);
3016 return WINED3D_OK;
3019 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3020 DWORD flags, struct wined3d_surface *ref)
3022 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3024 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3026 TRACE("Not an overlay surface.\n");
3027 return WINEDDERR_NOTAOVERLAYSURFACE;
3030 return WINED3D_OK;
3033 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3034 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3036 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3037 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3039 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3041 WARN("Not an overlay surface.\n");
3042 return WINEDDERR_NOTAOVERLAYSURFACE;
3044 else if (!dst_surface)
3046 WARN("Dest surface is NULL.\n");
3047 return WINED3DERR_INVALIDCALL;
3050 if (src_rect)
3052 surface->overlay_srcrect = *src_rect;
3054 else
3056 surface->overlay_srcrect.left = 0;
3057 surface->overlay_srcrect.top = 0;
3058 surface->overlay_srcrect.right = surface->resource.width;
3059 surface->overlay_srcrect.bottom = surface->resource.height;
3062 if (dst_rect)
3064 surface->overlay_destrect = *dst_rect;
3066 else
3068 surface->overlay_destrect.left = 0;
3069 surface->overlay_destrect.top = 0;
3070 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3071 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3074 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3076 list_remove(&surface->overlay_entry);
3079 if (flags & WINEDDOVER_SHOW)
3081 if (surface->overlay_dest != dst_surface)
3083 surface->overlay_dest = dst_surface;
3084 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3087 else if (flags & WINEDDOVER_HIDE)
3089 /* tests show that the rectangles are erased on hide */
3090 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3091 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3092 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3093 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3094 surface->overlay_dest = NULL;
3097 surface->surface_ops->surface_draw_overlay(surface);
3099 return WINED3D_OK;
3102 HRESULT CDECL wined3d_surface_set_clipper(struct wined3d_surface *surface, struct wined3d_clipper *clipper)
3104 TRACE("surface %p, clipper %p.\n", surface, clipper);
3106 surface->clipper = clipper;
3108 return WINED3D_OK;
3111 struct wined3d_clipper * CDECL wined3d_surface_get_clipper(const struct wined3d_surface *surface)
3113 TRACE("surface %p.\n", surface);
3115 return surface->clipper;
3118 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3120 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3122 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3124 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3126 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3127 return WINED3DERR_INVALIDCALL;
3130 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3131 surface->pow2Width, surface->pow2Height);
3132 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3133 surface->resource.format = format;
3135 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3136 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3137 format->glFormat, format->glInternal, format->glType);
3139 return WINED3D_OK;
3142 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3143 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3145 unsigned short *dst_s;
3146 const float *src_f;
3147 unsigned int x, y;
3149 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3151 for (y = 0; y < h; ++y)
3153 src_f = (const float *)(src + y * pitch_in);
3154 dst_s = (unsigned short *) (dst + y * pitch_out);
3155 for (x = 0; x < w; ++x)
3157 dst_s[x] = float_32_to_16(src_f + x);
3162 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3163 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3165 static const unsigned char convert_5to8[] =
3167 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3168 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3169 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3170 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3172 static const unsigned char convert_6to8[] =
3174 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3175 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3176 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3177 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3178 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3179 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3180 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3181 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3183 unsigned int x, y;
3185 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3187 for (y = 0; y < h; ++y)
3189 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3190 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3191 for (x = 0; x < w; ++x)
3193 WORD pixel = src_line[x];
3194 dst_line[x] = 0xff000000
3195 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3196 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3197 | convert_5to8[(pixel & 0x001f)];
3202 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3203 * in both cases we're just setting the X / Alpha channel to 0xff. */
3204 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3205 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3207 unsigned int x, y;
3209 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3211 for (y = 0; y < h; ++y)
3213 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3214 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3216 for (x = 0; x < w; ++x)
3218 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3223 static inline BYTE cliptobyte(int x)
3225 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3228 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3229 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3231 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3232 unsigned int x, y;
3234 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3236 for (y = 0; y < h; ++y)
3238 const BYTE *src_line = src + y * pitch_in;
3239 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3240 for (x = 0; x < w; ++x)
3242 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3243 * C = Y - 16; D = U - 128; E = V - 128;
3244 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3245 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3246 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3247 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3248 * U and V are shared between the pixels. */
3249 if (!(x & 1)) /* For every even pixel, read new U and V. */
3251 d = (int) src_line[1] - 128;
3252 e = (int) src_line[3] - 128;
3253 r2 = 409 * e + 128;
3254 g2 = - 100 * d - 208 * e + 128;
3255 b2 = 516 * d + 128;
3257 c2 = 298 * ((int) src_line[0] - 16);
3258 dst_line[x] = 0xff000000
3259 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3260 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3261 | cliptobyte((c2 + b2) >> 8); /* blue */
3262 /* Scale RGB values to 0..255 range,
3263 * then clip them if still not in range (may be negative),
3264 * then shift them within DWORD if necessary. */
3265 src_line += 2;
3270 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3271 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3273 unsigned int x, y;
3274 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3276 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3278 for (y = 0; y < h; ++y)
3280 const BYTE *src_line = src + y * pitch_in;
3281 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3282 for (x = 0; x < w; ++x)
3284 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3285 * C = Y - 16; D = U - 128; E = V - 128;
3286 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3287 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3288 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3289 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3290 * U and V are shared between the pixels. */
3291 if (!(x & 1)) /* For every even pixel, read new U and V. */
3293 d = (int) src_line[1] - 128;
3294 e = (int) src_line[3] - 128;
3295 r2 = 409 * e + 128;
3296 g2 = - 100 * d - 208 * e + 128;
3297 b2 = 516 * d + 128;
3299 c2 = 298 * ((int) src_line[0] - 16);
3300 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3301 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3302 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3303 /* Scale RGB values to 0..255 range,
3304 * then clip them if still not in range (may be negative),
3305 * then shift them within DWORD if necessary. */
3306 src_line += 2;
3311 struct d3dfmt_convertor_desc
3313 enum wined3d_format_id from, to;
3314 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3317 static const struct d3dfmt_convertor_desc convertors[] =
3319 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3320 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3321 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3322 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3323 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3324 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3327 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3328 enum wined3d_format_id to)
3330 unsigned int i;
3332 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3334 if (convertors[i].from == from && convertors[i].to == to)
3335 return &convertors[i];
3338 return NULL;
3341 /*****************************************************************************
3342 * surface_convert_format
3344 * Creates a duplicate of a surface in a different format. Is used by Blt to
3345 * blit between surfaces with different formats.
3347 * Parameters
3348 * source: Source surface
3349 * fmt: Requested destination format
3351 *****************************************************************************/
3352 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3354 const struct d3dfmt_convertor_desc *conv;
3355 WINED3DLOCKED_RECT lock_src, lock_dst;
3356 struct wined3d_surface *ret = NULL;
3357 HRESULT hr;
3359 conv = find_convertor(source->resource.format->id, to_fmt);
3360 if (!conv)
3362 FIXME("Cannot find a conversion function from format %s to %s.\n",
3363 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3364 return NULL;
3367 wined3d_surface_create(source->resource.device, source->resource.width,
3368 source->resource.height, to_fmt, TRUE /* lockable */, TRUE /* discard */, 0 /* level */,
3369 0 /* usage */, WINED3DPOOL_SCRATCH, WINED3DMULTISAMPLE_NONE /* TODO: Multisampled conversion */,
3370 0 /* MultiSampleQuality */, source->surface_type, NULL /* parent */, &wined3d_null_parent_ops, &ret);
3371 if (!ret)
3373 ERR("Failed to create a destination surface for conversion.\n");
3374 return NULL;
3377 memset(&lock_src, 0, sizeof(lock_src));
3378 memset(&lock_dst, 0, sizeof(lock_dst));
3380 hr = wined3d_surface_map(source, &lock_src, NULL, WINED3DLOCK_READONLY);
3381 if (FAILED(hr))
3383 ERR("Failed to lock the source surface.\n");
3384 wined3d_surface_decref(ret);
3385 return NULL;
3387 hr = wined3d_surface_map(ret, &lock_dst, NULL, WINED3DLOCK_READONLY);
3388 if (FAILED(hr))
3390 ERR("Failed to lock the destination surface.\n");
3391 wined3d_surface_unmap(source);
3392 wined3d_surface_decref(ret);
3393 return NULL;
3396 conv->convert(lock_src.pBits, lock_dst.pBits, lock_src.Pitch, lock_dst.Pitch,
3397 source->resource.width, source->resource.height);
3399 wined3d_surface_unmap(ret);
3400 wined3d_surface_unmap(source);
3402 return ret;
3405 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3406 unsigned int bpp, UINT pitch, DWORD color)
3408 BYTE *first;
3409 int x, y;
3411 /* Do first row */
3413 #define COLORFILL_ROW(type) \
3414 do { \
3415 type *d = (type *)buf; \
3416 for (x = 0; x < width; ++x) \
3417 d[x] = (type)color; \
3418 } while(0)
3420 switch (bpp)
3422 case 1:
3423 COLORFILL_ROW(BYTE);
3424 break;
3426 case 2:
3427 COLORFILL_ROW(WORD);
3428 break;
3430 case 3:
3432 BYTE *d = buf;
3433 for (x = 0; x < width; ++x, d += 3)
3435 d[0] = (color ) & 0xFF;
3436 d[1] = (color >> 8) & 0xFF;
3437 d[2] = (color >> 16) & 0xFF;
3439 break;
3441 case 4:
3442 COLORFILL_ROW(DWORD);
3443 break;
3445 default:
3446 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3447 return WINED3DERR_NOTAVAILABLE;
3450 #undef COLORFILL_ROW
3452 /* Now copy first row. */
3453 first = buf;
3454 for (y = 1; y < height; ++y)
3456 buf += pitch;
3457 memcpy(buf, first, width * bpp);
3460 return WINED3D_OK;
3463 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3465 TRACE("surface %p.\n", surface);
3467 if (!(surface->flags & SFLAG_LOCKED))
3469 WARN("Trying to unmap unmapped surface.\n");
3470 return WINEDDERR_NOTLOCKED;
3472 surface->flags &= ~SFLAG_LOCKED;
3474 surface->surface_ops->surface_unmap(surface);
3476 return WINED3D_OK;
3479 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3480 WINED3DLOCKED_RECT *locked_rect, const RECT *rect, DWORD flags)
3482 TRACE("surface %p, locked_rect %p, rect %s, flags %#x.\n",
3483 surface, locked_rect, wine_dbgstr_rect(rect), flags);
3485 if (surface->flags & SFLAG_LOCKED)
3487 WARN("Surface is already mapped.\n");
3488 return WINED3DERR_INVALIDCALL;
3490 surface->flags |= SFLAG_LOCKED;
3492 if (!(surface->flags & SFLAG_LOCKABLE))
3493 WARN("Trying to lock unlockable surface.\n");
3495 surface->surface_ops->surface_map(surface, rect, flags);
3497 locked_rect->Pitch = wined3d_surface_get_pitch(surface);
3499 if (!rect)
3501 locked_rect->pBits = surface->resource.allocatedMemory;
3502 surface->lockedRect.left = 0;
3503 surface->lockedRect.top = 0;
3504 surface->lockedRect.right = surface->resource.width;
3505 surface->lockedRect.bottom = surface->resource.height;
3507 else
3509 const struct wined3d_format *format = surface->resource.format;
3511 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3513 /* Compressed textures are block based, so calculate the offset of
3514 * the block that contains the top-left pixel of the locked rectangle. */
3515 locked_rect->pBits = surface->resource.allocatedMemory
3516 + ((rect->top / format->block_height) * locked_rect->Pitch)
3517 + ((rect->left / format->block_width) * format->block_byte_count);
3519 else
3521 locked_rect->pBits = surface->resource.allocatedMemory
3522 + (locked_rect->Pitch * rect->top)
3523 + (rect->left * format->byte_count);
3525 surface->lockedRect.left = rect->left;
3526 surface->lockedRect.top = rect->top;
3527 surface->lockedRect.right = rect->right;
3528 surface->lockedRect.bottom = rect->bottom;
3531 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3532 TRACE("Returning memory %p, pitch %u.\n", locked_rect->pBits, locked_rect->Pitch);
3534 return WINED3D_OK;
3537 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3539 HRESULT hr;
3541 TRACE("surface %p, dc %p.\n", surface, dc);
3543 if (surface->flags & SFLAG_USERPTR)
3545 ERR("Not supported on surfaces with application-provided memory.\n");
3546 return WINEDDERR_NODC;
3549 /* Give more detailed info for ddraw. */
3550 if (surface->flags & SFLAG_DCINUSE)
3551 return WINEDDERR_DCALREADYCREATED;
3553 /* Can't GetDC if the surface is locked. */
3554 if (surface->flags & SFLAG_LOCKED)
3555 return WINED3DERR_INVALIDCALL;
3557 hr = surface->surface_ops->surface_getdc(surface);
3558 if (FAILED(hr))
3559 return hr;
3561 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3562 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3564 /* GetDC on palettized formats is unsupported in D3D9, and the method
3565 * is missing in D3D8, so this should only be used for DX <=7
3566 * surfaces (with non-device palettes). */
3567 const PALETTEENTRY *pal = NULL;
3569 if (surface->palette)
3571 pal = surface->palette->palents;
3573 else
3575 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3576 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3578 if (dds_primary && dds_primary->palette)
3579 pal = dds_primary->palette->palents;
3582 if (pal)
3584 RGBQUAD col[256];
3585 unsigned int i;
3587 for (i = 0; i < 256; ++i)
3589 col[i].rgbRed = pal[i].peRed;
3590 col[i].rgbGreen = pal[i].peGreen;
3591 col[i].rgbBlue = pal[i].peBlue;
3592 col[i].rgbReserved = 0;
3594 SetDIBColorTable(surface->hDC, 0, 256, col);
3598 surface->flags |= SFLAG_DCINUSE;
3600 *dc = surface->hDC;
3601 TRACE("Returning dc %p.\n", *dc);
3603 return WINED3D_OK;
3606 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3608 TRACE("surface %p, dc %p.\n", surface, dc);
3610 if (!(surface->flags & SFLAG_DCINUSE))
3611 return WINEDDERR_NODC;
3613 if (surface->hDC != dc)
3615 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3616 dc, surface->hDC);
3617 return WINEDDERR_NODC;
3620 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3622 /* Copy the contents of the DIB over to the PBO. */
3623 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->dib.bitmap_size);
3626 /* We locked first, so unlock now. */
3627 wined3d_surface_unmap(surface);
3629 surface->flags &= ~SFLAG_DCINUSE;
3631 return WINED3D_OK;
3634 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3636 struct wined3d_swapchain *swapchain;
3637 HRESULT hr;
3639 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3641 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
3643 ERR("Flipped surface is not on a swapchain.\n");
3644 return WINEDDERR_NOTFLIPPABLE;
3646 swapchain = surface->container.u.swapchain;
3648 hr = surface->surface_ops->surface_flip(surface, override);
3649 if (FAILED(hr))
3650 return hr;
3652 /* Just overwrite the swapchain presentation interval. This is ok because
3653 * only ddraw apps can call Flip, and only d3d8 and d3d9 applications
3654 * specify the presentation interval. */
3655 if (!(flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)))
3656 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
3657 else if (flags & WINEDDFLIP_NOVSYNC)
3658 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3659 else if (flags & WINEDDFLIP_INTERVAL2)
3660 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
3661 else if (flags & WINEDDFLIP_INTERVAL3)
3662 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
3663 else
3664 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3666 return wined3d_swapchain_present(swapchain, NULL, NULL, swapchain->win_handle, NULL, 0);
3669 /* Do not call while under the GL lock. */
3670 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3672 struct wined3d_device *device = surface->resource.device;
3674 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3676 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3678 struct wined3d_texture *texture = surface->container.u.texture;
3680 TRACE("Passing to container (%p).\n", texture);
3681 texture->texture_ops->texture_preload(texture, srgb);
3683 else
3685 struct wined3d_context *context = NULL;
3687 TRACE("(%p) : About to load surface\n", surface);
3689 if (!device->isInDraw) context = context_acquire(device, NULL);
3691 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3692 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3694 if (palette9_changed(surface))
3696 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
3697 /* TODO: This is not necessarily needed with hw palettized texture support */
3698 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3699 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
3700 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
3704 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3706 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3708 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3709 GLclampf tmp;
3710 tmp = 0.9f;
3711 ENTER_GL();
3712 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3713 LEAVE_GL();
3716 if (context) context_release(context);
3720 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3722 if (!surface->resource.allocatedMemory)
3724 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3725 surface->resource.size + RESOURCE_ALIGNMENT);
3726 if (!surface->resource.heapMemory)
3728 ERR("Out of memory\n");
3729 return FALSE;
3731 surface->resource.allocatedMemory =
3732 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
3734 else
3736 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
3739 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3741 return TRUE;
3744 /* Read the framebuffer back into the surface */
3745 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
3747 struct wined3d_device *device = surface->resource.device;
3748 const struct wined3d_gl_info *gl_info;
3749 struct wined3d_context *context;
3750 BYTE *mem;
3751 GLint fmt;
3752 GLint type;
3753 BYTE *row, *top, *bottom;
3754 int i;
3755 BOOL bpp;
3756 RECT local_rect;
3757 BOOL srcIsUpsideDown;
3758 GLint rowLen = 0;
3759 GLint skipPix = 0;
3760 GLint skipRow = 0;
3762 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
3763 static BOOL warned = FALSE;
3764 if(!warned) {
3765 ERR("The application tries to lock the render target, but render target locking is disabled\n");
3766 warned = TRUE;
3768 return;
3771 context = context_acquire(device, surface);
3772 context_apply_blit_state(context, device);
3773 gl_info = context->gl_info;
3775 ENTER_GL();
3777 /* Select the correct read buffer, and give some debug output.
3778 * There is no need to keep track of the current read buffer or reset it, every part of the code
3779 * that reads sets the read buffer as desired.
3781 if (surface_is_offscreen(surface))
3783 /* Mapping the primary render target which is not on a swapchain.
3784 * Read from the back buffer. */
3785 TRACE("Mapping offscreen render target.\n");
3786 glReadBuffer(device->offscreenBuffer);
3787 srcIsUpsideDown = TRUE;
3789 else
3791 /* Onscreen surfaces are always part of a swapchain */
3792 GLenum buffer = surface_get_gl_buffer(surface);
3793 TRACE("Mapping %#x buffer.\n", buffer);
3794 glReadBuffer(buffer);
3795 checkGLcall("glReadBuffer");
3796 srcIsUpsideDown = FALSE;
3799 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
3800 if (!rect)
3802 local_rect.left = 0;
3803 local_rect.top = 0;
3804 local_rect.right = surface->resource.width;
3805 local_rect.bottom = surface->resource.height;
3807 else
3809 local_rect = *rect;
3811 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
3813 switch (surface->resource.format->id)
3815 case WINED3DFMT_P8_UINT:
3817 if (primary_render_target_is_p8(device))
3819 /* In case of P8 render targets the index is stored in the alpha component */
3820 fmt = GL_ALPHA;
3821 type = GL_UNSIGNED_BYTE;
3822 mem = dest;
3823 bpp = surface->resource.format->byte_count;
3825 else
3827 /* GL can't return palettized data, so read ARGB pixels into a
3828 * separate block of memory and convert them into palettized format
3829 * in software. Slow, but if the app means to use palettized render
3830 * targets and locks it...
3832 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
3833 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
3834 * for the color channels when palettizing the colors.
3836 fmt = GL_RGB;
3837 type = GL_UNSIGNED_BYTE;
3838 pitch *= 3;
3839 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
3840 if (!mem)
3842 ERR("Out of memory\n");
3843 LEAVE_GL();
3844 return;
3846 bpp = surface->resource.format->byte_count * 3;
3849 break;
3851 default:
3852 mem = dest;
3853 fmt = surface->resource.format->glFormat;
3854 type = surface->resource.format->glType;
3855 bpp = surface->resource.format->byte_count;
3858 if (surface->flags & SFLAG_PBO)
3860 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
3861 checkGLcall("glBindBufferARB");
3862 if (mem)
3864 ERR("mem not null for pbo -- unexpected\n");
3865 mem = NULL;
3869 /* Save old pixel store pack state */
3870 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
3871 checkGLcall("glGetIntegerv");
3872 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
3873 checkGLcall("glGetIntegerv");
3874 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
3875 checkGLcall("glGetIntegerv");
3877 /* Setup pixel store pack state -- to glReadPixels into the correct place */
3878 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
3879 checkGLcall("glPixelStorei");
3880 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
3881 checkGLcall("glPixelStorei");
3882 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
3883 checkGLcall("glPixelStorei");
3885 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
3886 local_rect.right - local_rect.left,
3887 local_rect.bottom - local_rect.top,
3888 fmt, type, mem);
3889 checkGLcall("glReadPixels");
3891 /* Reset previous pixel store pack state */
3892 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
3893 checkGLcall("glPixelStorei");
3894 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
3895 checkGLcall("glPixelStorei");
3896 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
3897 checkGLcall("glPixelStorei");
3899 if (surface->flags & SFLAG_PBO)
3901 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
3902 checkGLcall("glBindBufferARB");
3904 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
3905 * to get a pointer to it and perform the flipping in software. This is a lot
3906 * faster than calling glReadPixels for each line. In case we want more speed
3907 * we should rerender it flipped in a FBO and read the data back from the FBO. */
3908 if (!srcIsUpsideDown)
3910 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
3911 checkGLcall("glBindBufferARB");
3913 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3914 checkGLcall("glMapBufferARB");
3918 /* TODO: Merge this with the palettization loop below for P8 targets */
3919 if(!srcIsUpsideDown) {
3920 UINT len, off;
3921 /* glReadPixels returns the image upside down, and there is no way to prevent this.
3922 Flip the lines in software */
3923 len = (local_rect.right - local_rect.left) * bpp;
3924 off = local_rect.left * bpp;
3926 row = HeapAlloc(GetProcessHeap(), 0, len);
3927 if(!row) {
3928 ERR("Out of memory\n");
3929 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
3930 HeapFree(GetProcessHeap(), 0, mem);
3931 LEAVE_GL();
3932 return;
3935 top = mem + pitch * local_rect.top;
3936 bottom = mem + pitch * (local_rect.bottom - 1);
3937 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
3938 memcpy(row, top + off, len);
3939 memcpy(top + off, bottom + off, len);
3940 memcpy(bottom + off, row, len);
3941 top += pitch;
3942 bottom -= pitch;
3944 HeapFree(GetProcessHeap(), 0, row);
3946 /* Unmap the temp PBO buffer */
3947 if (surface->flags & SFLAG_PBO)
3949 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
3950 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
3954 LEAVE_GL();
3955 context_release(context);
3957 /* For P8 textures we need to perform an inverse palette lookup. This is
3958 * done by searching for a palette index which matches the RGB value.
3959 * Note this isn't guaranteed to work when there are multiple entries for
3960 * the same color but we have no choice. In case of P8 render targets,
3961 * the index is stored in the alpha component so no conversion is needed. */
3962 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
3964 const PALETTEENTRY *pal = NULL;
3965 DWORD width = pitch / 3;
3966 int x, y, c;
3968 if (surface->palette)
3970 pal = surface->palette->palents;
3972 else
3974 ERR("Palette is missing, cannot perform inverse palette lookup\n");
3975 HeapFree(GetProcessHeap(), 0, mem);
3976 return;
3979 for(y = local_rect.top; y < local_rect.bottom; y++) {
3980 for(x = local_rect.left; x < local_rect.right; x++) {
3981 /* start lines pixels */
3982 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
3983 const BYTE *green = blue + 1;
3984 const BYTE *red = green + 1;
3986 for(c = 0; c < 256; c++) {
3987 if(*red == pal[c].peRed &&
3988 *green == pal[c].peGreen &&
3989 *blue == pal[c].peBlue)
3991 *((BYTE *) dest + y * width + x) = c;
3992 break;
3997 HeapFree(GetProcessHeap(), 0, mem);
4001 /* Read the framebuffer contents into a texture */
4002 static void read_from_framebuffer_texture(struct wined3d_surface *surface, BOOL srgb)
4004 struct wined3d_device *device = surface->resource.device;
4005 const struct wined3d_gl_info *gl_info;
4006 struct wined3d_context *context;
4008 if (!surface_is_offscreen(surface))
4010 /* We would need to flip onscreen surfaces, but there's no efficient
4011 * way to do that here. It makes more sense for the caller to
4012 * explicitly go through sysmem. */
4013 ERR("Not supported for onscreen targets.\n");
4014 return;
4017 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
4018 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
4019 * states in the stateblock, and no driver was found yet that had bugs in that regard.
4021 context = context_acquire(device, surface);
4022 gl_info = context->gl_info;
4023 device_invalidate_state(device, STATE_FRAMEBUFFER);
4025 surface_prepare_texture(surface, gl_info, srgb);
4026 surface_bind_and_dirtify(surface, gl_info, srgb);
4028 TRACE("Reading back offscreen render target %p.\n", surface);
4030 ENTER_GL();
4032 glReadBuffer(device->offscreenBuffer);
4033 checkGLcall("glReadBuffer");
4035 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4036 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4037 checkGLcall("glCopyTexSubImage2D");
4039 LEAVE_GL();
4041 context_release(context);
4044 /* Context activation is done by the caller. */
4045 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4046 const struct wined3d_gl_info *gl_info, BOOL srgb)
4048 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4049 CONVERT_TYPES convert;
4050 struct wined3d_format format;
4052 if (surface->flags & alloc_flag) return;
4054 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4055 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4056 else surface->flags &= ~SFLAG_CONVERTED;
4058 surface_bind_and_dirtify(surface, gl_info, srgb);
4059 surface_allocate_surface(surface, gl_info, &format, srgb);
4060 surface->flags |= alloc_flag;
4063 /* Context activation is done by the caller. */
4064 void surface_prepare_texture(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
4066 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4068 struct wined3d_texture *texture = surface->container.u.texture;
4069 UINT sub_count = texture->level_count * texture->layer_count;
4070 UINT i;
4072 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4074 for (i = 0; i < sub_count; ++i)
4076 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4077 surface_prepare_texture_internal(s, gl_info, srgb);
4080 return;
4083 surface_prepare_texture_internal(surface, gl_info, srgb);
4086 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4087 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4089 struct wined3d_device *device = surface->resource.device;
4090 UINT pitch = wined3d_surface_get_pitch(surface);
4091 const struct wined3d_gl_info *gl_info;
4092 struct wined3d_context *context;
4093 RECT local_rect;
4094 UINT w, h;
4096 surface_get_rect(surface, rect, &local_rect);
4098 mem += local_rect.top * pitch + local_rect.left * bpp;
4099 w = local_rect.right - local_rect.left;
4100 h = local_rect.bottom - local_rect.top;
4102 /* Activate the correct context for the render target */
4103 context = context_acquire(device, surface);
4104 context_apply_blit_state(context, device);
4105 gl_info = context->gl_info;
4107 ENTER_GL();
4109 if (!surface_is_offscreen(surface))
4111 GLenum buffer = surface_get_gl_buffer(surface);
4112 TRACE("Unlocking %#x buffer.\n", buffer);
4113 context_set_draw_buffer(context, buffer);
4115 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4116 glPixelZoom(1.0f, -1.0f);
4118 else
4120 /* Primary offscreen render target */
4121 TRACE("Offscreen render target.\n");
4122 context_set_draw_buffer(context, device->offscreenBuffer);
4124 glPixelZoom(1.0f, 1.0f);
4127 glRasterPos3i(local_rect.left, local_rect.top, 1);
4128 checkGLcall("glRasterPos3i");
4130 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4131 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4133 if (surface->flags & SFLAG_PBO)
4135 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4136 checkGLcall("glBindBufferARB");
4139 glDrawPixels(w, h, fmt, type, mem);
4140 checkGLcall("glDrawPixels");
4142 if (surface->flags & SFLAG_PBO)
4144 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4145 checkGLcall("glBindBufferARB");
4148 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4149 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4151 LEAVE_GL();
4153 if (wined3d_settings.strict_draw_ordering
4154 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4155 && surface->container.u.swapchain->front_buffer == surface))
4156 wglFlush();
4158 context_release(context);
4161 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4162 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4164 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4165 const struct wined3d_device *device = surface->resource.device;
4166 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4167 BOOL blit_supported = FALSE;
4169 /* Copy the default values from the surface. Below we might perform fixups */
4170 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4171 *format = *surface->resource.format;
4172 *convert = NO_CONVERSION;
4174 /* Ok, now look if we have to do any conversion */
4175 switch (surface->resource.format->id)
4177 case WINED3DFMT_P8_UINT:
4178 /* Below the call to blit_supported is disabled for Wine 1.2
4179 * because the function isn't operating correctly yet. At the
4180 * moment 8-bit blits are handled in software and if certain GL
4181 * extensions are around, surface conversion is performed at
4182 * upload time. The blit_supported call recognizes it as a
4183 * destination fixup. This type of upload 'fixup' and 8-bit to
4184 * 8-bit blits need to be handled by the blit_shader.
4185 * TODO: get rid of this #if 0. */
4186 #if 0
4187 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4188 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4189 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4190 #endif
4191 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4193 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4194 * texturing. Further also use conversion in case of color keying.
4195 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4196 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4197 * conflicts with this.
4199 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4200 || colorkey_active || !use_texturing)
4202 format->glFormat = GL_RGBA;
4203 format->glInternal = GL_RGBA;
4204 format->glType = GL_UNSIGNED_BYTE;
4205 format->conv_byte_count = 4;
4206 if (colorkey_active)
4207 *convert = CONVERT_PALETTED_CK;
4208 else
4209 *convert = CONVERT_PALETTED;
4211 break;
4213 case WINED3DFMT_B2G3R3_UNORM:
4214 /* **********************
4215 GL_UNSIGNED_BYTE_3_3_2
4216 ********************** */
4217 if (colorkey_active) {
4218 /* This texture format will never be used.. So do not care about color keying
4219 up until the point in time it will be needed :-) */
4220 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4222 break;
4224 case WINED3DFMT_B5G6R5_UNORM:
4225 if (colorkey_active)
4227 *convert = CONVERT_CK_565;
4228 format->glFormat = GL_RGBA;
4229 format->glInternal = GL_RGB5_A1;
4230 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4231 format->conv_byte_count = 2;
4233 break;
4235 case WINED3DFMT_B5G5R5X1_UNORM:
4236 if (colorkey_active)
4238 *convert = CONVERT_CK_5551;
4239 format->glFormat = GL_BGRA;
4240 format->glInternal = GL_RGB5_A1;
4241 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4242 format->conv_byte_count = 2;
4244 break;
4246 case WINED3DFMT_B8G8R8_UNORM:
4247 if (colorkey_active)
4249 *convert = CONVERT_CK_RGB24;
4250 format->glFormat = GL_RGBA;
4251 format->glInternal = GL_RGBA8;
4252 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4253 format->conv_byte_count = 4;
4255 break;
4257 case WINED3DFMT_B8G8R8X8_UNORM:
4258 if (colorkey_active)
4260 *convert = CONVERT_RGB32_888;
4261 format->glFormat = GL_RGBA;
4262 format->glInternal = GL_RGBA8;
4263 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4264 format->conv_byte_count = 4;
4266 break;
4268 default:
4269 break;
4272 return WINED3D_OK;
4275 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4277 const struct wined3d_device *device = surface->resource.device;
4278 const struct wined3d_palette *pal = surface->palette;
4279 BOOL index_in_alpha = FALSE;
4280 unsigned int i;
4282 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4283 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4284 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4285 * duplicate entries. Store the color key in the unused alpha component to speed the
4286 * download up and to make conversion unneeded. */
4287 index_in_alpha = primary_render_target_is_p8(device);
4289 if (!pal)
4291 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
4292 if (device->wined3d->flags & WINED3D_PALETTE_PER_SURFACE)
4294 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4295 if (index_in_alpha)
4297 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4298 * there's no palette at this time. */
4299 for (i = 0; i < 256; i++) table[i][3] = i;
4302 else
4304 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
4305 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
4306 * capability flag is present (wine does advertise this capability) */
4307 for (i = 0; i < 256; ++i)
4309 table[i][0] = device->palettes[device->currentPalette][i].peRed;
4310 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
4311 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
4312 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
4316 else
4318 TRACE("Using surface palette %p\n", pal);
4319 /* Get the surface's palette */
4320 for (i = 0; i < 256; ++i)
4322 table[i][0] = pal->palents[i].peRed;
4323 table[i][1] = pal->palents[i].peGreen;
4324 table[i][2] = pal->palents[i].peBlue;
4326 /* When index_in_alpha is set the palette index is stored in the
4327 * alpha component. In case of a readback we can then read
4328 * GL_ALPHA. Color keying is handled in BltOverride using a
4329 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4330 * color key itself is passed to glAlphaFunc in other cases the
4331 * alpha component of pixels that should be masked away is set to 0. */
4332 if (index_in_alpha)
4334 table[i][3] = i;
4336 else if (colorkey && (i >= surface->SrcBltCKey.dwColorSpaceLowValue)
4337 && (i <= surface->SrcBltCKey.dwColorSpaceHighValue))
4339 table[i][3] = 0x00;
4341 else if (pal->flags & WINEDDPCAPS_ALPHA)
4343 table[i][3] = pal->palents[i].peFlags;
4345 else
4347 table[i][3] = 0xFF;
4353 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4354 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4356 const BYTE *source;
4357 BYTE *dest;
4358 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4360 switch (convert) {
4361 case NO_CONVERSION:
4363 memcpy(dst, src, pitch * height);
4364 break;
4366 case CONVERT_PALETTED:
4367 case CONVERT_PALETTED_CK:
4369 BYTE table[256][4];
4370 unsigned int x, y;
4372 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4374 for (y = 0; y < height; y++)
4376 source = src + pitch * y;
4377 dest = dst + outpitch * y;
4378 /* This is an 1 bpp format, using the width here is fine */
4379 for (x = 0; x < width; x++) {
4380 BYTE color = *source++;
4381 *dest++ = table[color][0];
4382 *dest++ = table[color][1];
4383 *dest++ = table[color][2];
4384 *dest++ = table[color][3];
4388 break;
4390 case CONVERT_CK_565:
4392 /* Converting the 565 format in 5551 packed to emulate color-keying.
4394 Note : in all these conversion, it would be best to average the averaging
4395 pixels to get the color of the pixel that will be color-keyed to
4396 prevent 'color bleeding'. This will be done later on if ever it is
4397 too visible.
4399 Note2: Nvidia documents say that their driver does not support alpha + color keying
4400 on the same surface and disables color keying in such a case
4402 unsigned int x, y;
4403 const WORD *Source;
4404 WORD *Dest;
4406 TRACE("Color keyed 565\n");
4408 for (y = 0; y < height; y++) {
4409 Source = (const WORD *)(src + y * pitch);
4410 Dest = (WORD *) (dst + y * outpitch);
4411 for (x = 0; x < width; x++ ) {
4412 WORD color = *Source++;
4413 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4414 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4415 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4416 *Dest |= 0x0001;
4417 Dest++;
4421 break;
4423 case CONVERT_CK_5551:
4425 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4426 unsigned int x, y;
4427 const WORD *Source;
4428 WORD *Dest;
4429 TRACE("Color keyed 5551\n");
4430 for (y = 0; y < height; y++) {
4431 Source = (const WORD *)(src + y * pitch);
4432 Dest = (WORD *) (dst + y * outpitch);
4433 for (x = 0; x < width; x++ ) {
4434 WORD color = *Source++;
4435 *Dest = color;
4436 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4437 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4438 *Dest |= (1 << 15);
4439 else
4440 *Dest &= ~(1 << 15);
4441 Dest++;
4445 break;
4447 case CONVERT_CK_RGB24:
4449 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4450 unsigned int x, y;
4451 for (y = 0; y < height; y++)
4453 source = src + pitch * y;
4454 dest = dst + outpitch * y;
4455 for (x = 0; x < width; x++) {
4456 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4457 DWORD dstcolor = color << 8;
4458 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4459 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4460 dstcolor |= 0xff;
4461 *(DWORD*)dest = dstcolor;
4462 source += 3;
4463 dest += 4;
4467 break;
4469 case CONVERT_RGB32_888:
4471 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4472 unsigned int x, y;
4473 for (y = 0; y < height; y++)
4475 source = src + pitch * y;
4476 dest = dst + outpitch * y;
4477 for (x = 0; x < width; x++) {
4478 DWORD color = 0xffffff & *(const DWORD*)source;
4479 DWORD dstcolor = color << 8;
4480 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4481 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4482 dstcolor |= 0xff;
4483 *(DWORD*)dest = dstcolor;
4484 source += 4;
4485 dest += 4;
4489 break;
4491 default:
4492 ERR("Unsupported conversion type %#x.\n", convert);
4494 return WINED3D_OK;
4497 BOOL palette9_changed(struct wined3d_surface *surface)
4499 struct wined3d_device *device = surface->resource.device;
4501 if (surface->palette || (surface->resource.format->id != WINED3DFMT_P8_UINT
4502 && surface->resource.format->id != WINED3DFMT_P8_UINT_A8_UNORM))
4504 /* If a ddraw-style palette is attached assume no d3d9 palette change.
4505 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
4507 return FALSE;
4510 if (surface->palette9)
4512 if (!memcmp(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
4514 return FALSE;
4517 else
4519 surface->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
4521 memcpy(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
4523 return TRUE;
4526 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4528 /* Flip the surface contents */
4529 /* Flip the DC */
4531 HDC tmp;
4532 tmp = front->hDC;
4533 front->hDC = back->hDC;
4534 back->hDC = tmp;
4537 /* Flip the DIBsection */
4539 HBITMAP tmp;
4540 BOOL hasDib = front->flags & SFLAG_DIBSECTION;
4541 tmp = front->dib.DIBsection;
4542 front->dib.DIBsection = back->dib.DIBsection;
4543 back->dib.DIBsection = tmp;
4545 if (back->flags & SFLAG_DIBSECTION) front->flags |= SFLAG_DIBSECTION;
4546 else front->flags &= ~SFLAG_DIBSECTION;
4547 if (hasDib) back->flags |= SFLAG_DIBSECTION;
4548 else back->flags &= ~SFLAG_DIBSECTION;
4551 /* Flip the surface data */
4553 void* tmp;
4555 tmp = front->dib.bitmap_data;
4556 front->dib.bitmap_data = back->dib.bitmap_data;
4557 back->dib.bitmap_data = tmp;
4559 tmp = front->resource.allocatedMemory;
4560 front->resource.allocatedMemory = back->resource.allocatedMemory;
4561 back->resource.allocatedMemory = tmp;
4563 tmp = front->resource.heapMemory;
4564 front->resource.heapMemory = back->resource.heapMemory;
4565 back->resource.heapMemory = tmp;
4568 /* Flip the PBO */
4570 GLuint tmp_pbo = front->pbo;
4571 front->pbo = back->pbo;
4572 back->pbo = tmp_pbo;
4575 /* client_memory should not be different, but just in case */
4577 BOOL tmp;
4578 tmp = front->dib.client_memory;
4579 front->dib.client_memory = back->dib.client_memory;
4580 back->dib.client_memory = tmp;
4583 /* Flip the opengl texture */
4585 GLuint tmp;
4587 tmp = back->texture_name;
4588 back->texture_name = front->texture_name;
4589 front->texture_name = tmp;
4591 tmp = back->texture_name_srgb;
4592 back->texture_name_srgb = front->texture_name_srgb;
4593 front->texture_name_srgb = tmp;
4595 resource_unload(&back->resource);
4596 resource_unload(&front->resource);
4600 DWORD tmp_flags = back->flags;
4601 back->flags = front->flags;
4602 front->flags = tmp_flags;
4606 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4607 * pixel copy calls. */
4608 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4609 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4611 struct wined3d_device *device = dst_surface->resource.device;
4612 float xrel, yrel;
4613 UINT row;
4614 struct wined3d_context *context;
4615 BOOL upsidedown = FALSE;
4616 RECT dst_rect = *dst_rect_in;
4618 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4619 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4621 if(dst_rect.top > dst_rect.bottom) {
4622 UINT tmp = dst_rect.bottom;
4623 dst_rect.bottom = dst_rect.top;
4624 dst_rect.top = tmp;
4625 upsidedown = TRUE;
4628 context = context_acquire(device, src_surface);
4629 context_apply_blit_state(context, device);
4630 surface_internal_preload(dst_surface, SRGB_RGB);
4631 ENTER_GL();
4633 /* Bind the target texture */
4634 glBindTexture(dst_surface->texture_target, dst_surface->texture_name);
4635 checkGLcall("glBindTexture");
4636 if (surface_is_offscreen(src_surface))
4638 TRACE("Reading from an offscreen target\n");
4639 upsidedown = !upsidedown;
4640 glReadBuffer(device->offscreenBuffer);
4642 else
4644 glReadBuffer(surface_get_gl_buffer(src_surface));
4646 checkGLcall("glReadBuffer");
4648 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4649 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4651 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4653 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4655 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4656 ERR("Texture filtering not supported in direct blit\n");
4659 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4660 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4662 ERR("Texture filtering not supported in direct blit\n");
4665 if (upsidedown
4666 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4667 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4669 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4671 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4672 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4673 src_rect->left, src_surface->resource.height - src_rect->bottom,
4674 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4676 else
4678 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4679 /* I have to process this row by row to swap the image,
4680 * otherwise it would be upside down, so stretching in y direction
4681 * doesn't cost extra time
4683 * However, stretching in x direction can be avoided if not necessary
4685 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4686 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4688 /* Well, that stuff works, but it's very slow.
4689 * find a better way instead
4691 UINT col;
4693 for (col = dst_rect.left; col < dst_rect.right; ++col)
4695 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4696 dst_rect.left + col /* x offset */, row /* y offset */,
4697 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4700 else
4702 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4703 dst_rect.left /* x offset */, row /* y offset */,
4704 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4708 checkGLcall("glCopyTexSubImage2D");
4710 LEAVE_GL();
4711 context_release(context);
4713 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4714 * path is never entered
4716 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4719 /* Uses the hardware to stretch and flip the image */
4720 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4721 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4723 struct wined3d_device *device = dst_surface->resource.device;
4724 struct wined3d_swapchain *src_swapchain = NULL;
4725 GLuint src, backup = 0;
4726 float left, right, top, bottom; /* Texture coordinates */
4727 UINT fbwidth = src_surface->resource.width;
4728 UINT fbheight = src_surface->resource.height;
4729 struct wined3d_context *context;
4730 GLenum drawBuffer = GL_BACK;
4731 GLenum texture_target;
4732 BOOL noBackBufferBackup;
4733 BOOL src_offscreen;
4734 BOOL upsidedown = FALSE;
4735 RECT dst_rect = *dst_rect_in;
4737 TRACE("Using hwstretch blit\n");
4738 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4739 context = context_acquire(device, src_surface);
4740 context_apply_blit_state(context, device);
4741 surface_internal_preload(dst_surface, SRGB_RGB);
4743 src_offscreen = surface_is_offscreen(src_surface);
4744 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4745 if (!noBackBufferBackup && !src_surface->texture_name)
4747 /* Get it a description */
4748 surface_internal_preload(src_surface, SRGB_RGB);
4750 ENTER_GL();
4752 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4753 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4755 if (context->aux_buffers >= 2)
4757 /* Got more than one aux buffer? Use the 2nd aux buffer */
4758 drawBuffer = GL_AUX1;
4760 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4762 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4763 drawBuffer = GL_AUX0;
4766 if(noBackBufferBackup) {
4767 glGenTextures(1, &backup);
4768 checkGLcall("glGenTextures");
4769 glBindTexture(GL_TEXTURE_2D, backup);
4770 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
4771 texture_target = GL_TEXTURE_2D;
4772 } else {
4773 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4774 * we are reading from the back buffer, the backup can be used as source texture
4776 texture_target = src_surface->texture_target;
4777 glBindTexture(texture_target, src_surface->texture_name);
4778 checkGLcall("glBindTexture(texture_target, src_surface->texture_name)");
4779 glEnable(texture_target);
4780 checkGLcall("glEnable(texture_target)");
4782 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4783 src_surface->flags &= ~SFLAG_INTEXTURE;
4786 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4787 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4789 if(dst_rect.top > dst_rect.bottom) {
4790 UINT tmp = dst_rect.bottom;
4791 dst_rect.bottom = dst_rect.top;
4792 dst_rect.top = tmp;
4793 upsidedown = TRUE;
4796 if (src_offscreen)
4798 TRACE("Reading from an offscreen target\n");
4799 upsidedown = !upsidedown;
4800 glReadBuffer(device->offscreenBuffer);
4802 else
4804 glReadBuffer(surface_get_gl_buffer(src_surface));
4807 /* TODO: Only back up the part that will be overwritten */
4808 glCopyTexSubImage2D(texture_target, 0,
4809 0, 0 /* read offsets */,
4810 0, 0,
4811 fbwidth,
4812 fbheight);
4814 checkGLcall("glCopyTexSubImage2D");
4816 /* No issue with overriding these - the sampler is dirty due to blit usage */
4817 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
4818 wined3d_gl_mag_filter(magLookup, Filter));
4819 checkGLcall("glTexParameteri");
4820 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
4821 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
4822 checkGLcall("glTexParameteri");
4824 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
4825 src_swapchain = src_surface->container.u.swapchain;
4826 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
4828 src = backup ? backup : src_surface->texture_name;
4830 else
4832 glReadBuffer(GL_FRONT);
4833 checkGLcall("glReadBuffer(GL_FRONT)");
4835 glGenTextures(1, &src);
4836 checkGLcall("glGenTextures(1, &src)");
4837 glBindTexture(GL_TEXTURE_2D, src);
4838 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
4840 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
4841 * out for power of 2 sizes
4843 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
4844 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
4845 checkGLcall("glTexImage2D");
4846 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
4847 0, 0 /* read offsets */,
4848 0, 0,
4849 fbwidth,
4850 fbheight);
4852 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4853 checkGLcall("glTexParameteri");
4854 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4855 checkGLcall("glTexParameteri");
4857 glReadBuffer(GL_BACK);
4858 checkGLcall("glReadBuffer(GL_BACK)");
4860 if(texture_target != GL_TEXTURE_2D) {
4861 glDisable(texture_target);
4862 glEnable(GL_TEXTURE_2D);
4863 texture_target = GL_TEXTURE_2D;
4866 checkGLcall("glEnd and previous");
4868 left = src_rect->left;
4869 right = src_rect->right;
4871 if (!upsidedown)
4873 top = src_surface->resource.height - src_rect->top;
4874 bottom = src_surface->resource.height - src_rect->bottom;
4876 else
4878 top = src_surface->resource.height - src_rect->bottom;
4879 bottom = src_surface->resource.height - src_rect->top;
4882 if (src_surface->flags & SFLAG_NORMCOORD)
4884 left /= src_surface->pow2Width;
4885 right /= src_surface->pow2Width;
4886 top /= src_surface->pow2Height;
4887 bottom /= src_surface->pow2Height;
4890 /* draw the source texture stretched and upside down. The correct surface is bound already */
4891 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
4892 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
4894 context_set_draw_buffer(context, drawBuffer);
4895 glReadBuffer(drawBuffer);
4897 glBegin(GL_QUADS);
4898 /* bottom left */
4899 glTexCoord2f(left, bottom);
4900 glVertex2i(0, 0);
4902 /* top left */
4903 glTexCoord2f(left, top);
4904 glVertex2i(0, dst_rect.bottom - dst_rect.top);
4906 /* top right */
4907 glTexCoord2f(right, top);
4908 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4910 /* bottom right */
4911 glTexCoord2f(right, bottom);
4912 glVertex2i(dst_rect.right - dst_rect.left, 0);
4913 glEnd();
4914 checkGLcall("glEnd and previous");
4916 if (texture_target != dst_surface->texture_target)
4918 glDisable(texture_target);
4919 glEnable(dst_surface->texture_target);
4920 texture_target = dst_surface->texture_target;
4923 /* Now read the stretched and upside down image into the destination texture */
4924 glBindTexture(texture_target, dst_surface->texture_name);
4925 checkGLcall("glBindTexture");
4926 glCopyTexSubImage2D(texture_target,
4928 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
4929 0, 0, /* We blitted the image to the origin */
4930 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4931 checkGLcall("glCopyTexSubImage2D");
4933 if(drawBuffer == GL_BACK) {
4934 /* Write the back buffer backup back */
4935 if(backup) {
4936 if(texture_target != GL_TEXTURE_2D) {
4937 glDisable(texture_target);
4938 glEnable(GL_TEXTURE_2D);
4939 texture_target = GL_TEXTURE_2D;
4941 glBindTexture(GL_TEXTURE_2D, backup);
4942 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
4944 else
4946 if (texture_target != src_surface->texture_target)
4948 glDisable(texture_target);
4949 glEnable(src_surface->texture_target);
4950 texture_target = src_surface->texture_target;
4952 glBindTexture(src_surface->texture_target, src_surface->texture_name);
4953 checkGLcall("glBindTexture(src_surface->texture_target, src_surface->texture_name)");
4956 glBegin(GL_QUADS);
4957 /* top left */
4958 glTexCoord2f(0.0f, 0.0f);
4959 glVertex2i(0, fbheight);
4961 /* bottom left */
4962 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
4963 glVertex2i(0, 0);
4965 /* bottom right */
4966 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
4967 (float)fbheight / (float)src_surface->pow2Height);
4968 glVertex2i(fbwidth, 0);
4970 /* top right */
4971 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
4972 glVertex2i(fbwidth, fbheight);
4973 glEnd();
4975 glDisable(texture_target);
4976 checkGLcall("glDisable(texture_target)");
4978 /* Cleanup */
4979 if (src != src_surface->texture_name && src != backup)
4981 glDeleteTextures(1, &src);
4982 checkGLcall("glDeleteTextures(1, &src)");
4984 if(backup) {
4985 glDeleteTextures(1, &backup);
4986 checkGLcall("glDeleteTextures(1, &backup)");
4989 LEAVE_GL();
4991 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
4993 context_release(context);
4995 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4996 * path is never entered
4998 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5001 /* Front buffer coordinates are always full screen coordinates, but our GL
5002 * drawable is limited to the window's client area. The sysmem and texture
5003 * copies do have the full screen size. Note that GL has a bottom-left
5004 * origin, while D3D has a top-left origin. */
5005 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5007 UINT drawable_height;
5009 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5010 && surface == surface->container.u.swapchain->front_buffer)
5012 POINT offset = {0, 0};
5013 RECT windowsize;
5015 ScreenToClient(window, &offset);
5016 OffsetRect(rect, offset.x, offset.y);
5018 GetClientRect(window, &windowsize);
5019 drawable_height = windowsize.bottom - windowsize.top;
5021 else
5023 drawable_height = surface->resource.height;
5026 rect->top = drawable_height - rect->top;
5027 rect->bottom = drawable_height - rect->bottom;
5030 /* blit between surface locations. onscreen on different swapchains is not supported.
5031 * depth / stencil is not supported. */
5032 static void surface_blt_fbo(struct wined3d_device *device, const WINED3DTEXTUREFILTERTYPE filter,
5033 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
5034 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
5036 const struct wined3d_gl_info *gl_info;
5037 struct wined3d_context *context;
5038 RECT src_rect, dst_rect;
5039 GLenum gl_filter;
5040 GLenum buffer;
5042 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
5043 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
5044 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
5045 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
5046 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
5048 src_rect = *src_rect_in;
5049 dst_rect = *dst_rect_in;
5051 switch (filter)
5053 case WINED3DTEXF_LINEAR:
5054 gl_filter = GL_LINEAR;
5055 break;
5057 default:
5058 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
5059 case WINED3DTEXF_NONE:
5060 case WINED3DTEXF_POINT:
5061 gl_filter = GL_NEAREST;
5062 break;
5065 if (src_location == SFLAG_INDRAWABLE && surface_is_offscreen(src_surface))
5066 src_location = SFLAG_INTEXTURE;
5067 if (dst_location == SFLAG_INDRAWABLE && surface_is_offscreen(dst_surface))
5068 dst_location = SFLAG_INTEXTURE;
5070 /* Make sure the locations are up-to-date. Loading the destination
5071 * surface isn't required if the entire surface is overwritten. (And is
5072 * in fact harmful if we're being called by surface_load_location() with
5073 * the purpose of loading the destination surface.) */
5074 surface_load_location(src_surface, src_location, NULL);
5075 if (!surface_is_full_rect(dst_surface, &dst_rect))
5076 surface_load_location(dst_surface, dst_location, NULL);
5078 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
5079 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
5080 else context = context_acquire(device, NULL);
5082 if (!context->valid)
5084 context_release(context);
5085 WARN("Invalid context, skipping blit.\n");
5086 return;
5089 gl_info = context->gl_info;
5091 if (src_location == SFLAG_INDRAWABLE)
5093 TRACE("Source surface %p is onscreen.\n", src_surface);
5094 buffer = surface_get_gl_buffer(src_surface);
5095 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
5097 else
5099 TRACE("Source surface %p is offscreen.\n", src_surface);
5100 buffer = GL_COLOR_ATTACHMENT0;
5103 ENTER_GL();
5104 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
5105 glReadBuffer(buffer);
5106 checkGLcall("glReadBuffer()");
5107 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
5108 LEAVE_GL();
5110 if (dst_location == SFLAG_INDRAWABLE)
5112 TRACE("Destination surface %p is onscreen.\n", dst_surface);
5113 buffer = surface_get_gl_buffer(dst_surface);
5114 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5116 else
5118 TRACE("Destination surface %p is offscreen.\n", dst_surface);
5119 buffer = GL_COLOR_ATTACHMENT0;
5122 ENTER_GL();
5123 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
5124 context_set_draw_buffer(context, buffer);
5125 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
5126 context_invalidate_state(context, STATE_FRAMEBUFFER);
5128 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
5129 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE));
5130 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE1));
5131 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE2));
5132 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE3));
5134 glDisable(GL_SCISSOR_TEST);
5135 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
5137 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
5138 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
5139 checkGLcall("glBlitFramebuffer()");
5141 LEAVE_GL();
5143 if (wined3d_settings.strict_draw_ordering
5144 || (dst_location == SFLAG_INDRAWABLE
5145 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
5146 wglFlush();
5148 context_release(context);
5151 static void surface_blt_to_drawable(struct wined3d_device *device,
5152 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5153 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5154 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5156 struct wined3d_context *context;
5157 RECT src_rect, dst_rect;
5159 src_rect = *src_rect_in;
5160 dst_rect = *dst_rect_in;
5162 /* Make sure the surface is up-to-date. This should probably use
5163 * surface_load_location() and worry about the destination surface too,
5164 * unless we're overwriting it completely. */
5165 surface_internal_preload(src_surface, SRGB_RGB);
5167 /* Activate the destination context, set it up for blitting */
5168 context = context_acquire(device, dst_surface);
5169 context_apply_blit_state(context, device);
5171 if (!surface_is_offscreen(dst_surface))
5172 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5174 device->blitter->set_shader(device->blit_priv, context->gl_info, src_surface);
5176 ENTER_GL();
5178 if (color_key)
5180 glEnable(GL_ALPHA_TEST);
5181 checkGLcall("glEnable(GL_ALPHA_TEST)");
5183 /* When the primary render target uses P8, the alpha component
5184 * contains the palette index. Which means that the colorkey is one of
5185 * the palette entries. In other cases pixels that should be masked
5186 * away have alpha set to 0. */
5187 if (primary_render_target_is_p8(device))
5188 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
5189 else
5190 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5191 checkGLcall("glAlphaFunc");
5193 else
5195 glDisable(GL_ALPHA_TEST);
5196 checkGLcall("glDisable(GL_ALPHA_TEST)");
5199 draw_textured_quad(src_surface, &src_rect, &dst_rect, filter);
5201 if (color_key)
5203 glDisable(GL_ALPHA_TEST);
5204 checkGLcall("glDisable(GL_ALPHA_TEST)");
5207 LEAVE_GL();
5209 /* Leave the opengl state valid for blitting */
5210 device->blitter->unset_shader(context->gl_info);
5212 if (wined3d_settings.strict_draw_ordering
5213 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5214 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5215 wglFlush(); /* Flush to ensure ordering across contexts. */
5217 context_release(context);
5220 /* Do not call while under the GL lock. */
5221 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const WINED3DCOLORVALUE *color)
5223 struct wined3d_device *device = s->resource.device;
5224 const struct blit_shader *blitter;
5226 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5227 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5228 if (!blitter)
5230 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5231 return WINED3DERR_INVALIDCALL;
5234 return blitter->color_fill(device, s, rect, color);
5237 /* Do not call while under the GL lock. */
5238 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5239 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5240 WINED3DTEXTUREFILTERTYPE Filter)
5242 struct wined3d_device *device = dst_surface->resource.device;
5243 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5244 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5246 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5247 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5248 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5250 /* Get the swapchain. One of the surfaces has to be a primary surface */
5251 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5253 WARN("Destination is in sysmem, rejecting gl blt\n");
5254 return WINED3DERR_INVALIDCALL;
5257 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5258 dstSwapchain = dst_surface->container.u.swapchain;
5260 if (src_surface)
5262 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5264 WARN("Src is in sysmem, rejecting gl blt\n");
5265 return WINED3DERR_INVALIDCALL;
5268 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5269 srcSwapchain = src_surface->container.u.swapchain;
5272 /* Early sort out of cases where no render target is used */
5273 if (!dstSwapchain && !srcSwapchain
5274 && src_surface != device->fb.render_targets[0]
5275 && dst_surface != device->fb.render_targets[0])
5277 TRACE("No surface is render target, not using hardware blit.\n");
5278 return WINED3DERR_INVALIDCALL;
5281 /* No destination color keying supported */
5282 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5284 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5285 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5286 return WINED3DERR_INVALIDCALL;
5289 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
5290 if (dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->back_buffers
5291 && dst_surface == dstSwapchain->front_buffer
5292 && src_surface == dstSwapchain->back_buffers[0])
5294 /* Half-Life does a Blt from the back buffer to the front buffer,
5295 * Full surface size, no flags... Use present instead
5297 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
5300 /* Check rects - wined3d_swapchain_present() doesn't handle them. */
5301 for (;;)
5303 TRACE("Looking if a Present can be done...\n");
5304 /* Source Rectangle must be full surface */
5305 if (src_rect->left || src_rect->top
5306 || src_rect->right != src_surface->resource.width
5307 || src_rect->bottom != src_surface->resource.height)
5309 TRACE("No, Source rectangle doesn't match\n");
5310 break;
5313 /* No stretching may occur */
5314 if (src_rect->right != dst_rect->right - dst_rect->left
5315 || src_rect->bottom != dst_rect->bottom - dst_rect->top)
5317 TRACE("No, stretching is done\n");
5318 break;
5321 /* Destination must be full surface or match the clipping rectangle */
5322 if (dst_surface->clipper && dst_surface->clipper->hWnd)
5324 RECT cliprect;
5325 POINT pos[2];
5326 GetClientRect(dst_surface->clipper->hWnd, &cliprect);
5327 pos[0].x = dst_rect->left;
5328 pos[0].y = dst_rect->top;
5329 pos[1].x = dst_rect->right;
5330 pos[1].y = dst_rect->bottom;
5331 MapWindowPoints(GetDesktopWindow(), dst_surface->clipper->hWnd, pos, 2);
5333 if (pos[0].x != cliprect.left || pos[0].y != cliprect.top
5334 || pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
5336 TRACE("No, dest rectangle doesn't match(clipper)\n");
5337 TRACE("Clip rect at %s\n", wine_dbgstr_rect(&cliprect));
5338 TRACE("Blt dest: %s\n", wine_dbgstr_rect(dst_rect));
5339 break;
5342 else if (dst_rect->left || dst_rect->top
5343 || dst_rect->right != dst_surface->resource.width
5344 || dst_rect->bottom != dst_surface->resource.height)
5346 TRACE("No, dest rectangle doesn't match(surface size)\n");
5347 break;
5350 TRACE("Yes\n");
5352 /* These flags are unimportant for the flag check, remove them */
5353 if (!(flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)))
5355 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
5357 /* The idea behind this is that a glReadPixels and a glDrawPixels call
5358 * take very long, while a flip is fast.
5359 * This applies to Half-Life, which does such Blts every time it finished
5360 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
5361 * menu. This is also used by all apps when they do windowed rendering
5363 * The problem is that flipping is not really the same as copying. After a
5364 * Blt the front buffer is a copy of the back buffer, and the back buffer is
5365 * untouched. Therefore it's necessary to override the swap effect
5366 * and to set it back after the flip.
5368 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
5369 * testcases.
5372 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
5373 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
5375 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead.\n");
5376 wined3d_swapchain_present(dstSwapchain, NULL, NULL, dstSwapchain->win_handle, NULL, 0);
5378 dstSwapchain->presentParms.SwapEffect = orig_swap;
5380 return WINED3D_OK;
5382 break;
5385 TRACE("Unsupported blit between buffers on the same swapchain\n");
5386 return WINED3DERR_INVALIDCALL;
5387 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
5388 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5389 return WINED3DERR_INVALIDCALL;
5390 } else if(dstSwapchain && srcSwapchain) {
5391 FIXME("Implement hardware blit between two different swapchains\n");
5392 return WINED3DERR_INVALIDCALL;
5394 else if (dstSwapchain)
5396 /* Handled with regular texture -> swapchain blit */
5397 if (src_surface == device->fb.render_targets[0])
5398 TRACE("Blit from active render target to a swapchain\n");
5400 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5402 FIXME("Implement blit from a swapchain to the active render target\n");
5403 return WINED3DERR_INVALIDCALL;
5406 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5408 /* Blit from render target to texture */
5409 BOOL stretchx;
5411 /* P8 read back is not implemented */
5412 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5413 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5415 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5416 return WINED3DERR_INVALIDCALL;
5419 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5421 TRACE("Color keying not supported by frame buffer to texture blit\n");
5422 return WINED3DERR_INVALIDCALL;
5423 /* Destination color key is checked above */
5426 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5427 stretchx = TRUE;
5428 else
5429 stretchx = FALSE;
5431 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5432 * flip the image nor scale it.
5434 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5435 * -> If the app wants a image width an unscaled width, copy it line per line
5436 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5437 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5438 * back buffer. This is slower than reading line per line, thus not used for flipping
5439 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5440 * pixel by pixel
5442 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
5443 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
5444 * backends. */
5445 if (fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5446 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5447 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5449 surface_blt_fbo(device, Filter,
5450 src_surface, SFLAG_INDRAWABLE, src_rect,
5451 dst_surface, SFLAG_INDRAWABLE, dst_rect);
5452 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
5454 else if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5455 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5457 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5458 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, Filter);
5459 } else {
5460 TRACE("Using hardware stretching to flip / stretch the texture\n");
5461 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5464 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5466 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5467 dst_surface->resource.allocatedMemory = NULL;
5468 dst_surface->resource.heapMemory = NULL;
5470 else
5472 dst_surface->flags &= ~SFLAG_INSYSMEM;
5475 return WINED3D_OK;
5477 else if (src_surface)
5479 /* Blit from offscreen surface to render target */
5480 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5481 WINEDDCOLORKEY oldBltCKey = src_surface->SrcBltCKey;
5483 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5485 if (!(flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5486 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5487 src_rect, src_surface->resource.usage, src_surface->resource.pool,
5488 src_surface->resource.format,
5489 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool,
5490 dst_surface->resource.format))
5492 TRACE("Using surface_blt_fbo.\n");
5493 /* The source is always a texture, but never the currently active render target, and the texture
5494 * contents are never upside down. */
5495 surface_blt_fbo(device, Filter,
5496 src_surface, SFLAG_INDRAWABLE, src_rect,
5497 dst_surface, SFLAG_INDRAWABLE, dst_rect);
5498 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
5499 return WINED3D_OK;
5502 if (!(flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5503 && arbfp_blit.blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5504 src_rect, src_surface->resource.usage, src_surface->resource.pool,
5505 src_surface->resource.format,
5506 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool,
5507 dst_surface->resource.format))
5508 return arbfp_blit_surface(device, Filter, src_surface, src_rect, dst_surface, dst_rect);
5510 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5511 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5512 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5514 FIXME("Unsupported blit operation falling back to software\n");
5515 return WINED3DERR_INVALIDCALL;
5518 /* Color keying: Check if we have to do a color keyed blt,
5519 * and if not check if a color key is activated.
5521 * Just modify the color keying parameters in the surface and restore them afterwards
5522 * The surface keeps track of the color key last used to load the opengl surface.
5523 * PreLoad will catch the change to the flags and color key and reload if necessary.
5525 if (flags & WINEDDBLT_KEYSRC)
5527 /* Use color key from surface */
5529 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5531 /* Use color key from DDBltFx */
5532 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5533 src_surface->SrcBltCKey = DDBltFx->ddckSrcColorkey;
5535 else
5537 /* Do not use color key */
5538 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5541 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5542 src_surface, src_rect, dst_surface, dst_rect);
5544 /* Restore the color key parameters */
5545 src_surface->CKeyFlags = oldCKeyFlags;
5546 src_surface->SrcBltCKey = oldBltCKey;
5548 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
5550 return WINED3D_OK;
5552 else
5554 /* Source-Less Blit to render target */
5555 if (flags & WINEDDBLT_COLORFILL)
5557 WINED3DCOLORVALUE color;
5559 TRACE("Colorfill\n");
5561 /* The color as given in the Blt function is in the surface format. */
5562 if (!surface_convert_color_to_float(dst_surface, DDBltFx->u5.dwFillColor, &color))
5563 return WINED3DERR_INVALIDCALL;
5565 return surface_color_fill(dst_surface, dst_rect, &color);
5569 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5570 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5571 return WINED3DERR_INVALIDCALL;
5574 /* GL locking is done by the caller */
5575 static void surface_depth_blt(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
5576 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5578 struct wined3d_device *device = surface->resource.device;
5579 GLint compare_mode = GL_NONE;
5580 struct blt_info info;
5581 GLint old_binding = 0;
5582 RECT rect;
5584 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5586 glDisable(GL_CULL_FACE);
5587 glDisable(GL_BLEND);
5588 glDisable(GL_ALPHA_TEST);
5589 glDisable(GL_SCISSOR_TEST);
5590 glDisable(GL_STENCIL_TEST);
5591 glEnable(GL_DEPTH_TEST);
5592 glDepthFunc(GL_ALWAYS);
5593 glDepthMask(GL_TRUE);
5594 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5595 glViewport(x, y, w, h);
5597 SetRect(&rect, 0, h, w, 0);
5598 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5599 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
5600 glGetIntegerv(info.binding, &old_binding);
5601 glBindTexture(info.bind_target, texture);
5602 if (gl_info->supported[ARB_SHADOW])
5604 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5605 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5608 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5609 gl_info, info.tex_type, &surface->ds_current_size);
5611 glBegin(GL_TRIANGLE_STRIP);
5612 glTexCoord3fv(info.coords[0]);
5613 glVertex2f(-1.0f, -1.0f);
5614 glTexCoord3fv(info.coords[1]);
5615 glVertex2f(1.0f, -1.0f);
5616 glTexCoord3fv(info.coords[2]);
5617 glVertex2f(-1.0f, 1.0f);
5618 glTexCoord3fv(info.coords[3]);
5619 glVertex2f(1.0f, 1.0f);
5620 glEnd();
5622 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5623 glBindTexture(info.bind_target, old_binding);
5625 glPopAttrib();
5627 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5630 void surface_modify_ds_location(struct wined3d_surface *surface,
5631 DWORD location, UINT w, UINT h)
5633 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5635 if (location & ~SFLAG_DS_LOCATIONS)
5636 FIXME("Invalid location (%#x) specified.\n", location);
5638 surface->ds_current_size.cx = w;
5639 surface->ds_current_size.cy = h;
5640 surface->flags &= ~SFLAG_DS_LOCATIONS;
5641 surface->flags |= location;
5644 /* Context activation is done by the caller. */
5645 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5647 struct wined3d_device *device = surface->resource.device;
5648 const struct wined3d_gl_info *gl_info = context->gl_info;
5649 GLsizei w, h;
5651 TRACE("surface %p, new location %#x.\n", surface, location);
5653 /* TODO: Make this work for modes other than FBO */
5654 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5656 if (!(surface->flags & location))
5658 w = surface->ds_current_size.cx;
5659 h = surface->ds_current_size.cy;
5660 surface->ds_current_size.cx = 0;
5661 surface->ds_current_size.cy = 0;
5663 else
5665 w = surface->resource.width;
5666 h = surface->resource.height;
5669 if (surface->ds_current_size.cx == surface->resource.width
5670 && surface->ds_current_size.cy == surface->resource.height)
5672 TRACE("Location (%#x) is already up to date.\n", location);
5673 return;
5676 if (surface->current_renderbuffer)
5678 FIXME("Not supported with fixed up depth stencil.\n");
5679 return;
5682 if (!(surface->flags & SFLAG_DS_LOCATIONS))
5684 /* This mostly happens when a depth / stencil is used without being
5685 * cleared first. In principle we could upload from sysmem, or
5686 * explicitly clear before first usage. For the moment there don't
5687 * appear to be a lot of applications depending on this, so a FIXME
5688 * should do. */
5689 FIXME("No up to date depth stencil location.\n");
5690 surface->flags |= location;
5691 surface->ds_current_size.cx = surface->resource.width;
5692 surface->ds_current_size.cy = surface->resource.height;
5693 return;
5696 if (location == SFLAG_DS_OFFSCREEN)
5698 GLint old_binding = 0;
5699 GLenum bind_target;
5701 /* The render target is allowed to be smaller than the depth/stencil
5702 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5703 * than the offscreen surface. Don't overwrite the offscreen surface
5704 * with undefined data. */
5705 w = min(w, context->swapchain->presentParms.BackBufferWidth);
5706 h = min(h, context->swapchain->presentParms.BackBufferHeight);
5708 TRACE("Copying onscreen depth buffer to depth texture.\n");
5710 ENTER_GL();
5712 if (!device->depth_blt_texture)
5714 glGenTextures(1, &device->depth_blt_texture);
5717 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5718 * directly on the FBO texture. That's because we need to flip. */
5719 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5720 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5721 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5723 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5724 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5726 else
5728 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5729 bind_target = GL_TEXTURE_2D;
5731 glBindTexture(bind_target, device->depth_blt_texture);
5732 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5733 * internal format, because the internal format might include stencil
5734 * data. In principle we should copy stencil data as well, but unless
5735 * the driver supports stencil export it's hard to do, and doesn't
5736 * seem to be needed in practice. If the hardware doesn't support
5737 * writing stencil data, the glCopyTexImage2D() call might trigger
5738 * software fallbacks. */
5739 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5740 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5741 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5742 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5743 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5744 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5745 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5746 glBindTexture(bind_target, old_binding);
5748 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5749 NULL, surface, SFLAG_INTEXTURE);
5750 context_set_draw_buffer(context, GL_NONE);
5751 glReadBuffer(GL_NONE);
5753 /* Do the actual blit */
5754 surface_depth_blt(surface, gl_info, device->depth_blt_texture, 0, 0, w, h, bind_target);
5755 checkGLcall("depth_blt");
5757 context_invalidate_state(context, STATE_FRAMEBUFFER);
5759 LEAVE_GL();
5761 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5763 else if (location == SFLAG_DS_ONSCREEN)
5765 TRACE("Copying depth texture to onscreen depth buffer.\n");
5767 ENTER_GL();
5769 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5770 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5771 surface_depth_blt(surface, gl_info, surface->texture_name,
5772 0, surface->pow2Height - h, w, h, surface->texture_target);
5773 checkGLcall("depth_blt");
5775 context_invalidate_state(context, STATE_FRAMEBUFFER);
5777 LEAVE_GL();
5779 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5781 else
5783 ERR("Invalid location (%#x) specified.\n", location);
5786 surface->flags |= location;
5787 surface->ds_current_size.cx = surface->resource.width;
5788 surface->ds_current_size.cy = surface->resource.height;
5791 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5793 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5794 struct wined3d_surface *overlay;
5796 TRACE("surface %p, location %s, persistent %#x.\n",
5797 surface, debug_surflocation(location), persistent);
5799 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5801 if (surface_is_offscreen(surface))
5803 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same
5804 * for offscreen targets. */
5805 if (location & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))
5806 location |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
5808 else
5810 TRACE("Surface %p is an onscreen surface.\n", surface);
5814 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5815 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5816 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5818 if (persistent)
5820 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5821 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5823 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5825 TRACE("Passing to container.\n");
5826 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5829 surface->flags &= ~SFLAG_LOCATIONS;
5830 surface->flags |= location;
5832 /* Redraw emulated overlays, if any */
5833 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5835 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5837 overlay->surface_ops->surface_draw_overlay(overlay);
5841 else
5843 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5845 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5847 TRACE("Passing to container\n");
5848 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5851 surface->flags &= ~location;
5854 if (!(surface->flags & SFLAG_LOCATIONS))
5856 ERR("Surface %p does not have any up to date location.\n", surface);
5860 static DWORD resource_access_from_location(DWORD location)
5862 switch (location)
5864 case SFLAG_INSYSMEM:
5865 return WINED3D_RESOURCE_ACCESS_CPU;
5867 case SFLAG_INDRAWABLE:
5868 case SFLAG_INSRGBTEX:
5869 case SFLAG_INTEXTURE:
5870 return WINED3D_RESOURCE_ACCESS_GPU;
5872 default:
5873 FIXME("Unhandled location %#x.\n", location);
5874 return 0;
5878 static void surface_load_sysmem(struct wined3d_surface *surface,
5879 const struct wined3d_gl_info *gl_info, const RECT *rect)
5881 surface_prepare_system_memory(surface);
5883 /* Download the surface to system memory. */
5884 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5886 struct wined3d_device *device = surface->resource.device;
5887 struct wined3d_context *context = NULL;
5889 if (!device->isInDraw)
5890 context = context_acquire(device, NULL);
5892 surface_bind_and_dirtify(surface, gl_info, !(surface->flags & SFLAG_INTEXTURE));
5893 surface_download_data(surface, gl_info);
5895 if (context)
5896 context_release(context);
5898 return;
5901 /* Note: It might be faster to download into a texture first. */
5902 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5903 wined3d_surface_get_pitch(surface));
5906 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5907 const struct wined3d_gl_info *gl_info, const RECT *rect)
5909 struct wined3d_device *device = surface->resource.device;
5910 struct wined3d_format format;
5911 CONVERT_TYPES convert;
5912 UINT byte_count;
5913 BYTE *mem;
5915 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5916 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5918 if (surface->flags & SFLAG_INTEXTURE)
5920 RECT r;
5922 surface_get_rect(surface, rect, &r);
5923 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5925 return WINED3D_OK;
5928 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5930 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5931 * path through sysmem. */
5932 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5935 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5937 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5938 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5939 * called. */
5940 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5942 struct wined3d_context *context = NULL;
5944 TRACE("Removing the pbo attached to surface %p.\n", surface);
5946 if (!device->isInDraw)
5947 context = context_acquire(device, NULL);
5949 surface_remove_pbo(surface, gl_info);
5951 if (context)
5952 context_release(context);
5955 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5957 UINT height = surface->resource.height;
5958 UINT width = surface->resource.width;
5959 UINT src_pitch, dst_pitch;
5961 byte_count = format.conv_byte_count;
5962 src_pitch = wined3d_surface_get_pitch(surface);
5964 /* Stick to the alignment for the converted surface too, makes it
5965 * easier to load the surface. */
5966 dst_pitch = width * byte_count;
5967 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5969 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5971 ERR("Out of memory (%u).\n", dst_pitch * height);
5972 return E_OUTOFMEMORY;
5975 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5976 src_pitch, width, height, dst_pitch, convert, surface);
5978 surface->flags |= SFLAG_CONVERTED;
5980 else
5982 surface->flags &= ~SFLAG_CONVERTED;
5983 mem = surface->resource.allocatedMemory;
5984 byte_count = format.byte_count;
5987 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5989 /* Don't delete PBO memory. */
5990 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5991 HeapFree(GetProcessHeap(), 0, mem);
5993 return WINED3D_OK;
5996 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5997 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5999 const DWORD attach_flags = WINED3DFMT_FLAG_FBO_ATTACHABLE | WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB;
6000 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
6001 struct wined3d_device *device = surface->resource.device;
6002 struct wined3d_context *context = NULL;
6003 UINT width, src_pitch, dst_pitch;
6004 struct wined3d_bo_address data;
6005 struct wined3d_format format;
6006 POINT dst_point = {0, 0};
6007 CONVERT_TYPES convert;
6008 BYTE *mem;
6010 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
6011 && surface_is_offscreen(surface)
6012 && (surface->flags & SFLAG_INDRAWABLE))
6014 read_from_framebuffer_texture(surface, srgb);
6016 return WINED3D_OK;
6019 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6020 && (surface->resource.format->flags & attach_flags) == attach_flags
6021 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6022 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6023 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6025 if (srgb)
6026 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
6027 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6028 else
6029 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
6030 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6032 return WINED3D_OK;
6035 /* Upload from system memory */
6037 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6038 TRUE /* We will use textures */, &format, &convert);
6040 if (srgb)
6042 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6044 /* Performance warning... */
6045 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6046 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6049 else
6051 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6053 /* Performance warning... */
6054 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6055 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6059 if (!(surface->flags & SFLAG_INSYSMEM))
6061 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6062 /* Lets hope we get it from somewhere... */
6063 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6066 if (!device->isInDraw)
6067 context = context_acquire(device, NULL);
6069 surface_prepare_texture(surface, gl_info, srgb);
6070 surface_bind_and_dirtify(surface, gl_info, srgb);
6072 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6074 surface->flags |= SFLAG_GLCKEY;
6075 surface->glCKey = surface->SrcBltCKey;
6077 else surface->flags &= ~SFLAG_GLCKEY;
6079 width = surface->resource.width;
6080 src_pitch = wined3d_surface_get_pitch(surface);
6082 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6083 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6084 * called. */
6085 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6087 TRACE("Removing the pbo attached to surface %p.\n", surface);
6088 surface_remove_pbo(surface, gl_info);
6091 if (format.convert)
6093 /* This code is entered for texture formats which need a fixup. */
6094 UINT height = surface->resource.height;
6096 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6097 dst_pitch = width * format.conv_byte_count;
6098 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6100 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6102 ERR("Out of memory (%u).\n", dst_pitch * height);
6103 if (context)
6104 context_release(context);
6105 return E_OUTOFMEMORY;
6107 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6109 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6111 /* This code is only entered for color keying fixups */
6112 UINT height = surface->resource.height;
6114 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6115 dst_pitch = width * format.conv_byte_count;
6116 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6118 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6120 ERR("Out of memory (%u).\n", dst_pitch * height);
6121 if (context)
6122 context_release(context);
6123 return E_OUTOFMEMORY;
6125 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6126 width, height, dst_pitch, convert, surface);
6128 else
6130 mem = surface->resource.allocatedMemory;
6133 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6134 data.addr = mem;
6135 surface_upload_data(surface, gl_info, &format, &src_rect, width, &dst_point, srgb, &data);
6137 if (context)
6138 context_release(context);
6140 /* Don't delete PBO memory. */
6141 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6142 HeapFree(GetProcessHeap(), 0, mem);
6144 return WINED3D_OK;
6147 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6149 struct wined3d_device *device = surface->resource.device;
6150 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6151 BOOL in_fbo = FALSE;
6152 HRESULT hr;
6154 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6156 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6158 if (location == SFLAG_INTEXTURE)
6160 struct wined3d_context *context = context_acquire(device, NULL);
6161 surface_load_ds_location(surface, context, SFLAG_DS_OFFSCREEN);
6162 context_release(context);
6163 return WINED3D_OK;
6165 else
6167 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6168 return WINED3DERR_INVALIDCALL;
6172 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6174 if (surface_is_offscreen(surface))
6176 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same
6177 * for offscreen targets. Prefer SFLAG_INTEXTURE. */
6178 if (location == SFLAG_INDRAWABLE)
6179 location = SFLAG_INTEXTURE;
6180 in_fbo = TRUE;
6182 else
6184 TRACE("Surface %p is an onscreen surface.\n", surface);
6188 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6189 location = SFLAG_INTEXTURE;
6191 if (surface->flags & location)
6193 TRACE("Location already up to date.\n");
6194 return WINED3D_OK;
6197 if (WARN_ON(d3d_surface))
6199 DWORD required_access = resource_access_from_location(location);
6200 if ((surface->resource.access_flags & required_access) != required_access)
6201 WARN("Operation requires %#x access, but surface only has %#x.\n",
6202 required_access, surface->resource.access_flags);
6205 if (!(surface->flags & SFLAG_LOCATIONS))
6207 ERR("Surface %p does not have any up to date location.\n", surface);
6208 surface->flags |= SFLAG_LOST;
6209 return WINED3DERR_DEVICELOST;
6212 switch (location)
6214 case SFLAG_INSYSMEM:
6215 surface_load_sysmem(surface, gl_info, rect);
6216 break;
6218 case SFLAG_INDRAWABLE:
6219 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6220 return hr;
6221 break;
6223 case SFLAG_INTEXTURE:
6224 case SFLAG_INSRGBTEX:
6225 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6226 return hr;
6227 break;
6229 default:
6230 ERR("Don't know how to handle location %#x.\n", location);
6231 break;
6234 if (!rect)
6236 surface->flags |= location;
6238 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6239 surface_evict_sysmem(surface);
6242 if (in_fbo && (surface->flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)))
6244 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
6245 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
6248 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6249 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6251 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6254 return WINED3D_OK;
6257 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6259 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6261 /* Not on a swapchain - must be offscreen */
6262 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6264 /* The front buffer is always onscreen */
6265 if (surface == swapchain->front_buffer) return FALSE;
6267 /* If the swapchain is rendered to an FBO, the backbuffer is
6268 * offscreen, otherwise onscreen */
6269 return swapchain->render_to_fbo;
6272 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6273 /* Context activation is done by the caller. */
6274 static void ffp_blit_free(struct wined3d_device *device) { }
6276 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6277 /* Context activation is done by the caller. */
6278 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6280 BYTE table[256][4];
6281 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6283 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6285 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6286 ENTER_GL();
6287 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6288 LEAVE_GL();
6291 /* Context activation is done by the caller. */
6292 static HRESULT ffp_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, struct wined3d_surface *surface)
6294 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6296 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6297 * else the surface is converted in software at upload time in LoadLocation.
6299 if(fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6300 ffp_blit_p8_upload_palette(surface, gl_info);
6302 ENTER_GL();
6303 glEnable(surface->texture_target);
6304 checkGLcall("glEnable(surface->texture_target)");
6305 LEAVE_GL();
6306 return WINED3D_OK;
6309 /* Context activation is done by the caller. */
6310 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6312 ENTER_GL();
6313 glDisable(GL_TEXTURE_2D);
6314 checkGLcall("glDisable(GL_TEXTURE_2D)");
6315 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6317 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6318 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6320 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6322 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6323 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6325 LEAVE_GL();
6328 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6329 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6330 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6332 enum complex_fixup src_fixup;
6334 switch (blit_op)
6336 case WINED3D_BLIT_OP_COLOR_BLIT:
6337 src_fixup = get_complex_fixup(src_format->color_fixup);
6338 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6340 TRACE("Checking support for fixup:\n");
6341 dump_color_fixup_desc(src_format->color_fixup);
6344 if (!is_identity_fixup(dst_format->color_fixup))
6346 TRACE("Destination fixups are not supported\n");
6347 return FALSE;
6350 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6352 TRACE("P8 fixup supported\n");
6353 return TRUE;
6356 /* We only support identity conversions. */
6357 if (is_identity_fixup(src_format->color_fixup))
6359 TRACE("[OK]\n");
6360 return TRUE;
6363 TRACE("[FAILED]\n");
6364 return FALSE;
6366 case WINED3D_BLIT_OP_COLOR_FILL:
6367 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6369 TRACE("Color fill not supported\n");
6370 return FALSE;
6373 return TRUE;
6375 case WINED3D_BLIT_OP_DEPTH_FILL:
6376 return TRUE;
6378 default:
6379 TRACE("Unsupported blit_op=%d\n", blit_op);
6380 return FALSE;
6384 /* Do not call while under the GL lock. */
6385 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6386 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
6388 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6389 struct wined3d_fb_state fb = {&dst_surface, NULL};
6391 return device_clear_render_targets(device, 1, &fb,
6392 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6395 /* Do not call while under the GL lock. */
6396 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6397 struct wined3d_surface *surface, const RECT *rect, float depth)
6399 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6400 struct wined3d_fb_state fb = {NULL, surface};
6402 return device_clear_render_targets(device, 0, &fb,
6403 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6406 const struct blit_shader ffp_blit = {
6407 ffp_blit_alloc,
6408 ffp_blit_free,
6409 ffp_blit_set,
6410 ffp_blit_unset,
6411 ffp_blit_supported,
6412 ffp_blit_color_fill,
6413 ffp_blit_depth_fill,
6416 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6418 return WINED3D_OK;
6421 /* Context activation is done by the caller. */
6422 static void cpu_blit_free(struct wined3d_device *device)
6426 /* Context activation is done by the caller. */
6427 static HRESULT cpu_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, struct wined3d_surface *surface)
6429 return WINED3D_OK;
6432 /* Context activation is done by the caller. */
6433 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6437 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6438 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6439 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6441 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6443 return TRUE;
6446 return FALSE;
6449 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6450 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6451 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6453 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6454 const struct wined3d_format *src_format, *dst_format;
6455 struct wined3d_surface *orig_src = src_surface;
6456 WINED3DLOCKED_RECT dlock, slock;
6457 HRESULT hr = WINED3D_OK;
6458 const BYTE *sbuf;
6459 RECT xdst,xsrc;
6460 BYTE *dbuf;
6461 int x, y;
6463 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6464 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6465 flags, fx, debug_d3dtexturefiltertype(filter));
6467 xsrc = *src_rect;
6469 if (!src_surface)
6471 RECT full_rect;
6473 full_rect.left = 0;
6474 full_rect.top = 0;
6475 full_rect.right = dst_surface->resource.width;
6476 full_rect.bottom = dst_surface->resource.height;
6477 IntersectRect(&xdst, &full_rect, dst_rect);
6479 else
6481 BOOL clip_horiz, clip_vert;
6483 xdst = *dst_rect;
6484 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6485 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6487 if (clip_vert || clip_horiz)
6489 /* Now check if this is a special case or not... */
6490 if ((flags & WINEDDBLT_DDFX)
6491 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6492 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6494 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6495 return WINED3D_OK;
6498 if (clip_horiz)
6500 if (xdst.left < 0)
6502 xsrc.left -= xdst.left;
6503 xdst.left = 0;
6505 if (xdst.right > dst_surface->resource.width)
6507 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6508 xdst.right = (int)dst_surface->resource.width;
6512 if (clip_vert)
6514 if (xdst.top < 0)
6516 xsrc.top -= xdst.top;
6517 xdst.top = 0;
6519 if (xdst.bottom > dst_surface->resource.height)
6521 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6522 xdst.bottom = (int)dst_surface->resource.height;
6526 /* And check if after clipping something is still to be done... */
6527 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6528 || (xdst.left >= (int)dst_surface->resource.width)
6529 || (xdst.top >= (int)dst_surface->resource.height)
6530 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6531 || (xsrc.left >= (int)src_surface->resource.width)
6532 || (xsrc.top >= (int)src_surface->resource.height))
6534 TRACE("Nothing to be done after clipping.\n");
6535 return WINED3D_OK;
6540 if (src_surface == dst_surface)
6542 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6543 slock = dlock;
6544 src_format = dst_surface->resource.format;
6545 dst_format = src_format;
6547 else
6549 dst_format = dst_surface->resource.format;
6550 if (src_surface)
6552 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6554 src_surface = surface_convert_format(src_surface, dst_format->id);
6555 if (!src_surface)
6557 /* The conv function writes a FIXME */
6558 WARN("Cannot convert source surface format to dest format.\n");
6559 goto release;
6562 wined3d_surface_map(src_surface, &slock, NULL, WINED3DLOCK_READONLY);
6563 src_format = src_surface->resource.format;
6565 else
6567 src_format = dst_format;
6569 if (dst_rect)
6570 wined3d_surface_map(dst_surface, &dlock, &xdst, 0);
6571 else
6572 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6575 bpp = dst_surface->resource.format->byte_count;
6576 srcheight = xsrc.bottom - xsrc.top;
6577 srcwidth = xsrc.right - xsrc.left;
6578 dstheight = xdst.bottom - xdst.top;
6579 dstwidth = xdst.right - xdst.left;
6580 width = (xdst.right - xdst.left) * bpp;
6582 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_COMPRESSED)
6584 UINT row_block_count;
6586 if (flags || src_surface == dst_surface)
6588 FIXME("Only plain blits supported on compressed surfaces.\n");
6589 hr = E_NOTIMPL;
6590 goto release;
6593 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6595 if (srcheight != dstheight || srcwidth != dstwidth)
6597 WARN("Stretching not supported on compressed surfaces.\n");
6598 hr = WINED3DERR_INVALIDCALL;
6599 goto release;
6602 dbuf = dlock.pBits;
6603 sbuf = slock.pBits;
6605 row_block_count = (dstwidth + dst_format->block_width - 1) / dst_format->block_width;
6606 for (y = 0; y < dstheight; y += dst_format->block_height)
6608 memcpy(dbuf, sbuf, row_block_count * dst_format->block_byte_count);
6609 dbuf += dlock.Pitch;
6610 sbuf += slock.Pitch;
6613 goto release;
6616 if (dst_rect && src_surface != dst_surface)
6617 dbuf = dlock.pBits;
6618 else
6619 dbuf = (BYTE*)dlock.pBits+(xdst.top*dlock.Pitch)+(xdst.left*bpp);
6621 /* First, all the 'source-less' blits */
6622 if (flags & WINEDDBLT_COLORFILL)
6624 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dlock.Pitch, fx->u5.dwFillColor);
6625 flags &= ~WINEDDBLT_COLORFILL;
6628 if (flags & WINEDDBLT_DEPTHFILL)
6630 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6632 if (flags & WINEDDBLT_ROP)
6634 /* Catch some degenerate cases here. */
6635 switch (fx->dwROP)
6637 case BLACKNESS:
6638 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,0);
6639 break;
6640 case 0xAA0029: /* No-op */
6641 break;
6642 case WHITENESS:
6643 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,~0);
6644 break;
6645 case SRCCOPY: /* Well, we do that below? */
6646 break;
6647 default:
6648 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6649 goto error;
6651 flags &= ~WINEDDBLT_ROP;
6653 if (flags & WINEDDBLT_DDROPS)
6655 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6657 /* Now the 'with source' blits. */
6658 if (src_surface)
6660 const BYTE *sbase;
6661 int sx, xinc, sy, yinc;
6663 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6664 goto release;
6666 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6667 && (srcwidth != dstwidth || srcheight != dstheight))
6669 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6670 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6673 sbase = (BYTE*)slock.pBits+(xsrc.top*slock.Pitch)+xsrc.left*bpp;
6674 xinc = (srcwidth << 16) / dstwidth;
6675 yinc = (srcheight << 16) / dstheight;
6677 if (!flags)
6679 /* No effects, we can cheat here. */
6680 if (dstwidth == srcwidth)
6682 if (dstheight == srcheight)
6684 /* No stretching in either direction. This needs to be as
6685 * fast as possible. */
6686 sbuf = sbase;
6688 /* Check for overlapping surfaces. */
6689 if (src_surface != dst_surface || xdst.top < xsrc.top
6690 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6692 /* No overlap, or dst above src, so copy from top downwards. */
6693 for (y = 0; y < dstheight; ++y)
6695 memcpy(dbuf, sbuf, width);
6696 sbuf += slock.Pitch;
6697 dbuf += dlock.Pitch;
6700 else if (xdst.top > xsrc.top)
6702 /* Copy from bottom upwards. */
6703 sbuf += (slock.Pitch*dstheight);
6704 dbuf += (dlock.Pitch*dstheight);
6705 for (y = 0; y < dstheight; ++y)
6707 sbuf -= slock.Pitch;
6708 dbuf -= dlock.Pitch;
6709 memcpy(dbuf, sbuf, width);
6712 else
6714 /* Src and dst overlapping on the same line, use memmove. */
6715 for (y = 0; y < dstheight; ++y)
6717 memmove(dbuf, sbuf, width);
6718 sbuf += slock.Pitch;
6719 dbuf += dlock.Pitch;
6723 else
6725 /* Stretching in y direction only. */
6726 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6728 sbuf = sbase + (sy >> 16) * slock.Pitch;
6729 memcpy(dbuf, sbuf, width);
6730 dbuf += dlock.Pitch;
6734 else
6736 /* Stretching in X direction. */
6737 int last_sy = -1;
6738 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6740 sbuf = sbase + (sy >> 16) * slock.Pitch;
6742 if ((sy >> 16) == (last_sy >> 16))
6744 /* This source row is the same as last source row -
6745 * Copy the already stretched row. */
6746 memcpy(dbuf, dbuf - dlock.Pitch, width);
6748 else
6750 #define STRETCH_ROW(type) \
6751 do { \
6752 const type *s = (const type *)sbuf; \
6753 type *d = (type *)dbuf; \
6754 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6755 d[x] = s[sx >> 16]; \
6756 } while(0)
6758 switch(bpp)
6760 case 1:
6761 STRETCH_ROW(BYTE);
6762 break;
6763 case 2:
6764 STRETCH_ROW(WORD);
6765 break;
6766 case 4:
6767 STRETCH_ROW(DWORD);
6768 break;
6769 case 3:
6771 const BYTE *s;
6772 BYTE *d = dbuf;
6773 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6775 DWORD pixel;
6777 s = sbuf + 3 * (sx >> 16);
6778 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6779 d[0] = (pixel ) & 0xff;
6780 d[1] = (pixel >> 8) & 0xff;
6781 d[2] = (pixel >> 16) & 0xff;
6782 d += 3;
6784 break;
6786 default:
6787 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6788 hr = WINED3DERR_NOTAVAILABLE;
6789 goto error;
6791 #undef STRETCH_ROW
6793 dbuf += dlock.Pitch;
6794 last_sy = sy;
6798 else
6800 LONG dstyinc = dlock.Pitch, dstxinc = bpp;
6801 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6802 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6803 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6805 /* The color keying flags are checked for correctness in ddraw */
6806 if (flags & WINEDDBLT_KEYSRC)
6808 keylow = src_surface->SrcBltCKey.dwColorSpaceLowValue;
6809 keyhigh = src_surface->SrcBltCKey.dwColorSpaceHighValue;
6811 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6813 keylow = fx->ddckSrcColorkey.dwColorSpaceLowValue;
6814 keyhigh = fx->ddckSrcColorkey.dwColorSpaceHighValue;
6817 if (flags & WINEDDBLT_KEYDEST)
6819 /* Destination color keys are taken from the source surface! */
6820 destkeylow = src_surface->DestBltCKey.dwColorSpaceLowValue;
6821 destkeyhigh = src_surface->DestBltCKey.dwColorSpaceHighValue;
6823 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6825 destkeylow = fx->ddckDestColorkey.dwColorSpaceLowValue;
6826 destkeyhigh = fx->ddckDestColorkey.dwColorSpaceHighValue;
6829 if (bpp == 1)
6831 keymask = 0xff;
6833 else
6835 keymask = src_format->red_mask
6836 | src_format->green_mask
6837 | src_format->blue_mask;
6839 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6842 if (flags & WINEDDBLT_DDFX)
6844 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6845 LONG tmpxy;
6846 dTopLeft = dbuf;
6847 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6848 dBottomLeft = dTopLeft + ((dstheight - 1) * dlock.Pitch);
6849 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6851 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6853 /* I don't think we need to do anything about this flag */
6854 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6856 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6858 tmp = dTopRight;
6859 dTopRight = dTopLeft;
6860 dTopLeft = tmp;
6861 tmp = dBottomRight;
6862 dBottomRight = dBottomLeft;
6863 dBottomLeft = tmp;
6864 dstxinc = dstxinc * -1;
6866 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6868 tmp = dTopLeft;
6869 dTopLeft = dBottomLeft;
6870 dBottomLeft = tmp;
6871 tmp = dTopRight;
6872 dTopRight = dBottomRight;
6873 dBottomRight = tmp;
6874 dstyinc = dstyinc * -1;
6876 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6878 /* I don't think we need to do anything about this flag */
6879 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6881 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6883 tmp = dBottomRight;
6884 dBottomRight = dTopLeft;
6885 dTopLeft = tmp;
6886 tmp = dBottomLeft;
6887 dBottomLeft = dTopRight;
6888 dTopRight = tmp;
6889 dstxinc = dstxinc * -1;
6890 dstyinc = dstyinc * -1;
6892 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6894 tmp = dTopLeft;
6895 dTopLeft = dBottomLeft;
6896 dBottomLeft = dBottomRight;
6897 dBottomRight = dTopRight;
6898 dTopRight = tmp;
6899 tmpxy = dstxinc;
6900 dstxinc = dstyinc;
6901 dstyinc = tmpxy;
6902 dstxinc = dstxinc * -1;
6904 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6906 tmp = dTopLeft;
6907 dTopLeft = dTopRight;
6908 dTopRight = dBottomRight;
6909 dBottomRight = dBottomLeft;
6910 dBottomLeft = tmp;
6911 tmpxy = dstxinc;
6912 dstxinc = dstyinc;
6913 dstyinc = tmpxy;
6914 dstyinc = dstyinc * -1;
6916 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6918 /* I don't think we need to do anything about this flag */
6919 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6921 dbuf = dTopLeft;
6922 flags &= ~(WINEDDBLT_DDFX);
6925 #define COPY_COLORKEY_FX(type) \
6926 do { \
6927 const type *s; \
6928 type *d = (type *)dbuf, *dx, tmp; \
6929 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6931 s = (const type *)(sbase + (sy >> 16) * slock.Pitch); \
6932 dx = d; \
6933 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6935 tmp = s[sx >> 16]; \
6936 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6937 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6939 dx[0] = tmp; \
6941 dx = (type *)(((BYTE *)dx) + dstxinc); \
6943 d = (type *)(((BYTE *)d) + dstyinc); \
6945 } while(0)
6947 switch (bpp)
6949 case 1:
6950 COPY_COLORKEY_FX(BYTE);
6951 break;
6952 case 2:
6953 COPY_COLORKEY_FX(WORD);
6954 break;
6955 case 4:
6956 COPY_COLORKEY_FX(DWORD);
6957 break;
6958 case 3:
6960 const BYTE *s;
6961 BYTE *d = dbuf, *dx;
6962 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6964 sbuf = sbase + (sy >> 16) * slock.Pitch;
6965 dx = d;
6966 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
6968 DWORD pixel, dpixel = 0;
6969 s = sbuf + 3 * (sx>>16);
6970 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6971 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
6972 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
6973 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
6975 dx[0] = (pixel ) & 0xff;
6976 dx[1] = (pixel >> 8) & 0xff;
6977 dx[2] = (pixel >> 16) & 0xff;
6979 dx += dstxinc;
6981 d += dstyinc;
6983 break;
6985 default:
6986 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
6987 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
6988 hr = WINED3DERR_NOTAVAILABLE;
6989 goto error;
6990 #undef COPY_COLORKEY_FX
6995 error:
6996 if (flags && FIXME_ON(d3d_surface))
6998 FIXME("\tUnsupported flags: %#x.\n", flags);
7001 release:
7002 wined3d_surface_unmap(dst_surface);
7003 if (src_surface && src_surface != dst_surface)
7004 wined3d_surface_unmap(src_surface);
7005 /* Release the converted surface, if any. */
7006 if (src_surface && src_surface != orig_src)
7007 wined3d_surface_decref(src_surface);
7009 return hr;
7012 /* Do not call while under the GL lock. */
7013 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7014 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
7016 WINEDDBLTFX BltFx;
7018 memset(&BltFx, 0, sizeof(BltFx));
7019 BltFx.dwSize = sizeof(BltFx);
7020 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7021 return wined3d_surface_blt(dst_surface, dst_rect, NULL, NULL,
7022 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7025 /* Do not call while under the GL lock. */
7026 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7027 struct wined3d_surface *surface, const RECT *rect, float depth)
7029 FIXME("Depth filling not implemented by cpu_blit.\n");
7030 return WINED3DERR_INVALIDCALL;
7033 const struct blit_shader cpu_blit = {
7034 cpu_blit_alloc,
7035 cpu_blit_free,
7036 cpu_blit_set,
7037 cpu_blit_unset,
7038 cpu_blit_supported,
7039 cpu_blit_color_fill,
7040 cpu_blit_depth_fill,
7043 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7044 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
7045 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7046 WINED3DPOOL pool, void *parent, const struct wined3d_parent_ops *parent_ops)
7048 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7049 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7050 unsigned int resource_size;
7051 HRESULT hr;
7053 if (multisample_quality > 0)
7055 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7056 multisample_quality = 0;
7059 /* Quick lockable sanity check.
7060 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7061 * this function is too deep to need to care about things like this.
7062 * Levels need to be checked too, since they all affect what can be done. */
7063 switch (pool)
7065 case WINED3DPOOL_SCRATCH:
7066 if (!lockable)
7068 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7069 "which are mutually exclusive, setting lockable to TRUE.\n");
7070 lockable = TRUE;
7072 break;
7074 case WINED3DPOOL_SYSTEMMEM:
7075 if (!lockable)
7076 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7077 break;
7079 case WINED3DPOOL_MANAGED:
7080 if (usage & WINED3DUSAGE_DYNAMIC)
7081 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7082 break;
7084 case WINED3DPOOL_DEFAULT:
7085 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7086 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7087 break;
7089 default:
7090 FIXME("Unknown pool %#x.\n", pool);
7091 break;
7094 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7095 FIXME("Trying to create a render target that isn't in the default pool.\n");
7097 /* FIXME: Check that the format is supported by the device. */
7099 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7100 if (!resource_size)
7101 return WINED3DERR_INVALIDCALL;
7103 surface->surface_type = surface_type;
7105 switch (surface_type)
7107 case SURFACE_OPENGL:
7108 surface->surface_ops = &surface_ops;
7109 break;
7111 case SURFACE_GDI:
7112 surface->surface_ops = &gdi_surface_ops;
7113 break;
7115 default:
7116 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7117 return WINED3DERR_INVALIDCALL;
7120 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7121 multisample_type, multisample_quality, usage, pool, width, height, 1,
7122 resource_size, parent, parent_ops, &surface_resource_ops);
7123 if (FAILED(hr))
7125 WARN("Failed to initialize resource, returning %#x.\n", hr);
7126 return hr;
7129 /* "Standalone" surface. */
7130 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7132 surface->texture_level = level;
7133 list_init(&surface->overlays);
7135 /* Flags */
7136 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7137 if (discard)
7138 surface->flags |= SFLAG_DISCARD;
7139 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7140 surface->flags |= SFLAG_LOCKABLE;
7141 /* I'm not sure if this qualifies as a hack or as an optimization. It
7142 * seems reasonable to assume that lockable render targets will get
7143 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7144 * creation. However, the other reason we want to do this is that several
7145 * ddraw applications access surface memory while the surface isn't
7146 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7147 * future locks prevents these from crashing. */
7148 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7149 surface->flags |= SFLAG_DYNLOCK;
7151 /* Mark the texture as dirty so that it gets loaded first time around. */
7152 surface_add_dirty_rect(surface, NULL);
7153 list_init(&surface->renderbuffers);
7155 TRACE("surface %p, memory %p, size %u\n",
7156 surface, surface->resource.allocatedMemory, surface->resource.size);
7158 /* Call the private setup routine */
7159 hr = surface->surface_ops->surface_private_setup(surface);
7160 if (FAILED(hr))
7162 ERR("Private setup failed, returning %#x\n", hr);
7163 surface->surface_ops->surface_cleanup(surface);
7164 return hr;
7167 return hr;
7170 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7171 enum wined3d_format_id format_id, BOOL lockable, BOOL discard, UINT level, DWORD usage, WINED3DPOOL pool,
7172 WINED3DMULTISAMPLE_TYPE multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7173 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7175 struct wined3d_surface *object;
7176 HRESULT hr;
7178 TRACE("device %p, width %u, height %u, format %s, lockable %#x, discard %#x, level %u\n",
7179 device, width, height, debug_d3dformat(format_id), lockable, discard, level);
7180 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7181 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7182 TRACE("surface_type %#x, parent %p, parent_ops %p.\n", surface_type, parent, parent_ops);
7184 if (surface_type == SURFACE_OPENGL && !device->adapter)
7186 ERR("OpenGL surfaces are not available without OpenGL.\n");
7187 return WINED3DERR_NOTAVAILABLE;
7190 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7191 if (!object)
7193 ERR("Failed to allocate surface memory.\n");
7194 return WINED3DERR_OUTOFVIDEOMEMORY;
7197 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level, lockable,
7198 discard, multisample_type, multisample_quality, device, usage, format_id, pool, parent, parent_ops);
7199 if (FAILED(hr))
7201 WARN("Failed to initialize surface, returning %#x.\n", hr);
7202 HeapFree(GetProcessHeap(), 0, object);
7203 return hr;
7206 TRACE("Created surface %p.\n", object);
7207 *surface = object;
7209 return hr;