wined3d: Don't create a DIB section in gdi_surface_map.
[wine/multimedia.git] / dlls / wined3d / surface.c
blob878b848ace3c432cf3dca4eeb4a21f7155f91369
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-2011, 2013 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_perf);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d);
37 #define MAXLOCKCOUNT 50 /* After this amount of locks do not free the sysmem copy. */
39 static void surface_cleanup(struct wined3d_surface *surface)
41 struct wined3d_surface *overlay, *cur;
43 TRACE("surface %p.\n", surface);
45 if ((surface->flags & SFLAG_PBO) || surface->rb_multisample
46 || surface->rb_resolved || !list_empty(&surface->renderbuffers))
48 struct wined3d_renderbuffer_entry *entry, *entry2;
49 const struct wined3d_gl_info *gl_info;
50 struct wined3d_context *context;
52 context = context_acquire(surface->resource.device, NULL);
53 gl_info = context->gl_info;
55 if (surface->flags & SFLAG_PBO)
57 TRACE("Deleting PBO %u.\n", surface->pbo);
58 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
61 if (surface->rb_multisample)
63 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
64 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
67 if (surface->rb_resolved)
69 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
70 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
73 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
75 TRACE("Deleting renderbuffer %u.\n", entry->id);
76 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
77 HeapFree(GetProcessHeap(), 0, entry);
80 context_release(context);
83 if (surface->flags & SFLAG_DIBSECTION)
85 DeleteDC(surface->hDC);
86 DeleteObject(surface->dib.DIBsection);
87 surface->dib.bitmap_data = NULL;
88 surface->resource.allocatedMemory = NULL;
91 if (surface->flags & SFLAG_USERPTR)
92 surface->resource.allocatedMemory = NULL;
94 if (surface->overlay_dest)
95 list_remove(&surface->overlay_entry);
97 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
99 list_remove(&overlay->overlay_entry);
100 overlay->overlay_dest = NULL;
103 resource_cleanup(&surface->resource);
106 void surface_update_draw_binding(struct wined3d_surface *surface)
108 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
109 surface->draw_binding = SFLAG_INDRAWABLE;
110 else if (surface->resource.multisample_type)
111 surface->draw_binding = SFLAG_INRB_MULTISAMPLE;
112 else
113 surface->draw_binding = SFLAG_INTEXTURE;
116 void surface_set_swapchain(struct wined3d_surface *surface, struct wined3d_swapchain *swapchain)
118 TRACE("surface %p, swapchain %p.\n", surface, swapchain);
120 if (swapchain)
122 surface->get_drawable_size = get_drawable_size_swapchain;
124 else
126 switch (wined3d_settings.offscreen_rendering_mode)
128 case ORM_FBO:
129 surface->get_drawable_size = get_drawable_size_fbo;
130 break;
132 case ORM_BACKBUFFER:
133 surface->get_drawable_size = get_drawable_size_backbuffer;
134 break;
136 default:
137 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
138 return;
142 surface->swapchain = swapchain;
143 surface_update_draw_binding(surface);
146 void surface_set_container(struct wined3d_surface *surface, struct wined3d_texture *container)
148 TRACE("surface %p, container %p.\n", surface, container);
150 if (!surface->swapchain)
152 switch (wined3d_settings.offscreen_rendering_mode)
154 case ORM_FBO:
155 surface->get_drawable_size = get_drawable_size_fbo;
156 break;
158 case ORM_BACKBUFFER:
159 surface->get_drawable_size = get_drawable_size_backbuffer;
160 break;
162 default:
163 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
164 return;
168 surface->container = container;
169 surface_update_draw_binding(surface);
172 struct blt_info
174 GLenum binding;
175 GLenum bind_target;
176 enum tex_types tex_type;
177 GLfloat coords[4][3];
180 struct float_rect
182 float l;
183 float t;
184 float r;
185 float b;
188 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
190 f->l = ((r->left * 2.0f) / w) - 1.0f;
191 f->t = ((r->top * 2.0f) / h) - 1.0f;
192 f->r = ((r->right * 2.0f) / w) - 1.0f;
193 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
196 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
198 GLfloat (*coords)[3] = info->coords;
199 struct float_rect f;
201 switch (target)
203 default:
204 FIXME("Unsupported texture target %#x\n", target);
205 /* Fall back to GL_TEXTURE_2D */
206 case GL_TEXTURE_2D:
207 info->binding = GL_TEXTURE_BINDING_2D;
208 info->bind_target = GL_TEXTURE_2D;
209 info->tex_type = tex_2d;
210 coords[0][0] = (float)rect->left / w;
211 coords[0][1] = (float)rect->top / h;
212 coords[0][2] = 0.0f;
214 coords[1][0] = (float)rect->right / w;
215 coords[1][1] = (float)rect->top / h;
216 coords[1][2] = 0.0f;
218 coords[2][0] = (float)rect->left / w;
219 coords[2][1] = (float)rect->bottom / h;
220 coords[2][2] = 0.0f;
222 coords[3][0] = (float)rect->right / w;
223 coords[3][1] = (float)rect->bottom / h;
224 coords[3][2] = 0.0f;
225 break;
227 case GL_TEXTURE_RECTANGLE_ARB:
228 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
229 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
230 info->tex_type = tex_rect;
231 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
232 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
233 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
234 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
235 break;
237 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
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] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
244 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
245 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
246 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
247 break;
249 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
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] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
256 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
257 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
258 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
259 break;
261 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
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] = 1.0f; coords[0][2] = f.t;
268 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
269 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
270 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
271 break;
273 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
274 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
275 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
276 info->tex_type = tex_cube;
277 cube_coords_float(rect, w, h, &f);
279 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
280 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
281 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
282 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
283 break;
285 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
286 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
287 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
288 info->tex_type = tex_cube;
289 cube_coords_float(rect, w, h, &f);
291 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
292 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
293 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
294 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
295 break;
297 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
298 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
299 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
300 info->tex_type = tex_cube;
301 cube_coords_float(rect, w, h, &f);
303 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
304 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
305 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
306 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
307 break;
311 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
313 if (rect_in)
314 *rect_out = *rect_in;
315 else
317 rect_out->left = 0;
318 rect_out->top = 0;
319 rect_out->right = surface->resource.width;
320 rect_out->bottom = surface->resource.height;
324 /* Context activation is done by the caller. */
325 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
326 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
328 const struct wined3d_gl_info *gl_info = context->gl_info;
329 struct wined3d_texture *texture = src_surface->container;
330 struct blt_info info;
332 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
334 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
335 checkGLcall("glEnable(bind_target)");
337 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
339 /* Filtering for StretchRect */
340 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
341 wined3d_gl_mag_filter(magLookup, filter));
342 checkGLcall("glTexParameteri");
343 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
344 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
345 checkGLcall("glTexParameteri");
346 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
347 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
348 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
349 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
350 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
351 checkGLcall("glTexEnvi");
353 /* Draw a quad */
354 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
355 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
356 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
358 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
359 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
361 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
362 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
364 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
365 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
366 gl_info->gl_ops.gl.p_glEnd();
368 /* Unbind the texture */
369 context_bind_texture(context, info.bind_target, 0);
371 /* We changed the filtering settings on the texture. Inform the
372 * container about this to get the filters reset properly next draw. */
373 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_POINT;
374 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
375 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_NONE;
376 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
379 /* Works correctly only for <= 4 bpp formats. */
380 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
382 masks[0] = ((1 << format->red_size) - 1) << format->red_offset;
383 masks[1] = ((1 << format->green_size) - 1) << format->green_offset;
384 masks[2] = ((1 << format->blue_size) - 1) << format->blue_offset;
387 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
389 const struct wined3d_format *format = surface->resource.format;
390 SYSTEM_INFO sysInfo;
391 BITMAPINFO *b_info;
392 int extraline = 0;
393 DWORD *masks;
395 TRACE("surface %p.\n", surface);
397 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
399 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
400 return WINED3DERR_INVALIDCALL;
403 switch (format->byte_count)
405 case 2:
406 case 4:
407 /* Allocate extra space to store the RGB bit masks. */
408 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
409 break;
411 case 3:
412 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
413 break;
415 default:
416 /* Allocate extra space for a palette. */
417 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
418 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
419 break;
422 if (!b_info)
423 return E_OUTOFMEMORY;
425 /* Some applications access the surface in via DWORDs, and do not take
426 * the necessary care at the end of the surface. So we need at least
427 * 4 extra bytes at the end of the surface. Check against the page size,
428 * if the last page used for the surface has at least 4 spare bytes we're
429 * safe, otherwise add an extra line to the DIB section. */
430 GetSystemInfo(&sysInfo);
431 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
433 extraline = 1;
434 TRACE("Adding an extra line to the DIB section.\n");
437 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
438 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
439 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
440 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
441 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
442 * wined3d_surface_get_pitch(surface);
443 b_info->bmiHeader.biPlanes = 1;
444 b_info->bmiHeader.biBitCount = format->byte_count * 8;
446 b_info->bmiHeader.biXPelsPerMeter = 0;
447 b_info->bmiHeader.biYPelsPerMeter = 0;
448 b_info->bmiHeader.biClrUsed = 0;
449 b_info->bmiHeader.biClrImportant = 0;
451 /* Get the bit masks */
452 masks = (DWORD *)b_info->bmiColors;
453 switch (surface->resource.format->id)
455 case WINED3DFMT_B8G8R8_UNORM:
456 b_info->bmiHeader.biCompression = BI_RGB;
457 break;
459 case WINED3DFMT_B5G5R5X1_UNORM:
460 case WINED3DFMT_B5G5R5A1_UNORM:
461 case WINED3DFMT_B4G4R4A4_UNORM:
462 case WINED3DFMT_B4G4R4X4_UNORM:
463 case WINED3DFMT_B2G3R3_UNORM:
464 case WINED3DFMT_B2G3R3A8_UNORM:
465 case WINED3DFMT_R10G10B10A2_UNORM:
466 case WINED3DFMT_R8G8B8A8_UNORM:
467 case WINED3DFMT_R8G8B8X8_UNORM:
468 case WINED3DFMT_B10G10R10A2_UNORM:
469 case WINED3DFMT_B5G6R5_UNORM:
470 case WINED3DFMT_R16G16B16A16_UNORM:
471 b_info->bmiHeader.biCompression = BI_BITFIELDS;
472 get_color_masks(format, masks);
473 break;
475 default:
476 /* Don't know palette */
477 b_info->bmiHeader.biCompression = BI_RGB;
478 break;
481 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
482 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
483 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
484 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
486 if (!surface->dib.DIBsection)
488 ERR("Failed to create DIB section.\n");
489 HeapFree(GetProcessHeap(), 0, b_info);
490 return HRESULT_FROM_WIN32(GetLastError());
493 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
494 /* Copy the existing surface to the dib section. */
495 if (surface->resource.allocatedMemory)
497 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
498 surface->resource.height * wined3d_surface_get_pitch(surface));
500 else
502 /* This is to make maps read the GL texture although memory is allocated. */
503 surface->flags &= ~SFLAG_INSYSMEM;
505 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
507 HeapFree(GetProcessHeap(), 0, b_info);
509 /* Now allocate a DC. */
510 surface->hDC = CreateCompatibleDC(0);
511 SelectObject(surface->hDC, surface->dib.DIBsection);
512 TRACE("Using wined3d palette %p.\n", surface->palette);
513 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
515 surface->flags |= SFLAG_DIBSECTION;
517 return WINED3D_OK;
520 static BOOL surface_need_pbo(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
522 if (surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
523 return FALSE;
524 if (!(surface->flags & SFLAG_DYNLOCK))
525 return FALSE;
526 if (surface->flags & (SFLAG_CONVERTED | SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM))
527 return FALSE;
528 if (!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT])
529 return FALSE;
531 return TRUE;
534 static void surface_load_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
536 struct wined3d_context *context;
537 GLenum error;
539 context = context_acquire(surface->resource.device, NULL);
541 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
542 error = gl_info->gl_ops.gl.p_glGetError();
543 if (!surface->pbo || error != GL_NO_ERROR)
544 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
546 TRACE("Binding PBO %u.\n", surface->pbo);
548 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
549 checkGLcall("glBindBufferARB");
551 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
552 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
553 checkGLcall("glBufferDataARB");
555 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
556 checkGLcall("glBindBufferARB");
558 /* We don't need the system memory anymore and we can't even use it for PBOs. */
559 if (!(surface->flags & SFLAG_CLIENT))
560 wined3d_resource_free_sysmem(&surface->resource);
561 surface->resource.allocatedMemory = NULL;
562 surface->flags |= SFLAG_PBO;
563 context_release(context);
566 static void surface_prepare_system_memory(struct wined3d_surface *surface)
568 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
570 TRACE("surface %p.\n", surface);
572 if (!(surface->flags & SFLAG_PBO) && surface_need_pbo(surface, gl_info))
573 surface_load_pbo(surface, gl_info);
574 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
576 /* Whatever surface we have, make sure that there is memory allocated
577 * for the downloaded copy, or a PBO to map. */
578 if (!surface->resource.heap_memory && !wined3d_resource_allocate_sysmem(&surface->resource))
579 ERR("Failed to allocate system memory.\n");
580 surface->resource.allocatedMemory = surface->resource.heap_memory;
582 if (surface->flags & SFLAG_INSYSMEM)
583 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
587 static void surface_evict_sysmem(struct wined3d_surface *surface)
589 if (surface->resource.map_count || (surface->flags & SFLAG_DONOTFREE))
590 return;
592 wined3d_resource_free_sysmem(&surface->resource);
593 surface->resource.allocatedMemory = NULL;
594 surface_invalidate_location(surface, SFLAG_INSYSMEM);
597 static void surface_force_reload(struct wined3d_surface *surface)
599 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
602 static void surface_release_client_storage(struct wined3d_surface *surface)
604 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
605 const struct wined3d_gl_info *gl_info = context->gl_info;
607 if (surface->container->texture_rgb.name)
609 wined3d_texture_bind_and_dirtify(surface->container, context, FALSE);
610 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
611 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
613 if (surface->container->texture_srgb.name)
615 wined3d_texture_bind_and_dirtify(surface->container, context, TRUE);
616 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
617 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
620 context_release(context);
622 surface_invalidate_location(surface, SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
623 surface_force_reload(surface);
626 static HRESULT surface_private_setup(struct wined3d_surface *surface)
628 /* TODO: Check against the maximum texture sizes supported by the video card. */
629 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
630 unsigned int pow2Width, pow2Height;
632 TRACE("surface %p.\n", surface);
634 surface->texture_target = GL_TEXTURE_2D;
636 /* Non-power2 support */
637 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
639 pow2Width = surface->resource.width;
640 pow2Height = surface->resource.height;
642 else
644 /* Find the nearest pow2 match */
645 pow2Width = pow2Height = 1;
646 while (pow2Width < surface->resource.width)
647 pow2Width <<= 1;
648 while (pow2Height < surface->resource.height)
649 pow2Height <<= 1;
651 surface->pow2Width = pow2Width;
652 surface->pow2Height = pow2Height;
654 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
656 /* TODO: Add support for non power two compressed textures. */
657 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
659 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
660 surface, surface->resource.width, surface->resource.height);
661 return WINED3DERR_NOTAVAILABLE;
665 if (pow2Width != surface->resource.width
666 || pow2Height != surface->resource.height)
668 surface->flags |= SFLAG_NONPOW2;
671 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
672 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
674 /* One of three options:
675 * 1: Do the same as we do with NPOT and scale the texture, (any
676 * texture ops would require the texture to be scaled which is
677 * potentially slow)
678 * 2: Set the texture to the maximum size (bad idea).
679 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
680 * 4: Create the surface, but allow it to be used only for DirectDraw
681 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
682 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
683 * the render target. */
684 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
686 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
687 return WINED3DERR_NOTAVAILABLE;
690 /* We should never use this surface in combination with OpenGL! */
691 TRACE("Creating an oversized surface: %ux%u.\n",
692 surface->pow2Width, surface->pow2Height);
695 switch (wined3d_settings.offscreen_rendering_mode)
697 case ORM_FBO:
698 surface->get_drawable_size = get_drawable_size_fbo;
699 break;
701 case ORM_BACKBUFFER:
702 surface->get_drawable_size = get_drawable_size_backbuffer;
703 break;
705 default:
706 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
707 return WINED3DERR_INVALIDCALL;
710 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
711 surface->flags |= SFLAG_DISCARDED;
713 return WINED3D_OK;
716 static void surface_realize_palette(struct wined3d_surface *surface)
718 struct wined3d_palette *palette = surface->palette;
720 TRACE("surface %p.\n", surface);
722 if (!palette) return;
724 if (surface->resource.format->id == WINED3DFMT_P8_UINT
725 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
727 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
729 /* Make sure the texture is up to date. This call doesn't do
730 * anything if the texture is already up to date. */
731 surface_load_location(surface, SFLAG_INTEXTURE);
733 /* We want to force a palette refresh, so mark the drawable as not being up to date */
734 if (!surface_is_offscreen(surface))
735 surface_invalidate_location(surface, SFLAG_INDRAWABLE);
737 else
739 if (!(surface->flags & SFLAG_INSYSMEM))
741 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
742 surface_load_location(surface, SFLAG_INSYSMEM);
744 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
748 if (surface->flags & SFLAG_DIBSECTION)
750 RGBQUAD col[256];
751 unsigned int i;
753 TRACE("Updating the DC's palette.\n");
755 for (i = 0; i < 256; ++i)
757 col[i].rgbRed = palette->palents[i].peRed;
758 col[i].rgbGreen = palette->palents[i].peGreen;
759 col[i].rgbBlue = palette->palents[i].peBlue;
760 col[i].rgbReserved = 0;
762 SetDIBColorTable(surface->hDC, 0, 256, col);
765 /* Propagate the changes to the drawable when we have a palette. */
766 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
767 surface_load_location(surface, surface->draw_binding);
770 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
772 struct wined3d_device *device = surface->resource.device;
774 TRACE("surface %p, rect %s, flags %#x.\n",
775 surface, wine_dbgstr_rect(rect), flags);
777 if (flags & WINED3D_MAP_DISCARD)
779 TRACE("WINED3D_MAP_DISCARD flag passed, marking SYSMEM as up to date.\n");
780 surface_prepare_system_memory(surface);
781 surface_validate_location(surface, SFLAG_INSYSMEM);
782 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
784 else
786 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
787 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
789 surface_load_location(surface, SFLAG_INSYSMEM);
792 if (surface->flags & SFLAG_PBO)
794 const struct wined3d_gl_info *gl_info;
795 struct wined3d_context *context;
797 context = context_acquire(device, NULL);
798 gl_info = context->gl_info;
800 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
801 checkGLcall("glBindBufferARB");
803 /* This shouldn't happen but could occur if some other function
804 * didn't handle the PBO properly. */
805 if (surface->resource.allocatedMemory)
806 ERR("The surface already has PBO memory allocated.\n");
808 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
809 checkGLcall("glMapBufferARB");
811 /* Make sure the PBO isn't set anymore in order not to break non-PBO
812 * calls. */
813 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
814 checkGLcall("glBindBufferARB");
816 context_release(context);
819 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
820 surface_set_dirty(surface);
823 static void surface_unmap(struct wined3d_surface *surface)
825 struct wined3d_device *device = surface->resource.device;
827 TRACE("surface %p.\n", surface);
829 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
831 if (surface->flags & SFLAG_PBO)
833 const struct wined3d_gl_info *gl_info;
834 struct wined3d_context *context;
836 TRACE("Freeing PBO memory.\n");
838 context = context_acquire(device, NULL);
839 gl_info = context->gl_info;
841 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
842 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
843 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
844 checkGLcall("glUnmapBufferARB");
845 context_release(context);
847 surface->resource.allocatedMemory = NULL;
850 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
852 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
854 TRACE("Not dirtified, nothing to do.\n");
855 return;
858 if (surface->swapchain && surface->swapchain->front_buffer == surface)
859 surface_load_location(surface, surface->draw_binding);
860 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
861 FIXME("Depth / stencil buffer locking is not implemented.\n");
864 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
866 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
867 return FALSE;
868 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
869 return FALSE;
870 return TRUE;
873 static void surface_depth_blt_fbo(const struct wined3d_device *device,
874 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
875 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
877 const struct wined3d_gl_info *gl_info;
878 struct wined3d_context *context;
879 DWORD src_mask, dst_mask;
880 GLbitfield gl_mask;
882 TRACE("device %p\n", device);
883 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
884 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect));
885 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
886 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect));
888 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
889 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
891 if (src_mask != dst_mask)
893 ERR("Incompatible formats %s and %s.\n",
894 debug_d3dformat(src_surface->resource.format->id),
895 debug_d3dformat(dst_surface->resource.format->id));
896 return;
899 if (!src_mask)
901 ERR("Not a depth / stencil format: %s.\n",
902 debug_d3dformat(src_surface->resource.format->id));
903 return;
906 gl_mask = 0;
907 if (src_mask & WINED3DFMT_FLAG_DEPTH)
908 gl_mask |= GL_DEPTH_BUFFER_BIT;
909 if (src_mask & WINED3DFMT_FLAG_STENCIL)
910 gl_mask |= GL_STENCIL_BUFFER_BIT;
912 /* Make sure the locations are up-to-date. Loading the destination
913 * surface isn't required if the entire surface is overwritten. */
914 surface_load_location(src_surface, src_location);
915 if (!surface_is_full_rect(dst_surface, dst_rect))
916 surface_load_location(dst_surface, dst_location);
918 context = context_acquire(device, NULL);
919 if (!context->valid)
921 context_release(context);
922 WARN("Invalid context, skipping blit.\n");
923 return;
926 gl_info = context->gl_info;
928 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
929 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
931 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
932 context_set_draw_buffer(context, GL_NONE);
933 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
934 context_invalidate_state(context, STATE_FRAMEBUFFER);
936 if (gl_mask & GL_DEPTH_BUFFER_BIT)
938 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
939 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
941 if (gl_mask & GL_STENCIL_BUFFER_BIT)
943 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
945 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
946 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
948 gl_info->gl_ops.gl.p_glStencilMask(~0U);
949 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
952 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
953 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
955 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
956 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
957 checkGLcall("glBlitFramebuffer()");
959 if (wined3d_settings.strict_draw_ordering)
960 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
962 context_release(context);
965 /* Blit between surface locations. Onscreen on different swapchains is not supported.
966 * Depth / stencil is not supported. */
967 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
968 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
969 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
971 const struct wined3d_gl_info *gl_info;
972 struct wined3d_context *context;
973 RECT src_rect, dst_rect;
974 GLenum gl_filter;
975 GLenum buffer;
977 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
978 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
979 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
980 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
981 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
983 src_rect = *src_rect_in;
984 dst_rect = *dst_rect_in;
986 switch (filter)
988 case WINED3D_TEXF_LINEAR:
989 gl_filter = GL_LINEAR;
990 break;
992 default:
993 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
994 case WINED3D_TEXF_NONE:
995 case WINED3D_TEXF_POINT:
996 gl_filter = GL_NEAREST;
997 break;
1000 /* Resolve the source surface first if needed. */
1001 if (src_location == SFLAG_INRB_MULTISAMPLE
1002 && (src_surface->resource.format->id != dst_surface->resource.format->id
1003 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1004 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1005 src_location = SFLAG_INRB_RESOLVED;
1007 /* Make sure the locations are up-to-date. Loading the destination
1008 * surface isn't required if the entire surface is overwritten. (And is
1009 * in fact harmful if we're being called by surface_load_location() with
1010 * the purpose of loading the destination surface.) */
1011 surface_load_location(src_surface, src_location);
1012 if (!surface_is_full_rect(dst_surface, &dst_rect))
1013 surface_load_location(dst_surface, dst_location);
1015 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1016 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1017 else context = context_acquire(device, NULL);
1019 if (!context->valid)
1021 context_release(context);
1022 WARN("Invalid context, skipping blit.\n");
1023 return;
1026 gl_info = context->gl_info;
1028 if (src_location == SFLAG_INDRAWABLE)
1030 TRACE("Source surface %p is onscreen.\n", src_surface);
1031 buffer = surface_get_gl_buffer(src_surface);
1032 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1034 else
1036 TRACE("Source surface %p is offscreen.\n", src_surface);
1037 buffer = GL_COLOR_ATTACHMENT0;
1040 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1041 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1042 checkGLcall("glReadBuffer()");
1043 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1045 if (dst_location == SFLAG_INDRAWABLE)
1047 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1048 buffer = surface_get_gl_buffer(dst_surface);
1049 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1051 else
1053 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1054 buffer = GL_COLOR_ATTACHMENT0;
1057 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1058 context_set_draw_buffer(context, buffer);
1059 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1060 context_invalidate_state(context, STATE_FRAMEBUFFER);
1062 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1063 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
1064 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
1065 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
1066 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
1068 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
1069 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1071 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1072 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1073 checkGLcall("glBlitFramebuffer()");
1075 if (wined3d_settings.strict_draw_ordering
1076 || (dst_location == SFLAG_INDRAWABLE
1077 && dst_surface->swapchain->front_buffer == dst_surface))
1078 gl_info->gl_ops.gl.p_glFlush();
1080 context_release(context);
1083 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1084 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
1085 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
1087 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1088 return FALSE;
1090 /* Source and/or destination need to be on the GL side */
1091 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
1092 return FALSE;
1094 switch (blit_op)
1096 case WINED3D_BLIT_OP_COLOR_BLIT:
1097 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1098 return FALSE;
1099 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1100 return FALSE;
1101 break;
1103 case WINED3D_BLIT_OP_DEPTH_BLIT:
1104 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1105 return FALSE;
1106 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1107 return FALSE;
1108 break;
1110 default:
1111 return FALSE;
1114 if (!(src_format->id == dst_format->id
1115 || (is_identity_fixup(src_format->color_fixup)
1116 && is_identity_fixup(dst_format->color_fixup))))
1117 return FALSE;
1119 return TRUE;
1122 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1123 DWORD color, struct wined3d_color *float_color)
1125 const struct wined3d_format *format = surface->resource.format;
1126 const struct wined3d_device *device = surface->resource.device;
1128 switch (format->id)
1130 case WINED3DFMT_P8_UINT:
1131 if (surface->palette)
1133 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1134 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1135 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1137 else
1139 float_color->r = 0.0f;
1140 float_color->g = 0.0f;
1141 float_color->b = 0.0f;
1143 float_color->a = swapchain_is_p8(device->swapchains[0]) ? color / 255.0f : 1.0f;
1144 break;
1146 case WINED3DFMT_B5G6R5_UNORM:
1147 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1148 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1149 float_color->b = (color & 0x1f) / 31.0f;
1150 float_color->a = 1.0f;
1151 break;
1153 case WINED3DFMT_B8G8R8_UNORM:
1154 case WINED3DFMT_B8G8R8X8_UNORM:
1155 float_color->r = D3DCOLOR_R(color);
1156 float_color->g = D3DCOLOR_G(color);
1157 float_color->b = D3DCOLOR_B(color);
1158 float_color->a = 1.0f;
1159 break;
1161 case WINED3DFMT_B8G8R8A8_UNORM:
1162 float_color->r = D3DCOLOR_R(color);
1163 float_color->g = D3DCOLOR_G(color);
1164 float_color->b = D3DCOLOR_B(color);
1165 float_color->a = D3DCOLOR_A(color);
1166 break;
1168 default:
1169 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1170 return FALSE;
1173 return TRUE;
1176 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1178 const struct wined3d_format *format = surface->resource.format;
1180 switch (format->id)
1182 case WINED3DFMT_S1_UINT_D15_UNORM:
1183 *float_depth = depth / (float)0x00007fff;
1184 break;
1186 case WINED3DFMT_D16_UNORM:
1187 *float_depth = depth / (float)0x0000ffff;
1188 break;
1190 case WINED3DFMT_D24_UNORM_S8_UINT:
1191 case WINED3DFMT_X8D24_UNORM:
1192 *float_depth = depth / (float)0x00ffffff;
1193 break;
1195 case WINED3DFMT_D32_UNORM:
1196 *float_depth = depth / (float)0xffffffff;
1197 break;
1199 default:
1200 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1201 return FALSE;
1204 return TRUE;
1207 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1209 const struct wined3d_resource *resource = &surface->resource;
1210 struct wined3d_device *device = resource->device;
1211 const struct blit_shader *blitter;
1213 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1214 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1215 if (!blitter)
1217 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1218 return WINED3DERR_INVALIDCALL;
1221 return blitter->depth_fill(device, surface, rect, depth);
1224 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1225 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1227 struct wined3d_device *device = src_surface->resource.device;
1229 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1230 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1231 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1232 return WINED3DERR_INVALIDCALL;
1234 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1236 surface_modify_ds_location(dst_surface, dst_location,
1237 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1239 return WINED3D_OK;
1242 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1243 struct wined3d_surface *render_target)
1245 TRACE("surface %p, render_target %p.\n", surface, render_target);
1247 /* TODO: Check surface sizes, pools, etc. */
1249 if (render_target->resource.multisample_type)
1250 return WINED3DERR_INVALIDCALL;
1252 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1255 /* Context activation is done by the caller. */
1256 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1258 if (surface->flags & SFLAG_DIBSECTION)
1260 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1262 else
1264 if (!surface->resource.heap_memory)
1265 wined3d_resource_allocate_sysmem(&surface->resource);
1266 else if (!(surface->flags & SFLAG_CLIENT))
1267 ERR("Surface %p has heap_memory %p and flags %#x.\n",
1268 surface, surface->resource.heap_memory, surface->flags);
1270 surface->resource.allocatedMemory = surface->resource.heap_memory;
1273 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1274 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1275 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1276 surface->resource.size, surface->resource.allocatedMemory));
1277 checkGLcall("glGetBufferSubDataARB");
1278 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1279 checkGLcall("glDeleteBuffersARB");
1281 surface->pbo = 0;
1282 surface->flags &= ~SFLAG_PBO;
1285 static BOOL surface_init_sysmem(struct wined3d_surface *surface)
1287 if (!surface->resource.allocatedMemory)
1289 if (!surface->resource.heap_memory)
1291 if (!wined3d_resource_allocate_sysmem(&surface->resource))
1293 ERR("Failed to allocate system memory.\n");
1294 return FALSE;
1297 else if (!(surface->flags & SFLAG_CLIENT))
1299 ERR("Surface %p has heap_memory %p and flags %#x.\n",
1300 surface, surface->resource.heap_memory, surface->flags);
1303 surface->resource.allocatedMemory = surface->resource.heap_memory;
1305 else
1307 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
1310 surface_validate_location(surface, SFLAG_INSYSMEM);
1311 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
1313 return TRUE;
1316 static void surface_unload(struct wined3d_resource *resource)
1318 struct wined3d_surface *surface = surface_from_resource(resource);
1319 struct wined3d_renderbuffer_entry *entry, *entry2;
1320 struct wined3d_device *device = resource->device;
1321 const struct wined3d_gl_info *gl_info;
1322 struct wined3d_context *context;
1324 TRACE("surface %p.\n", surface);
1326 if (resource->pool == WINED3D_POOL_DEFAULT)
1328 /* Default pool resources are supposed to be destroyed before Reset is called.
1329 * Implicit resources stay however. So this means we have an implicit render target
1330 * or depth stencil. The content may be destroyed, but we still have to tear down
1331 * opengl resources, so we cannot leave early.
1333 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1334 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1335 * or the depth stencil into an FBO the texture or render buffer will be removed
1336 * and all flags get lost
1338 if (!(surface->flags & SFLAG_PBO))
1339 surface_init_sysmem(surface);
1340 /* We also get here when the ddraw swapchain is destroyed, for example
1341 * for a mode switch. In this case this surface won't necessarily be
1342 * an implicit surface. We have to mark it lost so that the
1343 * application can restore it after the mode switch. */
1344 surface->flags |= SFLAG_LOST;
1346 else
1348 /* Load the surface into system memory */
1349 surface_load_location(surface, SFLAG_INSYSMEM);
1350 surface_invalidate_location(surface, surface->draw_binding);
1352 surface_invalidate_location(surface, SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
1353 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1355 context = context_acquire(device, NULL);
1356 gl_info = context->gl_info;
1358 /* Destroy PBOs, but load them into real sysmem before */
1359 if (surface->flags & SFLAG_PBO)
1360 surface_remove_pbo(surface, gl_info);
1362 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1363 * all application-created targets the application has to release the surface
1364 * before calling _Reset
1366 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1368 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1369 list_remove(&entry->entry);
1370 HeapFree(GetProcessHeap(), 0, entry);
1372 list_init(&surface->renderbuffers);
1373 surface->current_renderbuffer = NULL;
1375 if (surface->rb_multisample)
1377 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1378 surface->rb_multisample = 0;
1380 if (surface->rb_resolved)
1382 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1383 surface->rb_resolved = 0;
1386 context_release(context);
1388 resource_unload(resource);
1391 static const struct wined3d_resource_ops surface_resource_ops =
1393 surface_unload,
1396 static const struct wined3d_surface_ops surface_ops =
1398 surface_private_setup,
1399 surface_realize_palette,
1400 surface_map,
1401 surface_unmap,
1404 /*****************************************************************************
1405 * Initializes the GDI surface, aka creates the DIB section we render to
1406 * The DIB section creation is done by calling GetDC, which will create the
1407 * section and releasing the dc to allow the app to use it. The dib section
1408 * will stay until the surface is released
1410 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1411 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1412 * avoid confusion in the shared surface code.
1414 * Returns:
1415 * WINED3D_OK on success
1416 * The return values of called methods on failure
1418 *****************************************************************************/
1419 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1421 HRESULT hr;
1423 TRACE("surface %p.\n", surface);
1425 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1427 ERR("Overlays not yet supported by GDI surfaces.\n");
1428 return WINED3DERR_INVALIDCALL;
1431 /* Sysmem textures have memory already allocated - release it,
1432 * this avoids an unnecessary memcpy. */
1433 hr = surface_create_dib_section(surface);
1434 if (SUCCEEDED(hr))
1436 wined3d_resource_free_sysmem(&surface->resource);
1437 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1440 /* We don't mind the nonpow2 stuff in GDI. */
1441 surface->pow2Width = surface->resource.width;
1442 surface->pow2Height = surface->resource.height;
1444 return WINED3D_OK;
1447 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1449 struct wined3d_palette *palette = surface->palette;
1451 TRACE("surface %p.\n", surface);
1453 if (!palette) return;
1455 if (surface->flags & SFLAG_DIBSECTION)
1457 RGBQUAD col[256];
1458 unsigned int i;
1460 TRACE("Updating the DC's palette.\n");
1462 for (i = 0; i < 256; ++i)
1464 col[i].rgbRed = palette->palents[i].peRed;
1465 col[i].rgbGreen = palette->palents[i].peGreen;
1466 col[i].rgbBlue = palette->palents[i].peBlue;
1467 col[i].rgbReserved = 0;
1469 SetDIBColorTable(surface->hDC, 0, 256, col);
1472 /* Update the image because of the palette change. Some games like e.g.
1473 * Red Alert call SetEntries a lot to implement fading. */
1474 /* Tell the swapchain to update the screen. */
1475 if (surface->swapchain && surface == surface->swapchain->front_buffer)
1476 x11_copy_to_screen(surface->swapchain, NULL);
1479 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
1481 TRACE("surface %p, rect %s, flags %#x.\n",
1482 surface, wine_dbgstr_rect(rect), flags);
1485 static void gdi_surface_unmap(struct wined3d_surface *surface)
1487 TRACE("surface %p.\n", surface);
1489 /* Tell the swapchain to update the screen. */
1490 if (surface->swapchain && surface == surface->swapchain->front_buffer)
1491 x11_copy_to_screen(surface->swapchain, &surface->lockedRect);
1493 memset(&surface->lockedRect, 0, sizeof(RECT));
1496 static const struct wined3d_surface_ops gdi_surface_ops =
1498 gdi_surface_private_setup,
1499 gdi_surface_realize_palette,
1500 gdi_surface_map,
1501 gdi_surface_unmap,
1504 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target, GLint level)
1506 TRACE("surface %p, target %#x.\n", surface, target);
1508 if (surface->texture_target != target)
1510 if (target == GL_TEXTURE_RECTANGLE_ARB)
1512 surface->flags &= ~SFLAG_NORMCOORD;
1514 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
1516 surface->flags |= SFLAG_NORMCOORD;
1519 surface->texture_target = target;
1520 surface->texture_level = level;
1521 surface_force_reload(surface);
1524 /* This call just downloads data, the caller is responsible for binding the
1525 * correct texture. */
1526 /* Context activation is done by the caller. */
1527 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1529 const struct wined3d_format *format = surface->resource.format;
1531 /* Only support read back of converted P8 surfaces. */
1532 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1534 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1535 return;
1538 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1540 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
1541 surface, surface->texture_level, format->glFormat, format->glType,
1542 surface->resource.allocatedMemory);
1544 if (surface->flags & SFLAG_PBO)
1546 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
1547 checkGLcall("glBindBufferARB");
1548 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
1549 checkGLcall("glGetCompressedTexImageARB");
1550 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1551 checkGLcall("glBindBufferARB");
1553 else
1555 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
1556 surface->texture_level, surface->resource.allocatedMemory));
1557 checkGLcall("glGetCompressedTexImageARB");
1560 else
1562 void *mem;
1563 GLenum gl_format = format->glFormat;
1564 GLenum gl_type = format->glType;
1565 int src_pitch = 0;
1566 int dst_pitch = 0;
1568 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
1569 if (format->id == WINED3DFMT_P8_UINT && swapchain_is_p8(surface->resource.device->swapchains[0]))
1571 gl_format = GL_ALPHA;
1572 gl_type = GL_UNSIGNED_BYTE;
1575 if (surface->flags & SFLAG_NONPOW2)
1577 unsigned char alignment = surface->resource.device->surface_alignment;
1578 src_pitch = format->byte_count * surface->pow2Width;
1579 dst_pitch = wined3d_surface_get_pitch(surface);
1580 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1581 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1583 else
1585 mem = surface->resource.allocatedMemory;
1588 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1589 surface, surface->texture_level, gl_format, gl_type, mem);
1591 if (surface->flags & SFLAG_PBO)
1593 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
1594 checkGLcall("glBindBufferARB");
1596 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1597 gl_format, gl_type, NULL);
1598 checkGLcall("glGetTexImage");
1600 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1601 checkGLcall("glBindBufferARB");
1603 else
1605 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1606 gl_format, gl_type, mem);
1607 checkGLcall("glGetTexImage");
1610 if (surface->flags & SFLAG_NONPOW2)
1612 const BYTE *src_data;
1613 BYTE *dst_data;
1614 UINT y;
1616 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1617 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1618 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1620 * We're doing this...
1622 * instead of boxing the texture :
1623 * |<-texture width ->| -->pow2width| /\
1624 * |111111111111111111| | |
1625 * |222 Texture 222222| boxed empty | texture height
1626 * |3333 Data 33333333| | |
1627 * |444444444444444444| | \/
1628 * ----------------------------------- |
1629 * | boxed empty | boxed empty | pow2height
1630 * | | | \/
1631 * -----------------------------------
1634 * we're repacking the data to the expected texture width
1636 * |<-texture width ->| -->pow2width| /\
1637 * |111111111111111111222222222222222| |
1638 * |222333333333333333333444444444444| texture height
1639 * |444444 | |
1640 * | | \/
1641 * | | |
1642 * | empty | pow2height
1643 * | | \/
1644 * -----------------------------------
1646 * == is the same as
1648 * |<-texture width ->| /\
1649 * |111111111111111111|
1650 * |222222222222222222|texture height
1651 * |333333333333333333|
1652 * |444444444444444444| \/
1653 * --------------------
1655 * this also means that any references to allocatedMemory should work with the data as if were a
1656 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
1658 * internally the texture is still stored in a boxed format so any references to textureName will
1659 * get a boxed texture with width pow2width and not a texture of width resource.width.
1661 * Performance should not be an issue, because applications normally do not lock the surfaces when
1662 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
1663 * and doesn't have to be re-read. */
1664 src_data = mem;
1665 dst_data = surface->resource.allocatedMemory;
1666 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1667 for (y = 0; y < surface->resource.height; ++y)
1669 memcpy(dst_data, src_data, dst_pitch);
1670 src_data += src_pitch;
1671 dst_data += dst_pitch;
1674 HeapFree(GetProcessHeap(), 0, mem);
1678 /* Surface has now been downloaded */
1679 surface->flags |= SFLAG_INSYSMEM;
1682 /* This call just uploads data, the caller is responsible for binding the
1683 * correct texture. */
1684 /* Context activation is done by the caller. */
1685 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1686 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1687 BOOL srgb, const struct wined3d_bo_address *data)
1689 UINT update_w = src_rect->right - src_rect->left;
1690 UINT update_h = src_rect->bottom - src_rect->top;
1692 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1693 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1694 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1696 if (surface->resource.map_count)
1698 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
1699 surface->flags |= SFLAG_PIN_SYSMEM;
1702 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1704 update_h *= format->height_scale.numerator;
1705 update_h /= format->height_scale.denominator;
1708 if (data->buffer_object)
1710 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
1711 checkGLcall("glBindBufferARB");
1714 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1716 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1717 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1718 const BYTE *addr = data->addr;
1719 GLenum internal;
1721 addr += (src_rect->top / format->block_height) * src_pitch;
1722 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1724 if (srgb)
1725 internal = format->glGammaInternal;
1726 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
1727 internal = format->rtInternal;
1728 else
1729 internal = format->glInternal;
1731 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
1732 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1733 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1735 if (row_length == src_pitch)
1737 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1738 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1740 else
1742 UINT row, y;
1744 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
1745 * can't use the unpack row length like below. */
1746 for (row = 0, y = dst_point->y; row < row_count; ++row)
1748 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1749 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1750 y += format->block_height;
1751 addr += src_pitch;
1754 checkGLcall("glCompressedTexSubImage2DARB");
1756 else
1758 const BYTE *addr = data->addr;
1760 addr += src_rect->top * src_pitch;
1761 addr += src_rect->left * format->byte_count;
1763 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1764 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1765 update_w, update_h, format->glFormat, format->glType, addr);
1767 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1768 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1769 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1770 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1771 checkGLcall("glTexSubImage2D");
1774 if (data->buffer_object)
1776 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1777 checkGLcall("glBindBufferARB");
1780 if (wined3d_settings.strict_draw_ordering)
1781 gl_info->gl_ops.gl.p_glFlush();
1783 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1785 struct wined3d_device *device = surface->resource.device;
1786 unsigned int i;
1788 for (i = 0; i < device->context_count; ++i)
1790 context_surface_update(device->contexts[i], surface);
1795 static HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck, BOOL use_texturing,
1796 struct wined3d_format *format, enum wined3d_conversion_type *conversion_type)
1798 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
1799 const struct wined3d_device *device = surface->resource.device;
1800 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1801 BOOL blit_supported = FALSE;
1803 /* Copy the default values from the surface. Below we might perform fixups */
1804 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
1805 *format = *surface->resource.format;
1806 *conversion_type = WINED3D_CT_NONE;
1808 /* Ok, now look if we have to do any conversion */
1809 switch (surface->resource.format->id)
1811 case WINED3DFMT_P8_UINT:
1812 /* Below the call to blit_supported is disabled for Wine 1.2
1813 * because the function isn't operating correctly yet. At the
1814 * moment 8-bit blits are handled in software and if certain GL
1815 * extensions are around, surface conversion is performed at
1816 * upload time. The blit_supported call recognizes it as a
1817 * destination fixup. This type of upload 'fixup' and 8-bit to
1818 * 8-bit blits need to be handled by the blit_shader.
1819 * TODO: get rid of this #if 0. */
1820 #if 0
1821 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1822 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
1823 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
1824 #endif
1825 blit_supported = gl_info->supported[ARB_FRAGMENT_PROGRAM];
1827 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
1828 * texturing. Further also use conversion in case of color keying.
1829 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1830 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1831 * conflicts with this.
1833 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
1834 || colorkey_active || !use_texturing)
1836 format->glFormat = GL_RGBA;
1837 format->glInternal = GL_RGBA;
1838 format->glType = GL_UNSIGNED_BYTE;
1839 format->conv_byte_count = 4;
1840 if (colorkey_active)
1841 *conversion_type = WINED3D_CT_PALETTED_CK;
1842 else
1843 *conversion_type = WINED3D_CT_PALETTED;
1845 break;
1847 case WINED3DFMT_B2G3R3_UNORM:
1848 /* **********************
1849 GL_UNSIGNED_BYTE_3_3_2
1850 ********************** */
1851 if (colorkey_active) {
1852 /* This texture format will never be used.. So do not care about color keying
1853 up until the point in time it will be needed :-) */
1854 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1856 break;
1858 case WINED3DFMT_B5G6R5_UNORM:
1859 if (colorkey_active)
1861 *conversion_type = WINED3D_CT_CK_565;
1862 format->glFormat = GL_RGBA;
1863 format->glInternal = GL_RGB5_A1;
1864 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
1865 format->conv_byte_count = 2;
1867 break;
1869 case WINED3DFMT_B5G5R5X1_UNORM:
1870 if (colorkey_active)
1872 *conversion_type = WINED3D_CT_CK_5551;
1873 format->glFormat = GL_BGRA;
1874 format->glInternal = GL_RGB5_A1;
1875 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1876 format->conv_byte_count = 2;
1878 break;
1880 case WINED3DFMT_B8G8R8_UNORM:
1881 if (colorkey_active)
1883 *conversion_type = WINED3D_CT_CK_RGB24;
1884 format->glFormat = GL_RGBA;
1885 format->glInternal = GL_RGBA8;
1886 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1887 format->conv_byte_count = 4;
1889 break;
1891 case WINED3DFMT_B8G8R8X8_UNORM:
1892 if (colorkey_active)
1894 *conversion_type = WINED3D_CT_RGB32_888;
1895 format->glFormat = GL_RGBA;
1896 format->glInternal = GL_RGBA8;
1897 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1898 format->conv_byte_count = 4;
1900 break;
1902 case WINED3DFMT_B8G8R8A8_UNORM:
1903 if (colorkey_active)
1905 *conversion_type = WINED3D_CT_CK_ARGB32;
1906 format->conv_byte_count = 4;
1908 break;
1910 default:
1911 break;
1914 if (*conversion_type != WINED3D_CT_NONE)
1916 format->rtInternal = format->glInternal;
1917 format->glGammaInternal = format->glInternal;
1920 return WINED3D_OK;
1923 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
1925 UINT width_mask, height_mask;
1927 if (!rect->left && !rect->top
1928 && rect->right == surface->resource.width
1929 && rect->bottom == surface->resource.height)
1930 return TRUE;
1932 /* This assumes power of two block sizes, but NPOT block sizes would be
1933 * silly anyway. */
1934 width_mask = surface->resource.format->block_width - 1;
1935 height_mask = surface->resource.format->block_height - 1;
1937 if (!(rect->left & width_mask) && !(rect->top & height_mask)
1938 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
1939 return TRUE;
1941 return FALSE;
1944 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1945 struct wined3d_surface *src_surface, const RECT *src_rect)
1947 const struct wined3d_format *src_format;
1948 const struct wined3d_format *dst_format;
1949 const struct wined3d_gl_info *gl_info;
1950 enum wined3d_conversion_type convert;
1951 struct wined3d_context *context;
1952 struct wined3d_bo_address data;
1953 struct wined3d_format format;
1954 UINT update_w, update_h;
1955 UINT dst_w, dst_h;
1956 RECT r, dst_rect;
1957 UINT src_pitch;
1958 POINT p;
1960 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1961 dst_surface, wine_dbgstr_point(dst_point),
1962 src_surface, wine_dbgstr_rect(src_rect));
1964 src_format = src_surface->resource.format;
1965 dst_format = dst_surface->resource.format;
1967 if (src_format->id != dst_format->id)
1969 WARN("Source and destination surfaces should have the same format.\n");
1970 return WINED3DERR_INVALIDCALL;
1973 if (!dst_point)
1975 p.x = 0;
1976 p.y = 0;
1977 dst_point = &p;
1979 else if (dst_point->x < 0 || dst_point->y < 0)
1981 WARN("Invalid destination point.\n");
1982 return WINED3DERR_INVALIDCALL;
1985 if (!src_rect)
1987 r.left = 0;
1988 r.top = 0;
1989 r.right = src_surface->resource.width;
1990 r.bottom = src_surface->resource.height;
1991 src_rect = &r;
1993 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1994 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1996 WARN("Invalid source rectangle.\n");
1997 return WINED3DERR_INVALIDCALL;
2000 dst_w = dst_surface->resource.width;
2001 dst_h = dst_surface->resource.height;
2003 update_w = src_rect->right - src_rect->left;
2004 update_h = src_rect->bottom - src_rect->top;
2006 if (update_w > dst_w || dst_point->x > dst_w - update_w
2007 || update_h > dst_h || dst_point->y > dst_h - update_h)
2009 WARN("Destination out of bounds.\n");
2010 return WINED3DERR_INVALIDCALL;
2013 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
2015 WARN("Source rectangle not block-aligned.\n");
2016 return WINED3DERR_INVALIDCALL;
2019 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
2020 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
2022 WARN("Destination rectangle not block-aligned.\n");
2023 return WINED3DERR_INVALIDCALL;
2026 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
2027 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
2028 if (convert != WINED3D_CT_NONE || format.convert)
2029 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
2031 context = context_acquire(dst_surface->resource.device, NULL);
2032 gl_info = context->gl_info;
2034 /* Only load the surface for partial updates. For newly allocated texture
2035 * the texture wouldn't be the current location, and we'd upload zeroes
2036 * just to overwrite them again. */
2037 if (update_w == dst_w && update_h == dst_h)
2038 surface_prepare_texture(dst_surface, context, FALSE);
2039 else
2040 surface_load_location(dst_surface, SFLAG_INTEXTURE);
2041 wined3d_texture_bind(dst_surface->container, context, FALSE);
2043 data.buffer_object = src_surface->pbo;
2044 data.addr = src_surface->resource.allocatedMemory;
2045 src_pitch = wined3d_surface_get_pitch(src_surface);
2047 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
2049 context_invalidate_active_texture(context);
2051 context_release(context);
2053 surface_validate_location(dst_surface, SFLAG_INTEXTURE);
2054 surface_invalidate_location(dst_surface, ~SFLAG_INTEXTURE);
2056 return WINED3D_OK;
2059 /* This call just allocates the texture, the caller is responsible for binding
2060 * the correct texture. */
2061 /* Context activation is done by the caller. */
2062 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2063 const struct wined3d_format *format, BOOL srgb)
2065 BOOL disable_client_storage = FALSE;
2066 GLsizei width = surface->pow2Width;
2067 GLsizei height = surface->pow2Height;
2068 const BYTE *mem = NULL;
2069 GLenum internal;
2071 if (srgb)
2073 internal = format->glGammaInternal;
2075 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2077 internal = format->rtInternal;
2079 else
2081 internal = format->glInternal;
2084 if (!internal)
2085 FIXME("No GL internal format for format %s.\n", debug_d3dformat(format->id));
2087 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
2089 height *= format->height_scale.numerator;
2090 height /= format->height_scale.denominator;
2093 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",
2094 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2095 internal, width, height, format->glFormat, format->glType);
2097 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2099 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2100 || !surface->resource.allocatedMemory)
2102 /* In some cases we want to disable client storage.
2103 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2104 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2105 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2106 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2108 surface->flags &= ~SFLAG_CLIENT;
2110 else
2112 surface->flags |= SFLAG_CLIENT;
2114 /* Point OpenGL to our allocated texture memory. Do not use
2115 * resource.allocatedMemory here because it might point into a
2116 * PBO. Instead use heap_memory. */
2117 mem = surface->resource.heap_memory;
2119 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2120 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2121 disable_client_storage = TRUE;
2125 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2127 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2128 internal, width, height, 0, surface->resource.size, mem));
2129 checkGLcall("glCompressedTexImage2DARB");
2131 else
2133 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
2134 internal, width, height, 0, format->glFormat, format->glType, mem);
2135 checkGLcall("glTexImage2D");
2138 if (disable_client_storage)
2140 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2141 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2145 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2146 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2147 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2148 /* Context activation is done by the caller. */
2149 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2151 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2152 struct wined3d_renderbuffer_entry *entry;
2153 GLuint renderbuffer = 0;
2154 unsigned int src_width, src_height;
2155 unsigned int width, height;
2157 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2159 width = rt->pow2Width;
2160 height = rt->pow2Height;
2162 else
2164 width = surface->pow2Width;
2165 height = surface->pow2Height;
2168 src_width = surface->pow2Width;
2169 src_height = surface->pow2Height;
2171 /* A depth stencil smaller than the render target is not valid */
2172 if (width > src_width || height > src_height) return;
2174 /* Remove any renderbuffer set if the sizes match */
2175 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2176 || (width == src_width && height == src_height))
2178 surface->current_renderbuffer = NULL;
2179 return;
2182 /* Look if we've already got a renderbuffer of the correct dimensions */
2183 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2185 if (entry->width == width && entry->height == height)
2187 renderbuffer = entry->id;
2188 surface->current_renderbuffer = entry;
2189 break;
2193 if (!renderbuffer)
2195 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2196 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2197 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2198 surface->resource.format->glInternal, width, height);
2200 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2201 entry->width = width;
2202 entry->height = height;
2203 entry->id = renderbuffer;
2204 list_add_head(&surface->renderbuffers, &entry->entry);
2206 surface->current_renderbuffer = entry;
2209 checkGLcall("set_compatible_renderbuffer");
2212 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2214 const struct wined3d_swapchain *swapchain = surface->swapchain;
2216 TRACE("surface %p.\n", surface);
2218 if (!swapchain)
2220 ERR("Surface %p is not on a swapchain.\n", surface);
2221 return GL_NONE;
2224 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2226 if (swapchain->render_to_fbo)
2228 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2229 return GL_COLOR_ATTACHMENT0;
2231 TRACE("Returning GL_BACK\n");
2232 return GL_BACK;
2234 else if (surface == swapchain->front_buffer)
2236 TRACE("Returning GL_FRONT\n");
2237 return GL_FRONT;
2240 FIXME("Higher back buffer, returning GL_BACK\n");
2241 return GL_BACK;
2244 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2245 void surface_set_dirty(struct wined3d_surface *surface)
2247 TRACE("surface %p.\n", surface);
2249 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2250 surface_load_location(surface, SFLAG_INSYSMEM);
2252 surface_validate_location(surface, SFLAG_INSYSMEM);
2253 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
2255 wined3d_texture_set_dirty(surface->container);
2258 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2260 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2261 BOOL ck_changed;
2263 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2265 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
2267 ERR("Not supported on scratch surfaces.\n");
2268 return WINED3DERR_INVALIDCALL;
2271 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2273 /* Reload if either the texture and sysmem have different ideas about the
2274 * color key, or the actual key values changed. */
2275 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2276 && (surface->gl_color_key.color_space_low_value != surface->src_blt_color_key.color_space_low_value
2277 || surface->gl_color_key.color_space_high_value != surface->src_blt_color_key.color_space_high_value)))
2279 TRACE("Reloading because of color keying\n");
2280 /* To perform the color key conversion we need a sysmem copy of
2281 * the surface. Make sure we have it. */
2283 surface_load_location(surface, SFLAG_INSYSMEM);
2284 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
2285 /* Switching color keying on / off may change the internal format. */
2286 if (ck_changed)
2287 surface_force_reload(surface);
2289 else if (!(surface->flags & flag))
2291 TRACE("Reloading because surface is dirty.\n");
2293 else
2295 TRACE("surface is already in texture\n");
2296 return WINED3D_OK;
2299 surface_load_location(surface, flag);
2300 surface_evict_sysmem(surface);
2302 return WINED3D_OK;
2305 /* See also float_16_to_32() in wined3d_private.h */
2306 static inline unsigned short float_32_to_16(const float *in)
2308 int exp = 0;
2309 float tmp = fabsf(*in);
2310 unsigned int mantissa;
2311 unsigned short ret;
2313 /* Deal with special numbers */
2314 if (*in == 0.0f)
2315 return 0x0000;
2316 if (isnan(*in))
2317 return 0x7c01;
2318 if (isinf(*in))
2319 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2321 if (tmp < powf(2, 10))
2325 tmp = tmp * 2.0f;
2326 exp--;
2327 } while (tmp < powf(2, 10));
2329 else if (tmp >= powf(2, 11))
2333 tmp /= 2.0f;
2334 exp++;
2335 } while (tmp >= powf(2, 11));
2338 mantissa = (unsigned int)tmp;
2339 if (tmp - mantissa >= 0.5f)
2340 ++mantissa; /* Round to nearest, away from zero. */
2342 exp += 10; /* Normalize the mantissa. */
2343 exp += 15; /* Exponent is encoded with excess 15. */
2345 if (exp > 30) /* too big */
2347 ret = 0x7c00; /* INF */
2349 else if (exp <= 0)
2351 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2352 while (exp <= 0)
2354 mantissa = mantissa >> 1;
2355 ++exp;
2357 ret = mantissa & 0x3ff;
2359 else
2361 ret = (exp << 10) | (mantissa & 0x3ff);
2364 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2365 return ret;
2368 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2370 ULONG refcount;
2372 TRACE("surface %p, swapchain %p, container %p.\n",
2373 surface, surface->swapchain, surface->container);
2375 if (surface->swapchain)
2376 return wined3d_swapchain_incref(surface->swapchain);
2378 if (surface->container)
2379 return wined3d_texture_incref(surface->container);
2381 refcount = InterlockedIncrement(&surface->resource.ref);
2382 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2384 return refcount;
2387 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2389 ULONG refcount;
2391 TRACE("surface %p, swapchain %p, container %p.\n",
2392 surface, surface->swapchain, surface->container);
2394 if (surface->swapchain)
2395 return wined3d_swapchain_decref(surface->swapchain);
2397 if (surface->container)
2398 return wined3d_texture_decref(surface->container);
2400 refcount = InterlockedDecrement(&surface->resource.ref);
2401 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2403 if (!refcount)
2405 surface_cleanup(surface);
2406 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2408 TRACE("Destroyed surface %p.\n", surface);
2409 HeapFree(GetProcessHeap(), 0, surface);
2412 return refcount;
2415 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2417 return resource_set_priority(&surface->resource, priority);
2420 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2422 return resource_get_priority(&surface->resource);
2425 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2427 struct wined3d_context *context;
2428 TRACE("surface %p.\n", surface);
2430 if (!surface->resource.device->d3d_initialized)
2432 ERR("D3D not initialized.\n");
2433 return;
2436 context = context_acquire(surface->resource.device, NULL);
2437 surface_internal_preload(surface, context, SRGB_ANY);
2438 context_release(context);
2441 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2443 TRACE("surface %p.\n", surface);
2445 return surface->resource.parent;
2448 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2450 TRACE("surface %p.\n", surface);
2452 return &surface->resource;
2455 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2457 TRACE("surface %p, flags %#x.\n", surface, flags);
2459 switch (flags)
2461 case WINEDDGBS_CANBLT:
2462 case WINEDDGBS_ISBLTDONE:
2463 return WINED3D_OK;
2465 default:
2466 return WINED3DERR_INVALIDCALL;
2470 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2472 TRACE("surface %p, flags %#x.\n", surface, flags);
2474 /* XXX: DDERR_INVALIDSURFACETYPE */
2476 switch (flags)
2478 case WINEDDGFS_CANFLIP:
2479 case WINEDDGFS_ISFLIPDONE:
2480 return WINED3D_OK;
2482 default:
2483 return WINED3DERR_INVALIDCALL;
2487 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2489 TRACE("surface %p.\n", surface);
2491 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2492 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2495 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2497 TRACE("surface %p.\n", surface);
2499 surface->flags &= ~SFLAG_LOST;
2500 return WINED3D_OK;
2503 void CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2505 TRACE("surface %p, palette %p.\n", surface, palette);
2507 if (surface->palette == palette)
2509 TRACE("Nop palette change.\n");
2510 return;
2513 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2514 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2516 surface->palette = palette;
2518 if (palette)
2520 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2521 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
2523 surface->surface_ops->surface_realize_palette(surface);
2527 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
2528 DWORD flags, const struct wined3d_color_key *color_key)
2530 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
2532 if (flags & WINEDDCKEY_COLORSPACE)
2534 FIXME(" colorkey value not supported (%08x) !\n", flags);
2535 return WINED3DERR_INVALIDCALL;
2538 /* Dirtify the surface, but only if a key was changed. */
2539 if (color_key)
2541 switch (flags & ~WINEDDCKEY_COLORSPACE)
2543 case WINEDDCKEY_DESTBLT:
2544 surface->dst_blt_color_key = *color_key;
2545 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
2546 break;
2548 case WINEDDCKEY_DESTOVERLAY:
2549 surface->dst_overlay_color_key = *color_key;
2550 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
2551 break;
2553 case WINEDDCKEY_SRCOVERLAY:
2554 surface->src_overlay_color_key = *color_key;
2555 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
2556 break;
2558 case WINEDDCKEY_SRCBLT:
2559 surface->src_blt_color_key = *color_key;
2560 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
2561 break;
2564 else
2566 switch (flags & ~WINEDDCKEY_COLORSPACE)
2568 case WINEDDCKEY_DESTBLT:
2569 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
2570 break;
2572 case WINEDDCKEY_DESTOVERLAY:
2573 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
2574 break;
2576 case WINEDDCKEY_SRCOVERLAY:
2577 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
2578 break;
2580 case WINEDDCKEY_SRCBLT:
2581 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
2582 break;
2586 return WINED3D_OK;
2589 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
2591 TRACE("surface %p.\n", surface);
2593 return surface->palette;
2596 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2598 const struct wined3d_format *format = surface->resource.format;
2599 DWORD pitch;
2601 TRACE("surface %p.\n", surface);
2603 if (surface->pitch)
2604 return surface->pitch;
2606 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
2608 /* Since compressed formats are block based, pitch means the amount of
2609 * bytes to the next row of block rather than the next row of pixels. */
2610 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
2611 pitch = row_block_count * format->block_byte_count;
2613 else
2615 unsigned char alignment = surface->resource.device->surface_alignment;
2616 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
2617 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2620 TRACE("Returning %u.\n", pitch);
2622 return pitch;
2625 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem, UINT pitch)
2627 TRACE("surface %p, mem %p.\n", surface, mem);
2629 if (surface->resource.map_count || (surface->flags & SFLAG_DCINUSE))
2631 WARN("Surface is mapped or the DC is in use.\n");
2632 return WINED3DERR_INVALIDCALL;
2635 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
2636 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2638 ERR("Not supported on render targets.\n");
2639 return WINED3DERR_INVALIDCALL;
2642 if (mem && mem != surface->resource.allocatedMemory)
2644 /* Do I have to copy the old surface content? */
2645 if (surface->flags & SFLAG_DIBSECTION)
2647 DeleteDC(surface->hDC);
2648 DeleteObject(surface->dib.DIBsection);
2649 surface->dib.bitmap_data = NULL;
2650 surface->resource.allocatedMemory = NULL;
2651 surface->hDC = NULL;
2652 surface->flags &= ~SFLAG_DIBSECTION;
2654 else if (!(surface->flags & SFLAG_USERPTR))
2656 wined3d_resource_free_sysmem(&surface->resource);
2658 surface->resource.allocatedMemory = mem;
2659 surface->flags |= SFLAG_USERPTR;
2661 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
2662 surface_validate_location(surface, SFLAG_INSYSMEM);
2663 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
2665 /* For client textures OpenGL has to be notified. */
2666 if (surface->flags & SFLAG_CLIENT)
2667 surface_release_client_storage(surface);
2669 else if (surface->flags & SFLAG_USERPTR)
2671 /* heap_memory should be NULL already. */
2672 if (surface->resource.heap_memory)
2673 ERR("User pointer surface has heap memory allocated.\n");
2675 if (!mem)
2677 surface->resource.allocatedMemory = NULL;
2678 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
2680 if (surface->flags & SFLAG_CLIENT)
2681 surface_release_client_storage(surface);
2683 surface_prepare_system_memory(surface);
2686 surface_validate_location(surface, SFLAG_INSYSMEM);
2687 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
2690 surface->pitch = pitch;
2692 return WINED3D_OK;
2695 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2697 LONG w, h;
2699 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2701 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2703 WARN("Not an overlay surface.\n");
2704 return WINEDDERR_NOTAOVERLAYSURFACE;
2707 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2708 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2709 surface->overlay_destrect.left = x;
2710 surface->overlay_destrect.top = y;
2711 surface->overlay_destrect.right = x + w;
2712 surface->overlay_destrect.bottom = y + h;
2714 return WINED3D_OK;
2717 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2719 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2721 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2723 TRACE("Not an overlay surface.\n");
2724 return WINEDDERR_NOTAOVERLAYSURFACE;
2727 if (!surface->overlay_dest)
2729 TRACE("Overlay not visible.\n");
2730 *x = 0;
2731 *y = 0;
2732 return WINEDDERR_OVERLAYNOTVISIBLE;
2735 *x = surface->overlay_destrect.left;
2736 *y = surface->overlay_destrect.top;
2738 TRACE("Returning position %d, %d.\n", *x, *y);
2740 return WINED3D_OK;
2743 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2744 DWORD flags, struct wined3d_surface *ref)
2746 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2748 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2750 TRACE("Not an overlay surface.\n");
2751 return WINEDDERR_NOTAOVERLAYSURFACE;
2754 return WINED3D_OK;
2757 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2758 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2760 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2761 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2763 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2765 WARN("Not an overlay surface.\n");
2766 return WINEDDERR_NOTAOVERLAYSURFACE;
2768 else if (!dst_surface)
2770 WARN("Dest surface is NULL.\n");
2771 return WINED3DERR_INVALIDCALL;
2774 if (src_rect)
2776 surface->overlay_srcrect = *src_rect;
2778 else
2780 surface->overlay_srcrect.left = 0;
2781 surface->overlay_srcrect.top = 0;
2782 surface->overlay_srcrect.right = surface->resource.width;
2783 surface->overlay_srcrect.bottom = surface->resource.height;
2786 if (dst_rect)
2788 surface->overlay_destrect = *dst_rect;
2790 else
2792 surface->overlay_destrect.left = 0;
2793 surface->overlay_destrect.top = 0;
2794 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2795 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2798 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2800 surface->overlay_dest = NULL;
2801 list_remove(&surface->overlay_entry);
2804 if (flags & WINEDDOVER_SHOW)
2806 if (surface->overlay_dest != dst_surface)
2808 surface->overlay_dest = dst_surface;
2809 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2812 else if (flags & WINEDDOVER_HIDE)
2814 /* tests show that the rectangles are erased on hide */
2815 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2816 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2817 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2818 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2819 surface->overlay_dest = NULL;
2822 return WINED3D_OK;
2825 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
2826 UINT width, UINT height, enum wined3d_format_id format_id,
2827 enum wined3d_multisample_type multisample_type, UINT multisample_quality)
2829 struct wined3d_device *device = surface->resource.device;
2830 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2831 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
2832 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height, 1);
2834 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u.\n",
2835 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type);
2837 if (!resource_size)
2838 return WINED3DERR_INVALIDCALL;
2840 if (device->d3d_initialized)
2841 surface->resource.resource_ops->resource_unload(&surface->resource);
2843 if (surface->flags & SFLAG_DIBSECTION)
2845 DeleteDC(surface->hDC);
2846 DeleteObject(surface->dib.DIBsection);
2847 surface->dib.bitmap_data = NULL;
2848 surface->flags &= ~SFLAG_DIBSECTION;
2851 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_USERPTR);
2852 surface->resource.allocatedMemory = NULL;
2853 wined3d_resource_free_sysmem(&surface->resource);
2855 surface->resource.width = width;
2856 surface->resource.height = height;
2857 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2858 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2860 surface->pow2Width = width;
2861 surface->pow2Height = height;
2863 else
2865 surface->pow2Width = surface->pow2Height = 1;
2866 while (surface->pow2Width < width)
2867 surface->pow2Width <<= 1;
2868 while (surface->pow2Height < height)
2869 surface->pow2Height <<= 1;
2872 if (surface->pow2Width != width || surface->pow2Height != height)
2873 surface->flags |= SFLAG_NONPOW2;
2874 else
2875 surface->flags &= ~SFLAG_NONPOW2;
2877 surface->resource.format = format;
2878 surface->resource.multisample_type = multisample_type;
2879 surface->resource.multisample_quality = multisample_quality;
2880 surface->resource.size = resource_size;
2882 if (!surface_init_sysmem(surface))
2883 return E_OUTOFMEMORY;
2885 return WINED3D_OK;
2888 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2889 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2891 unsigned short *dst_s;
2892 const float *src_f;
2893 unsigned int x, y;
2895 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2897 for (y = 0; y < h; ++y)
2899 src_f = (const float *)(src + y * pitch_in);
2900 dst_s = (unsigned short *) (dst + y * pitch_out);
2901 for (x = 0; x < w; ++x)
2903 dst_s[x] = float_32_to_16(src_f + x);
2908 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2909 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2911 static const unsigned char convert_5to8[] =
2913 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2914 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2915 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2916 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2918 static const unsigned char convert_6to8[] =
2920 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2921 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2922 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2923 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2924 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2925 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2926 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2927 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2929 unsigned int x, y;
2931 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2933 for (y = 0; y < h; ++y)
2935 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2936 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2937 for (x = 0; x < w; ++x)
2939 WORD pixel = src_line[x];
2940 dst_line[x] = 0xff000000
2941 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2942 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2943 | convert_5to8[(pixel & 0x001f)];
2948 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2949 * in both cases we're just setting the X / Alpha channel to 0xff. */
2950 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2951 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2953 unsigned int x, y;
2955 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2957 for (y = 0; y < h; ++y)
2959 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2960 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2962 for (x = 0; x < w; ++x)
2964 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2969 static inline BYTE cliptobyte(int x)
2971 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2974 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2975 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2977 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2978 unsigned int x, y;
2980 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2982 for (y = 0; y < h; ++y)
2984 const BYTE *src_line = src + y * pitch_in;
2985 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2986 for (x = 0; x < w; ++x)
2988 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2989 * C = Y - 16; D = U - 128; E = V - 128;
2990 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2991 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2992 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2993 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2994 * U and V are shared between the pixels. */
2995 if (!(x & 1)) /* For every even pixel, read new U and V. */
2997 d = (int) src_line[1] - 128;
2998 e = (int) src_line[3] - 128;
2999 r2 = 409 * e + 128;
3000 g2 = - 100 * d - 208 * e + 128;
3001 b2 = 516 * d + 128;
3003 c2 = 298 * ((int) src_line[0] - 16);
3004 dst_line[x] = 0xff000000
3005 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3006 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3007 | cliptobyte((c2 + b2) >> 8); /* blue */
3008 /* Scale RGB values to 0..255 range,
3009 * then clip them if still not in range (may be negative),
3010 * then shift them within DWORD if necessary. */
3011 src_line += 2;
3016 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3017 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3019 unsigned int x, y;
3020 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3022 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3024 for (y = 0; y < h; ++y)
3026 const BYTE *src_line = src + y * pitch_in;
3027 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3028 for (x = 0; x < w; ++x)
3030 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3031 * C = Y - 16; D = U - 128; E = V - 128;
3032 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3033 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3034 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3035 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3036 * U and V are shared between the pixels. */
3037 if (!(x & 1)) /* For every even pixel, read new U and V. */
3039 d = (int) src_line[1] - 128;
3040 e = (int) src_line[3] - 128;
3041 r2 = 409 * e + 128;
3042 g2 = - 100 * d - 208 * e + 128;
3043 b2 = 516 * d + 128;
3045 c2 = 298 * ((int) src_line[0] - 16);
3046 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3047 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3048 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3049 /* Scale RGB values to 0..255 range,
3050 * then clip them if still not in range (may be negative),
3051 * then shift them within DWORD if necessary. */
3052 src_line += 2;
3057 struct d3dfmt_converter_desc
3059 enum wined3d_format_id from, to;
3060 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3063 static const struct d3dfmt_converter_desc converters[] =
3065 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3066 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3067 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3068 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3069 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3070 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3073 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
3074 enum wined3d_format_id to)
3076 unsigned int i;
3078 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
3080 if (converters[i].from == from && converters[i].to == to)
3081 return &converters[i];
3084 return NULL;
3087 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3089 struct wined3d_map_desc src_map, dst_map;
3090 const struct d3dfmt_converter_desc *conv;
3091 struct wined3d_texture *ret = NULL;
3092 struct wined3d_resource_desc desc;
3093 struct wined3d_surface *dst;
3095 conv = find_converter(source->resource.format->id, to_fmt);
3096 if (!conv)
3098 FIXME("Cannot find a conversion function from format %s to %s.\n",
3099 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3100 return NULL;
3103 /* FIXME: Multisampled conversion? */
3104 wined3d_resource_get_desc(&source->resource, &desc);
3105 desc.format = to_fmt;
3106 desc.usage = 0;
3107 desc.pool = WINED3D_POOL_SCRATCH;
3108 if (FAILED(wined3d_texture_create_2d(source->resource.device, &desc, 1,
3109 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, &wined3d_null_parent_ops, &ret)))
3111 ERR("Failed to create a destination surface for conversion.\n");
3112 return NULL;
3114 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
3116 memset(&src_map, 0, sizeof(src_map));
3117 memset(&dst_map, 0, sizeof(dst_map));
3119 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
3121 ERR("Failed to lock the source surface.\n");
3122 wined3d_texture_decref(ret);
3123 return NULL;
3125 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
3127 ERR("Failed to lock the destination surface.\n");
3128 wined3d_surface_unmap(source);
3129 wined3d_texture_decref(ret);
3130 return NULL;
3133 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
3134 source->resource.width, source->resource.height);
3136 wined3d_surface_unmap(dst);
3137 wined3d_surface_unmap(source);
3139 return ret;
3142 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3143 unsigned int bpp, UINT pitch, DWORD color)
3145 BYTE *first;
3146 unsigned int x, y;
3148 /* Do first row */
3150 #define COLORFILL_ROW(type) \
3151 do { \
3152 type *d = (type *)buf; \
3153 for (x = 0; x < width; ++x) \
3154 d[x] = (type)color; \
3155 } while(0)
3157 switch (bpp)
3159 case 1:
3160 COLORFILL_ROW(BYTE);
3161 break;
3163 case 2:
3164 COLORFILL_ROW(WORD);
3165 break;
3167 case 3:
3169 BYTE *d = buf;
3170 for (x = 0; x < width; ++x, d += 3)
3172 d[0] = (color ) & 0xff;
3173 d[1] = (color >> 8) & 0xff;
3174 d[2] = (color >> 16) & 0xff;
3176 break;
3178 case 4:
3179 COLORFILL_ROW(DWORD);
3180 break;
3182 default:
3183 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3184 return WINED3DERR_NOTAVAILABLE;
3187 #undef COLORFILL_ROW
3189 /* Now copy first row. */
3190 first = buf;
3191 for (y = 1; y < height; ++y)
3193 buf += pitch;
3194 memcpy(buf, first, width * bpp);
3197 return WINED3D_OK;
3200 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
3202 return surface_from_resource(resource);
3205 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3207 TRACE("surface %p.\n", surface);
3209 if (!surface->resource.map_count)
3211 WARN("Trying to unmap unmapped surface.\n");
3212 return WINEDDERR_NOTLOCKED;
3214 --surface->resource.map_count;
3216 surface->surface_ops->surface_unmap(surface);
3218 return WINED3D_OK;
3221 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3222 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
3224 const struct wined3d_format *format = surface->resource.format;
3226 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
3227 surface, map_desc, wine_dbgstr_rect(rect), flags);
3229 if (surface->resource.map_count)
3231 WARN("Surface is already mapped.\n");
3232 return WINED3DERR_INVALIDCALL;
3235 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
3236 && !surface_check_block_align(surface, rect))
3238 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3239 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3241 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
3242 return WINED3DERR_INVALIDCALL;
3245 ++surface->resource.map_count;
3247 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
3248 WARN("Trying to lock unlockable surface.\n");
3250 /* Performance optimization: Count how often a surface is mapped, if it is
3251 * mapped regularly do not throw away the system memory copy. This avoids
3252 * the need to download the surface from OpenGL all the time. The surface
3253 * is still downloaded if the OpenGL texture is changed. */
3254 if (!(surface->flags & SFLAG_DYNLOCK))
3256 if (++surface->lockCount > MAXLOCKCOUNT)
3258 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3259 surface->flags |= SFLAG_DYNLOCK;
3263 surface->surface_ops->surface_map(surface, rect, flags);
3265 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3266 map_desc->row_pitch = surface->resource.width * format->byte_count;
3267 else
3268 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
3269 map_desc->slice_pitch = 0;
3271 if (!rect)
3273 map_desc->data = surface->resource.allocatedMemory;
3274 surface->lockedRect.left = 0;
3275 surface->lockedRect.top = 0;
3276 surface->lockedRect.right = surface->resource.width;
3277 surface->lockedRect.bottom = surface->resource.height;
3279 else
3281 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3283 /* Compressed textures are block based, so calculate the offset of
3284 * the block that contains the top-left pixel of the locked rectangle. */
3285 map_desc->data = surface->resource.allocatedMemory
3286 + ((rect->top / format->block_height) * map_desc->row_pitch)
3287 + ((rect->left / format->block_width) * format->block_byte_count);
3289 else
3291 map_desc->data = surface->resource.allocatedMemory
3292 + (map_desc->row_pitch * rect->top)
3293 + (rect->left * format->byte_count);
3295 surface->lockedRect.left = rect->left;
3296 surface->lockedRect.top = rect->top;
3297 surface->lockedRect.right = rect->right;
3298 surface->lockedRect.bottom = rect->bottom;
3301 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3302 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
3304 return WINED3D_OK;
3307 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3309 struct wined3d_map_desc map;
3310 HRESULT hr;
3312 TRACE("surface %p, dc %p.\n", surface, dc);
3314 if (surface->flags & SFLAG_USERPTR)
3316 ERR("Not supported on surfaces with application-provided memory.\n");
3317 return WINEDDERR_NODC;
3320 /* Give more detailed info for ddraw. */
3321 if (surface->flags & SFLAG_DCINUSE)
3322 return WINEDDERR_DCALREADYCREATED;
3324 /* Can't GetDC if the surface is locked. */
3325 if (surface->resource.map_count)
3326 return WINED3DERR_INVALIDCALL;
3328 /* Create a DIB section if there isn't a dc yet. */
3329 if (!surface->hDC)
3331 if (surface->flags & SFLAG_CLIENT)
3333 surface_load_location(surface, SFLAG_INSYSMEM);
3334 surface_release_client_storage(surface);
3336 hr = surface_create_dib_section(surface);
3337 if (FAILED(hr))
3338 return WINED3DERR_INVALIDCALL;
3340 /* Use the DIB section from now on if we are not using a PBO. */
3341 if (!(surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)))
3343 wined3d_resource_free_sysmem(&surface->resource);
3344 surface->resource.allocatedMemory = surface->dib.bitmap_data;
3348 /* Map the surface. */
3349 hr = wined3d_surface_map(surface, &map, NULL, 0);
3350 if (FAILED(hr))
3352 ERR("Map failed, hr %#x.\n", hr);
3353 return hr;
3356 /* Sync the DIB with the PBO. This can't be done earlier because Map()
3357 * activates the allocatedMemory. */
3358 if (surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM))
3359 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
3361 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3362 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3364 /* GetDC on palettized formats is unsupported in D3D9, and the method
3365 * is missing in D3D8, so this should only be used for DX <=7
3366 * surfaces (with non-device palettes). */
3367 const PALETTEENTRY *pal = NULL;
3369 if (surface->palette)
3371 pal = surface->palette->palents;
3373 else
3375 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3376 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3378 if (dds_primary && dds_primary->palette)
3379 pal = dds_primary->palette->palents;
3382 if (pal)
3384 RGBQUAD col[256];
3385 unsigned int i;
3387 for (i = 0; i < 256; ++i)
3389 col[i].rgbRed = pal[i].peRed;
3390 col[i].rgbGreen = pal[i].peGreen;
3391 col[i].rgbBlue = pal[i].peBlue;
3392 col[i].rgbReserved = 0;
3394 SetDIBColorTable(surface->hDC, 0, 256, col);
3398 surface->flags |= SFLAG_DCINUSE;
3400 *dc = surface->hDC;
3401 TRACE("Returning dc %p.\n", *dc);
3403 return WINED3D_OK;
3406 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3408 TRACE("surface %p, dc %p.\n", surface, dc);
3410 if (!(surface->flags & SFLAG_DCINUSE))
3411 return WINEDDERR_NODC;
3413 if (surface->hDC != dc)
3415 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3416 dc, surface->hDC);
3417 return WINEDDERR_NODC;
3420 /* Copy the contents of the DIB over to the PBO. */
3421 if ((surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)) && surface->resource.allocatedMemory)
3422 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3424 /* We locked first, so unlock now. */
3425 wined3d_surface_unmap(surface);
3427 surface->flags &= ~SFLAG_DCINUSE;
3429 return WINED3D_OK;
3432 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3434 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3436 if (flags)
3438 static UINT once;
3439 if (!once++)
3440 FIXME("Ignoring flags %#x.\n", flags);
3441 else
3442 WARN("Ignoring flags %#x.\n", flags);
3445 if (surface->swapchain)
3447 ERR("Not supported on swapchain surfaces.\n");
3448 return WINEDDERR_NOTFLIPPABLE;
3451 flip_surface(surface, override);
3452 return WINED3D_OK;
3455 /* Context activation is done by the caller */
3456 void surface_internal_preload(struct wined3d_surface *surface,
3457 struct wined3d_context *context, enum WINED3DSRGB srgb)
3459 struct wined3d_texture *texture = surface->container;
3461 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3463 texture->texture_ops->texture_preload(texture, context, srgb);
3466 /* Read the framebuffer back into the surface */
3467 static void read_from_framebuffer(struct wined3d_surface *surface, void *dest, UINT pitch)
3469 struct wined3d_device *device = surface->resource.device;
3470 const struct wined3d_gl_info *gl_info;
3471 struct wined3d_context *context;
3472 BYTE *mem;
3473 GLint fmt;
3474 GLint type;
3475 BYTE *row, *top, *bottom;
3476 int i;
3477 BOOL bpp;
3478 BOOL srcIsUpsideDown;
3479 GLint rowLen = 0;
3480 GLint skipPix = 0;
3481 GLint skipRow = 0;
3483 context = context_acquire(device, surface);
3484 context_apply_blit_state(context, device);
3485 gl_info = context->gl_info;
3487 /* Select the correct read buffer, and give some debug output.
3488 * There is no need to keep track of the current read buffer or reset it, every part of the code
3489 * that reads sets the read buffer as desired.
3491 if (surface_is_offscreen(surface))
3493 /* Mapping the primary render target which is not on a swapchain.
3494 * Read from the back buffer. */
3495 TRACE("Mapping offscreen render target.\n");
3496 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3497 srcIsUpsideDown = TRUE;
3499 else
3501 /* Onscreen surfaces are always part of a swapchain */
3502 GLenum buffer = surface_get_gl_buffer(surface);
3503 TRACE("Mapping %#x buffer.\n", buffer);
3504 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
3505 checkGLcall("glReadBuffer");
3506 srcIsUpsideDown = FALSE;
3509 switch (surface->resource.format->id)
3511 case WINED3DFMT_P8_UINT:
3513 if (swapchain_is_p8(context->swapchain))
3515 /* In case of P8 render targets the index is stored in the alpha component */
3516 fmt = GL_ALPHA;
3517 type = GL_UNSIGNED_BYTE;
3518 mem = dest;
3519 bpp = surface->resource.format->byte_count;
3521 else
3523 /* GL can't return palettized data, so read ARGB pixels into a
3524 * separate block of memory and convert them into palettized format
3525 * in software. Slow, but if the app means to use palettized render
3526 * targets and locks it...
3528 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
3529 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
3530 * for the color channels when palettizing the colors.
3532 fmt = GL_RGB;
3533 type = GL_UNSIGNED_BYTE;
3534 pitch *= 3;
3535 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
3536 if (!mem)
3538 ERR("Out of memory\n");
3539 return;
3541 bpp = surface->resource.format->byte_count * 3;
3544 break;
3546 default:
3547 mem = dest;
3548 fmt = surface->resource.format->glFormat;
3549 type = surface->resource.format->glType;
3550 bpp = surface->resource.format->byte_count;
3553 if (surface->flags & SFLAG_PBO)
3555 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
3556 checkGLcall("glBindBufferARB");
3557 if (mem)
3559 ERR("mem not null for pbo -- unexpected\n");
3560 mem = NULL;
3564 /* Save old pixel store pack state */
3565 gl_info->gl_ops.gl.p_glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
3566 checkGLcall("glGetIntegerv");
3567 gl_info->gl_ops.gl.p_glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
3568 checkGLcall("glGetIntegerv");
3569 gl_info->gl_ops.gl.p_glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
3570 checkGLcall("glGetIntegerv");
3572 /* Setup pixel store pack state -- to glReadPixels into the correct place */
3573 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
3574 checkGLcall("glPixelStorei");
3575 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
3576 checkGLcall("glPixelStorei");
3577 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_ROWS, 0);
3578 checkGLcall("glPixelStorei");
3580 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
3581 surface->resource.width, surface->resource.height,
3582 fmt, type, mem);
3583 checkGLcall("glReadPixels");
3585 /* Reset previous pixel store pack state */
3586 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
3587 checkGLcall("glPixelStorei");
3588 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
3589 checkGLcall("glPixelStorei");
3590 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
3591 checkGLcall("glPixelStorei");
3593 if (surface->flags & SFLAG_PBO)
3595 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
3596 checkGLcall("glBindBufferARB");
3598 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
3599 * to get a pointer to it and perform the flipping in software. This is a lot
3600 * faster than calling glReadPixels for each line. In case we want more speed
3601 * we should rerender it flipped in a FBO and read the data back from the FBO. */
3602 if (!srcIsUpsideDown)
3604 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
3605 checkGLcall("glBindBufferARB");
3607 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3608 checkGLcall("glMapBufferARB");
3612 /* TODO: Merge this with the palettization loop below for P8 targets */
3613 if (!srcIsUpsideDown)
3615 UINT len;
3616 /* glReadPixels returns the image upside down, and there is no way to prevent this.
3617 Flip the lines in software */
3618 len = surface->resource.width * bpp;
3620 row = HeapAlloc(GetProcessHeap(), 0, len);
3621 if (!row)
3623 ERR("Out of memory\n");
3624 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
3625 HeapFree(GetProcessHeap(), 0, mem);
3626 return;
3629 top = mem;
3630 bottom = mem + pitch * (surface->resource.height - 1);
3631 for (i = 0; i < surface->resource.height / 2; i++)
3633 memcpy(row, top, len);
3634 memcpy(top, bottom, len);
3635 memcpy(bottom, row, len);
3636 top += pitch;
3637 bottom -= pitch;
3639 HeapFree(GetProcessHeap(), 0, row);
3641 /* Unmap the temp PBO buffer */
3642 if (surface->flags & SFLAG_PBO)
3644 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
3645 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
3649 /* For P8 textures we need to perform an inverse palette lookup. This is
3650 * done by searching for a palette index which matches the RGB value.
3651 * Note this isn't guaranteed to work when there are multiple entries for
3652 * the same color but we have no choice. In case of P8 render targets,
3653 * the index is stored in the alpha component so no conversion is needed. */
3654 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !swapchain_is_p8(context->swapchain))
3656 const PALETTEENTRY *pal = NULL;
3657 DWORD width = pitch / 3;
3658 int x, y, c;
3660 if (surface->palette)
3662 pal = surface->palette->palents;
3664 else
3666 ERR("Palette is missing, cannot perform inverse palette lookup\n");
3667 HeapFree(GetProcessHeap(), 0, mem);
3668 return;
3671 for (y = 0; y < surface->resource.height; y++)
3673 for (x = 0; x < surface->resource.width; x++)
3675 /* start lines pixels */
3676 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
3677 const BYTE *green = blue + 1;
3678 const BYTE *red = green + 1;
3680 for (c = 0; c < 256; c++)
3682 if (*red == pal[c].peRed
3683 && *green == pal[c].peGreen
3684 && *blue == pal[c].peBlue)
3686 *((BYTE *) dest + y * width + x) = c;
3687 break;
3692 HeapFree(GetProcessHeap(), 0, mem);
3695 context_release(context);
3698 /* Read the framebuffer contents into a texture. Note that this function
3699 * doesn't do any kind of flipping. Using this on an onscreen surface will
3700 * result in a flipped D3D texture. */
3701 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
3703 struct wined3d_device *device = surface->resource.device;
3704 const struct wined3d_gl_info *gl_info;
3705 struct wined3d_context *context;
3707 context = context_acquire(device, surface);
3708 gl_info = context->gl_info;
3709 device_invalidate_state(device, STATE_FRAMEBUFFER);
3711 surface_prepare_texture(surface, context, srgb);
3712 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3714 TRACE("Reading back offscreen render target %p.\n", surface);
3716 if (surface_is_offscreen(surface))
3717 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3718 else
3719 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
3720 checkGLcall("glReadBuffer");
3722 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
3723 0, 0, 0, 0, surface->resource.width, surface->resource.height);
3724 checkGLcall("glCopyTexSubImage2D");
3726 context_release(context);
3729 /* Context activation is done by the caller. */
3730 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
3731 struct wined3d_context *context, BOOL srgb)
3733 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
3734 enum wined3d_conversion_type convert;
3735 struct wined3d_format format;
3737 if (surface->flags & alloc_flag) return;
3739 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
3740 if (convert != WINED3D_CT_NONE || format.convert)
3741 surface->flags |= SFLAG_CONVERTED;
3742 else surface->flags &= ~SFLAG_CONVERTED;
3744 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3745 surface_allocate_surface(surface, context->gl_info, &format, srgb);
3746 surface->flags |= alloc_flag;
3749 /* Context activation is done by the caller. */
3750 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
3752 struct wined3d_texture *texture = surface->container;
3753 UINT sub_count = texture->level_count * texture->layer_count;
3754 UINT i;
3756 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
3758 for (i = 0; i < sub_count; ++i)
3760 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
3761 surface_prepare_texture_internal(s, context, srgb);
3764 return;
3767 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
3769 if (multisample)
3771 if (surface->rb_multisample)
3772 return;
3774 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
3775 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
3776 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
3777 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
3778 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
3780 else
3782 if (surface->rb_resolved)
3783 return;
3785 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
3786 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
3787 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
3788 surface->pow2Width, surface->pow2Height);
3789 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
3793 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
3795 /* FIXME: Is this really how color keys are supposed to work? I think it
3796 * makes more sense to compare the individual channels. */
3797 return color >= color_key->color_space_low_value
3798 && color <= color_key->color_space_high_value;
3801 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
3803 const struct wined3d_device *device = surface->resource.device;
3804 const struct wined3d_palette *pal = surface->palette;
3805 BOOL index_in_alpha = FALSE;
3806 unsigned int i;
3808 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
3809 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
3810 * is slow. Further RGB->P8 conversion is not possible because palettes can have
3811 * duplicate entries. Store the color key in the unused alpha component to speed the
3812 * download up and to make conversion unneeded. */
3813 index_in_alpha = swapchain_is_p8(device->swapchains[0]);
3815 if (!pal)
3817 FIXME("No palette set.\n");
3818 if (index_in_alpha)
3820 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
3821 * there's no palette at this time. */
3822 for (i = 0; i < 256; i++) table[i][3] = i;
3825 else
3827 TRACE("Using surface palette %p\n", pal);
3828 /* Get the surface's palette */
3829 for (i = 0; i < 256; ++i)
3831 table[i][0] = pal->palents[i].peRed;
3832 table[i][1] = pal->palents[i].peGreen;
3833 table[i][2] = pal->palents[i].peBlue;
3835 /* When index_in_alpha is set the palette index is stored in the
3836 * alpha component. In case of a readback we can then read
3837 * GL_ALPHA. Color keying is handled in surface_blt_special() using a
3838 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
3839 * color key itself is passed to glAlphaFunc in other cases the
3840 * alpha component of pixels that should be masked away is set to 0. */
3841 if (index_in_alpha)
3842 table[i][3] = i;
3843 else if (colorkey && color_in_range(&surface->src_blt_color_key, i))
3844 table[i][3] = 0x00;
3845 else if (pal->flags & WINEDDPCAPS_ALPHA)
3846 table[i][3] = pal->palents[i].peFlags;
3847 else
3848 table[i][3] = 0xff;
3853 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
3854 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
3856 const BYTE *source;
3857 BYTE *dest;
3859 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
3860 src, dst, pitch, width, height, outpitch, conversion_type, surface);
3862 switch (conversion_type)
3864 case WINED3D_CT_NONE:
3866 memcpy(dst, src, pitch * height);
3867 break;
3870 case WINED3D_CT_PALETTED:
3871 case WINED3D_CT_PALETTED_CK:
3873 BYTE table[256][4];
3874 unsigned int x, y;
3876 d3dfmt_p8_init_palette(surface, table, (conversion_type == WINED3D_CT_PALETTED_CK));
3878 for (y = 0; y < height; y++)
3880 source = src + pitch * y;
3881 dest = dst + outpitch * y;
3882 /* This is an 1 bpp format, using the width here is fine */
3883 for (x = 0; x < width; x++) {
3884 BYTE color = *source++;
3885 *dest++ = table[color][0];
3886 *dest++ = table[color][1];
3887 *dest++ = table[color][2];
3888 *dest++ = table[color][3];
3892 break;
3894 case WINED3D_CT_CK_565:
3896 /* Converting the 565 format in 5551 packed to emulate color-keying.
3898 Note : in all these conversion, it would be best to average the averaging
3899 pixels to get the color of the pixel that will be color-keyed to
3900 prevent 'color bleeding'. This will be done later on if ever it is
3901 too visible.
3903 Note2: Nvidia documents say that their driver does not support alpha + color keying
3904 on the same surface and disables color keying in such a case
3906 unsigned int x, y;
3907 const WORD *Source;
3908 WORD *Dest;
3910 TRACE("Color keyed 565\n");
3912 for (y = 0; y < height; y++) {
3913 Source = (const WORD *)(src + y * pitch);
3914 Dest = (WORD *) (dst + y * outpitch);
3915 for (x = 0; x < width; x++ ) {
3916 WORD color = *Source++;
3917 *Dest = ((color & 0xffc0) | ((color & 0x1f) << 1));
3918 if (!color_in_range(&surface->src_blt_color_key, color))
3919 *Dest |= 0x0001;
3920 Dest++;
3924 break;
3926 case WINED3D_CT_CK_5551:
3928 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
3929 unsigned int x, y;
3930 const WORD *Source;
3931 WORD *Dest;
3932 TRACE("Color keyed 5551\n");
3933 for (y = 0; y < height; y++) {
3934 Source = (const WORD *)(src + y * pitch);
3935 Dest = (WORD *) (dst + y * outpitch);
3936 for (x = 0; x < width; x++ ) {
3937 WORD color = *Source++;
3938 *Dest = color;
3939 if (!color_in_range(&surface->src_blt_color_key, color))
3940 *Dest |= (1 << 15);
3941 else
3942 *Dest &= ~(1 << 15);
3943 Dest++;
3947 break;
3949 case WINED3D_CT_CK_RGB24:
3951 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
3952 unsigned int x, y;
3953 for (y = 0; y < height; y++)
3955 source = src + pitch * y;
3956 dest = dst + outpitch * y;
3957 for (x = 0; x < width; x++) {
3958 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
3959 DWORD dstcolor = color << 8;
3960 if (!color_in_range(&surface->src_blt_color_key, color))
3961 dstcolor |= 0xff;
3962 *(DWORD*)dest = dstcolor;
3963 source += 3;
3964 dest += 4;
3968 break;
3970 case WINED3D_CT_RGB32_888:
3972 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
3973 unsigned int x, y;
3974 for (y = 0; y < height; y++)
3976 source = src + pitch * y;
3977 dest = dst + outpitch * y;
3978 for (x = 0; x < width; x++) {
3979 DWORD color = 0xffffff & *(const DWORD*)source;
3980 DWORD dstcolor = color << 8;
3981 if (!color_in_range(&surface->src_blt_color_key, color))
3982 dstcolor |= 0xff;
3983 *(DWORD*)dest = dstcolor;
3984 source += 4;
3985 dest += 4;
3989 break;
3991 case WINED3D_CT_CK_ARGB32:
3993 unsigned int x, y;
3994 for (y = 0; y < height; ++y)
3996 source = src + pitch * y;
3997 dest = dst + outpitch * y;
3998 for (x = 0; x < width; ++x)
4000 DWORD color = *(const DWORD *)source;
4001 if (color_in_range(&surface->src_blt_color_key, color))
4002 color &= ~0xff000000;
4003 *(DWORD*)dest = color;
4004 source += 4;
4005 dest += 4;
4009 break;
4011 default:
4012 ERR("Unsupported conversion type %#x.\n", conversion_type);
4014 return WINED3D_OK;
4017 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4019 /* Flip the surface contents */
4020 /* Flip the DC */
4022 HDC tmp;
4023 tmp = front->hDC;
4024 front->hDC = back->hDC;
4025 back->hDC = tmp;
4028 /* Flip the DIBsection */
4030 HBITMAP tmp = front->dib.DIBsection;
4031 front->dib.DIBsection = back->dib.DIBsection;
4032 back->dib.DIBsection = tmp;
4035 /* Flip the surface data */
4037 void* tmp;
4039 tmp = front->dib.bitmap_data;
4040 front->dib.bitmap_data = back->dib.bitmap_data;
4041 back->dib.bitmap_data = tmp;
4043 tmp = front->resource.allocatedMemory;
4044 front->resource.allocatedMemory = back->resource.allocatedMemory;
4045 back->resource.allocatedMemory = tmp;
4047 tmp = front->resource.heap_memory;
4048 front->resource.heap_memory = back->resource.heap_memory;
4049 back->resource.heap_memory = tmp;
4052 /* Flip the PBO */
4054 GLuint tmp_pbo = front->pbo;
4055 front->pbo = back->pbo;
4056 back->pbo = tmp_pbo;
4059 /* Flip the opengl texture */
4061 GLuint tmp;
4063 tmp = back->rb_multisample;
4064 back->rb_multisample = front->rb_multisample;
4065 front->rb_multisample = tmp;
4067 tmp = back->rb_resolved;
4068 back->rb_resolved = front->rb_resolved;
4069 front->rb_resolved = tmp;
4071 resource_unload(&back->resource);
4072 resource_unload(&front->resource);
4076 DWORD tmp_flags = back->flags;
4077 back->flags = front->flags;
4078 front->flags = tmp_flags;
4082 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4083 * pixel copy calls. */
4084 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4085 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4087 struct wined3d_device *device = dst_surface->resource.device;
4088 const struct wined3d_gl_info *gl_info;
4089 float xrel, yrel;
4090 struct wined3d_context *context;
4091 BOOL upsidedown = FALSE;
4092 RECT dst_rect = *dst_rect_in;
4094 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4095 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4097 if(dst_rect.top > dst_rect.bottom) {
4098 UINT tmp = dst_rect.bottom;
4099 dst_rect.bottom = dst_rect.top;
4100 dst_rect.top = tmp;
4101 upsidedown = TRUE;
4104 context = context_acquire(device, src_surface);
4105 gl_info = context->gl_info;
4106 context_apply_blit_state(context, device);
4107 surface_internal_preload(dst_surface, context, SRGB_RGB);
4109 /* Bind the target texture */
4110 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
4111 if (surface_is_offscreen(src_surface))
4113 TRACE("Reading from an offscreen target\n");
4114 upsidedown = !upsidedown;
4115 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
4117 else
4119 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
4121 checkGLcall("glReadBuffer");
4123 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4124 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4126 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4128 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4130 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4131 ERR("Texture filtering not supported in direct blit.\n");
4133 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4134 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4136 ERR("Texture filtering not supported in direct blit\n");
4139 if (upsidedown
4140 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4141 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4143 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
4144 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4145 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4146 src_rect->left, src_surface->resource.height - src_rect->bottom,
4147 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4149 else
4151 LONG row;
4152 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4153 /* I have to process this row by row to swap the image,
4154 * otherwise it would be upside down, so stretching in y direction
4155 * doesn't cost extra time
4157 * However, stretching in x direction can be avoided if not necessary
4159 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4160 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4162 /* Well, that stuff works, but it's very slow.
4163 * find a better way instead
4165 LONG col;
4167 for (col = dst_rect.left; col < dst_rect.right; ++col)
4169 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4170 dst_rect.left + col /* x offset */, row /* y offset */,
4171 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4174 else
4176 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4177 dst_rect.left /* x offset */, row /* y offset */,
4178 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4182 checkGLcall("glCopyTexSubImage2D");
4184 context_release(context);
4186 /* The texture is now most up to date - If the surface is a render target
4187 * and has a drawable, this path is never entered. */
4188 surface_validate_location(dst_surface, SFLAG_INTEXTURE);
4189 surface_invalidate_location(dst_surface, ~SFLAG_INTEXTURE);
4192 /* Uses the hardware to stretch and flip the image */
4193 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4194 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4196 struct wined3d_device *device = dst_surface->resource.device;
4197 GLuint src, backup = 0;
4198 float left, right, top, bottom; /* Texture coordinates */
4199 UINT fbwidth = src_surface->resource.width;
4200 UINT fbheight = src_surface->resource.height;
4201 const struct wined3d_gl_info *gl_info;
4202 struct wined3d_context *context;
4203 GLenum drawBuffer = GL_BACK;
4204 GLenum texture_target;
4205 BOOL noBackBufferBackup;
4206 BOOL src_offscreen;
4207 BOOL upsidedown = FALSE;
4208 RECT dst_rect = *dst_rect_in;
4210 TRACE("Using hwstretch blit\n");
4211 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4212 context = context_acquire(device, src_surface);
4213 gl_info = context->gl_info;
4214 context_apply_blit_state(context, device);
4215 surface_internal_preload(dst_surface, context, SRGB_RGB);
4217 src_offscreen = surface_is_offscreen(src_surface);
4218 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4219 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
4221 /* Get it a description */
4222 surface_internal_preload(src_surface, context, SRGB_RGB);
4225 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4226 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4228 if (context->aux_buffers >= 2)
4230 /* Got more than one aux buffer? Use the 2nd aux buffer */
4231 drawBuffer = GL_AUX1;
4233 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4235 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4236 drawBuffer = GL_AUX0;
4239 if (noBackBufferBackup)
4241 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
4242 checkGLcall("glGenTextures");
4243 context_bind_texture(context, GL_TEXTURE_2D, backup);
4244 texture_target = GL_TEXTURE_2D;
4246 else
4248 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4249 * we are reading from the back buffer, the backup can be used as source texture
4251 texture_target = src_surface->texture_target;
4252 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
4253 gl_info->gl_ops.gl.p_glEnable(texture_target);
4254 checkGLcall("glEnable(texture_target)");
4256 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4257 src_surface->flags &= ~SFLAG_INTEXTURE;
4260 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4261 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4263 if(dst_rect.top > dst_rect.bottom) {
4264 UINT tmp = dst_rect.bottom;
4265 dst_rect.bottom = dst_rect.top;
4266 dst_rect.top = tmp;
4267 upsidedown = TRUE;
4270 if (src_offscreen)
4272 TRACE("Reading from an offscreen target\n");
4273 upsidedown = !upsidedown;
4274 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
4276 else
4278 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
4281 /* TODO: Only back up the part that will be overwritten */
4282 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
4284 checkGLcall("glCopyTexSubImage2D");
4286 /* No issue with overriding these - the sampler is dirty due to blit usage */
4287 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
4288 wined3d_gl_mag_filter(magLookup, filter));
4289 checkGLcall("glTexParameteri");
4290 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
4291 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
4292 checkGLcall("glTexParameteri");
4294 if (!src_surface->swapchain || src_surface == src_surface->swapchain->back_buffers[0])
4296 src = backup ? backup : src_surface->container->texture_rgb.name;
4298 else
4300 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
4301 checkGLcall("glReadBuffer(GL_FRONT)");
4303 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
4304 checkGLcall("glGenTextures(1, &src)");
4305 context_bind_texture(context, GL_TEXTURE_2D, src);
4307 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
4308 * out for power of 2 sizes
4310 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
4311 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
4312 checkGLcall("glTexImage2D");
4313 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
4315 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4316 checkGLcall("glTexParameteri");
4317 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4318 checkGLcall("glTexParameteri");
4320 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
4321 checkGLcall("glReadBuffer(GL_BACK)");
4323 if (texture_target != GL_TEXTURE_2D)
4325 gl_info->gl_ops.gl.p_glDisable(texture_target);
4326 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
4327 texture_target = GL_TEXTURE_2D;
4330 checkGLcall("glEnd and previous");
4332 left = src_rect->left;
4333 right = src_rect->right;
4335 if (!upsidedown)
4337 top = src_surface->resource.height - src_rect->top;
4338 bottom = src_surface->resource.height - src_rect->bottom;
4340 else
4342 top = src_surface->resource.height - src_rect->bottom;
4343 bottom = src_surface->resource.height - src_rect->top;
4346 if (src_surface->flags & SFLAG_NORMCOORD)
4348 left /= src_surface->pow2Width;
4349 right /= src_surface->pow2Width;
4350 top /= src_surface->pow2Height;
4351 bottom /= src_surface->pow2Height;
4354 /* draw the source texture stretched and upside down. The correct surface is bound already */
4355 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
4356 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
4358 context_set_draw_buffer(context, drawBuffer);
4359 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
4361 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
4362 /* bottom left */
4363 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
4364 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
4366 /* top left */
4367 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
4368 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
4370 /* top right */
4371 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
4372 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4374 /* bottom right */
4375 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
4376 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
4377 gl_info->gl_ops.gl.p_glEnd();
4378 checkGLcall("glEnd and previous");
4380 if (texture_target != dst_surface->texture_target)
4382 gl_info->gl_ops.gl.p_glDisable(texture_target);
4383 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
4384 texture_target = dst_surface->texture_target;
4387 /* Now read the stretched and upside down image into the destination texture */
4388 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
4389 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
4391 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
4392 0, 0, /* We blitted the image to the origin */
4393 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4394 checkGLcall("glCopyTexSubImage2D");
4396 if (drawBuffer == GL_BACK)
4398 /* Write the back buffer backup back. */
4399 if (backup)
4401 if (texture_target != GL_TEXTURE_2D)
4403 gl_info->gl_ops.gl.p_glDisable(texture_target);
4404 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
4405 texture_target = GL_TEXTURE_2D;
4407 context_bind_texture(context, GL_TEXTURE_2D, backup);
4409 else
4411 if (texture_target != src_surface->texture_target)
4413 gl_info->gl_ops.gl.p_glDisable(texture_target);
4414 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
4415 texture_target = src_surface->texture_target;
4417 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
4420 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
4421 /* top left */
4422 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
4423 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
4425 /* bottom left */
4426 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
4427 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
4429 /* bottom right */
4430 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
4431 (float)fbheight / (float)src_surface->pow2Height);
4432 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
4434 /* top right */
4435 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
4436 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
4437 gl_info->gl_ops.gl.p_glEnd();
4439 gl_info->gl_ops.gl.p_glDisable(texture_target);
4440 checkGLcall("glDisable(texture_target)");
4442 /* Cleanup */
4443 if (src != src_surface->container->texture_rgb.name && src != backup)
4445 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
4446 checkGLcall("glDeleteTextures(1, &src)");
4448 if (backup)
4450 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
4451 checkGLcall("glDeleteTextures(1, &backup)");
4454 if (wined3d_settings.strict_draw_ordering)
4455 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4457 context_release(context);
4459 /* The texture is now most up to date - If the surface is a render target
4460 * and has a drawable, this path is never entered. */
4461 surface_validate_location(dst_surface, SFLAG_INTEXTURE);
4462 surface_invalidate_location(dst_surface, ~SFLAG_INTEXTURE);
4465 /* Front buffer coordinates are always full screen coordinates, but our GL
4466 * drawable is limited to the window's client area. The sysmem and texture
4467 * copies do have the full screen size. Note that GL has a bottom-left
4468 * origin, while D3D has a top-left origin. */
4469 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
4471 UINT drawable_height;
4473 if (surface->swapchain && surface == surface->swapchain->front_buffer)
4475 POINT offset = {0, 0};
4476 RECT windowsize;
4478 ScreenToClient(window, &offset);
4479 OffsetRect(rect, offset.x, offset.y);
4481 GetClientRect(window, &windowsize);
4482 drawable_height = windowsize.bottom - windowsize.top;
4484 else
4486 drawable_height = surface->resource.height;
4489 rect->top = drawable_height - rect->top;
4490 rect->bottom = drawable_height - rect->bottom;
4493 static void surface_blt_to_drawable(const struct wined3d_device *device,
4494 enum wined3d_texture_filter_type filter, BOOL color_key,
4495 struct wined3d_surface *src_surface, const RECT *src_rect_in,
4496 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
4498 const struct wined3d_gl_info *gl_info;
4499 struct wined3d_context *context;
4500 RECT src_rect, dst_rect;
4502 src_rect = *src_rect_in;
4503 dst_rect = *dst_rect_in;
4505 context = context_acquire(device, dst_surface);
4506 gl_info = context->gl_info;
4508 /* Make sure the surface is up-to-date. This should probably use
4509 * surface_load_location() and worry about the destination surface too,
4510 * unless we're overwriting it completely. */
4511 surface_internal_preload(src_surface, context, SRGB_RGB);
4513 /* Activate the destination context, set it up for blitting */
4514 context_apply_blit_state(context, device);
4516 if (!surface_is_offscreen(dst_surface))
4517 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
4519 device->blitter->set_shader(device->blit_priv, context, src_surface);
4521 if (color_key)
4523 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
4524 checkGLcall("glEnable(GL_ALPHA_TEST)");
4526 /* When the primary render target uses P8, the alpha component
4527 * contains the palette index. Which means that the colorkey is one of
4528 * the palette entries. In other cases pixels that should be masked
4529 * away have alpha set to 0. */
4530 if (swapchain_is_p8(context->swapchain))
4531 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
4532 (float)src_surface->src_blt_color_key.color_space_low_value / 256.0f);
4533 else
4534 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
4535 checkGLcall("glAlphaFunc");
4537 else
4539 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4540 checkGLcall("glDisable(GL_ALPHA_TEST)");
4543 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
4545 if (color_key)
4547 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4548 checkGLcall("glDisable(GL_ALPHA_TEST)");
4551 /* Leave the opengl state valid for blitting */
4552 device->blitter->unset_shader(context->gl_info);
4554 if (wined3d_settings.strict_draw_ordering
4555 || (dst_surface->swapchain && dst_surface->swapchain->front_buffer == dst_surface))
4556 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4558 context_release(context);
4561 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
4563 struct wined3d_device *device = s->resource.device;
4564 const struct blit_shader *blitter;
4566 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
4567 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
4568 if (!blitter)
4570 FIXME("No blitter is capable of performing the requested color fill operation.\n");
4571 return WINED3DERR_INVALIDCALL;
4574 return blitter->color_fill(device, s, rect, color);
4577 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4578 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
4579 enum wined3d_texture_filter_type filter)
4581 struct wined3d_device *device = dst_surface->resource.device;
4582 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4583 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
4585 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
4586 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4587 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
4589 /* Get the swapchain. One of the surfaces has to be a primary surface */
4590 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4592 WARN("Destination is in sysmem, rejecting gl blt\n");
4593 return WINED3DERR_INVALIDCALL;
4596 dst_swapchain = dst_surface->swapchain;
4598 if (src_surface)
4600 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4602 WARN("Src is in sysmem, rejecting gl blt\n");
4603 return WINED3DERR_INVALIDCALL;
4606 src_swapchain = src_surface->swapchain;
4608 else
4610 src_swapchain = NULL;
4613 /* Early sort out of cases where no render target is used */
4614 if (!dst_swapchain && !src_swapchain
4615 && src_surface != device->fb.render_targets[0]
4616 && dst_surface != device->fb.render_targets[0])
4618 TRACE("No surface is render target, not using hardware blit.\n");
4619 return WINED3DERR_INVALIDCALL;
4622 /* No destination color keying supported */
4623 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
4625 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
4626 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
4627 return WINED3DERR_INVALIDCALL;
4630 if (dst_swapchain && dst_swapchain == src_swapchain)
4632 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
4633 return WINED3DERR_INVALIDCALL;
4636 if (dst_swapchain && src_swapchain)
4638 FIXME("Implement hardware blit between two different swapchains\n");
4639 return WINED3DERR_INVALIDCALL;
4642 if (dst_swapchain)
4644 /* Handled with regular texture -> swapchain blit */
4645 if (src_surface == device->fb.render_targets[0])
4646 TRACE("Blit from active render target to a swapchain\n");
4648 else if (src_swapchain && dst_surface == device->fb.render_targets[0])
4650 FIXME("Implement blit from a swapchain to the active render target\n");
4651 return WINED3DERR_INVALIDCALL;
4654 if ((src_swapchain || src_surface == device->fb.render_targets[0]) && !dst_swapchain)
4656 /* Blit from render target to texture */
4657 BOOL stretchx;
4659 /* P8 read back is not implemented */
4660 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
4661 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
4663 TRACE("P8 read back not supported by frame buffer to texture blit\n");
4664 return WINED3DERR_INVALIDCALL;
4667 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
4669 TRACE("Color keying not supported by frame buffer to texture blit\n");
4670 return WINED3DERR_INVALIDCALL;
4671 /* Destination color key is checked above */
4674 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
4675 stretchx = TRUE;
4676 else
4677 stretchx = FALSE;
4679 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
4680 * flip the image nor scale it.
4682 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
4683 * -> If the app wants a image width an unscaled width, copy it line per line
4684 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
4685 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
4686 * back buffer. This is slower than reading line per line, thus not used for flipping
4687 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
4688 * pixel by pixel. */
4689 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
4690 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
4692 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
4693 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
4695 else
4697 TRACE("Using hardware stretching to flip / stretch the texture.\n");
4698 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
4701 surface_evict_sysmem(dst_surface);
4703 return WINED3D_OK;
4705 else if (src_surface)
4707 /* Blit from offscreen surface to render target */
4708 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
4709 DWORD oldCKeyFlags = src_surface->CKeyFlags;
4711 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4713 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4714 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
4715 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
4717 FIXME("Unsupported blit operation falling back to software\n");
4718 return WINED3DERR_INVALIDCALL;
4721 /* Color keying: Check if we have to do a color keyed blt,
4722 * and if not check if a color key is activated.
4724 * Just modify the color keying parameters in the surface and restore them afterwards
4725 * The surface keeps track of the color key last used to load the opengl surface.
4726 * PreLoad will catch the change to the flags and color key and reload if necessary.
4728 if (flags & WINEDDBLT_KEYSRC)
4730 /* Use color key from surface */
4732 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4734 /* Use color key from DDBltFx */
4735 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
4736 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
4738 else
4740 /* Do not use color key */
4741 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
4744 surface_blt_to_drawable(device, filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
4745 src_surface, src_rect, dst_surface, dst_rect);
4747 /* Restore the color key parameters */
4748 src_surface->CKeyFlags = oldCKeyFlags;
4749 src_surface->src_blt_color_key = old_blt_key;
4751 surface_validate_location(dst_surface, dst_surface->draw_binding);
4752 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
4754 return WINED3D_OK;
4757 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
4758 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
4759 return WINED3DERR_INVALIDCALL;
4762 /* Context activation is done by the caller. */
4763 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
4764 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
4766 struct wined3d_device *device = surface->resource.device;
4767 const struct wined3d_gl_info *gl_info = context->gl_info;
4768 GLint compare_mode = GL_NONE;
4769 struct blt_info info;
4770 GLint old_binding = 0;
4771 RECT rect;
4773 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4775 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
4776 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
4777 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4778 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
4779 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
4780 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
4781 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
4782 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
4783 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4784 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
4785 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
4787 SetRect(&rect, 0, h, w, 0);
4788 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
4789 context_active_texture(context, context->gl_info, 0);
4790 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
4791 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
4792 if (gl_info->supported[ARB_SHADOW])
4794 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
4795 if (compare_mode != GL_NONE)
4796 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
4799 device->shader_backend->shader_select_depth_blt(device->shader_priv,
4800 gl_info, info.tex_type, &surface->ds_current_size);
4802 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
4803 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
4804 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
4805 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
4806 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
4807 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
4808 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
4809 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
4810 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
4811 gl_info->gl_ops.gl.p_glEnd();
4813 if (compare_mode != GL_NONE)
4814 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
4815 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
4817 gl_info->gl_ops.gl.p_glPopAttrib();
4819 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
4822 void surface_modify_ds_location(struct wined3d_surface *surface,
4823 DWORD location, UINT w, UINT h)
4825 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
4827 if (location & ~(SFLAG_LOCATIONS | SFLAG_DISCARDED))
4828 FIXME("Invalid location (%#x) specified.\n", location);
4830 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
4831 || (!(surface->flags & SFLAG_INTEXTURE) && (location & SFLAG_INTEXTURE)))
4832 wined3d_texture_set_dirty(surface->container);
4834 surface->ds_current_size.cx = w;
4835 surface->ds_current_size.cy = h;
4836 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_DISCARDED);
4837 surface->flags |= location;
4840 /* Context activation is done by the caller. */
4841 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4843 const struct wined3d_gl_info *gl_info = context->gl_info;
4844 struct wined3d_device *device = surface->resource.device;
4845 GLsizei w, h;
4847 TRACE("surface %p, new location %#x.\n", surface, location);
4849 /* TODO: Make this work for modes other than FBO */
4850 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4852 if (!(surface->flags & location))
4854 w = surface->ds_current_size.cx;
4855 h = surface->ds_current_size.cy;
4856 surface->ds_current_size.cx = 0;
4857 surface->ds_current_size.cy = 0;
4859 else
4861 w = surface->resource.width;
4862 h = surface->resource.height;
4865 if (surface->ds_current_size.cx == surface->resource.width
4866 && surface->ds_current_size.cy == surface->resource.height)
4868 TRACE("Location (%#x) is already up to date.\n", location);
4869 return;
4872 if (surface->current_renderbuffer)
4874 FIXME("Not supported with fixed up depth stencil.\n");
4875 return;
4878 if (surface->flags & SFLAG_DISCARDED)
4880 TRACE("Surface was discarded, no need copy data.\n");
4881 switch (location)
4883 case SFLAG_INTEXTURE:
4884 surface_prepare_texture(surface, context, FALSE);
4885 break;
4886 case SFLAG_INRB_MULTISAMPLE:
4887 surface_prepare_rb(surface, gl_info, TRUE);
4888 break;
4889 case SFLAG_INDRAWABLE:
4890 /* Nothing to do */
4891 break;
4892 default:
4893 FIXME("Unhandled location %#x\n", location);
4895 surface->flags &= ~SFLAG_DISCARDED;
4896 surface->flags |= location;
4897 surface->ds_current_size.cx = surface->resource.width;
4898 surface->ds_current_size.cy = surface->resource.height;
4899 return;
4902 if (!(surface->flags & SFLAG_LOCATIONS))
4904 FIXME("No up to date depth stencil location.\n");
4905 surface->flags |= location;
4906 surface->ds_current_size.cx = surface->resource.width;
4907 surface->ds_current_size.cy = surface->resource.height;
4908 return;
4911 if (location == SFLAG_INTEXTURE)
4913 GLint old_binding = 0;
4914 GLenum bind_target;
4916 /* The render target is allowed to be smaller than the depth/stencil
4917 * buffer, so the onscreen depth/stencil buffer is potentially smaller
4918 * than the offscreen surface. Don't overwrite the offscreen surface
4919 * with undefined data. */
4920 w = min(w, context->swapchain->desc.backbuffer_width);
4921 h = min(h, context->swapchain->desc.backbuffer_height);
4923 TRACE("Copying onscreen depth buffer to depth texture.\n");
4925 if (!device->depth_blt_texture)
4926 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
4928 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4929 * directly on the FBO texture. That's because we need to flip. */
4930 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4931 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
4932 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4934 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4935 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4937 else
4939 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4940 bind_target = GL_TEXTURE_2D;
4942 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
4943 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
4944 * internal format, because the internal format might include stencil
4945 * data. In principle we should copy stencil data as well, but unless
4946 * the driver supports stencil export it's hard to do, and doesn't
4947 * seem to be needed in practice. If the hardware doesn't support
4948 * writing stencil data, the glCopyTexImage2D() call might trigger
4949 * software fallbacks. */
4950 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
4951 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4952 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4953 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4954 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4955 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4956 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4957 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
4959 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4960 NULL, surface, SFLAG_INTEXTURE);
4961 context_set_draw_buffer(context, GL_NONE);
4963 /* Do the actual blit */
4964 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
4965 checkGLcall("depth_blt");
4967 context_invalidate_state(context, STATE_FRAMEBUFFER);
4969 if (wined3d_settings.strict_draw_ordering)
4970 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4972 else if (location == SFLAG_INDRAWABLE)
4974 TRACE("Copying depth texture to onscreen depth buffer.\n");
4976 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4977 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
4978 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
4979 0, surface->pow2Height - h, w, h, surface->texture_target);
4980 checkGLcall("depth_blt");
4982 context_invalidate_state(context, STATE_FRAMEBUFFER);
4984 if (wined3d_settings.strict_draw_ordering)
4985 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4987 else
4989 ERR("Invalid location (%#x) specified.\n", location);
4992 surface->flags |= location;
4993 surface->ds_current_size.cx = surface->resource.width;
4994 surface->ds_current_size.cy = surface->resource.height;
4997 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
4999 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location & SFLAG_LOCATIONS));
5001 surface->flags |= (location & SFLAG_LOCATIONS);
5004 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
5006 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location & SFLAG_LOCATIONS));
5008 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5009 wined3d_texture_set_dirty(surface->container);
5010 surface->flags &= ~(location & SFLAG_LOCATIONS);
5012 if (!(surface->flags & SFLAG_LOCATIONS))
5013 ERR("Surface %p does not have any up to date location.\n", surface);
5016 static DWORD resource_access_from_location(DWORD location)
5018 switch (location)
5020 case SFLAG_INSYSMEM:
5021 return WINED3D_RESOURCE_ACCESS_CPU;
5023 case SFLAG_INDRAWABLE:
5024 case SFLAG_INSRGBTEX:
5025 case SFLAG_INTEXTURE:
5026 case SFLAG_INRB_MULTISAMPLE:
5027 case SFLAG_INRB_RESOLVED:
5028 return WINED3D_RESOURCE_ACCESS_GPU;
5030 default:
5031 FIXME("Unhandled location %#x.\n", location);
5032 return 0;
5036 static void surface_load_sysmem(struct wined3d_surface *surface,
5037 const struct wined3d_gl_info *gl_info)
5039 surface_prepare_system_memory(surface);
5041 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5042 surface_load_location(surface, SFLAG_INTEXTURE);
5044 /* Download the surface to system memory. */
5045 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5047 struct wined3d_device *device = surface->resource.device;
5048 struct wined3d_context *context;
5050 /* TODO: Use already acquired context when possible. */
5051 context = context_acquire(device, NULL);
5053 wined3d_texture_bind_and_dirtify(surface->container, context, !(surface->flags & SFLAG_INTEXTURE));
5054 surface_download_data(surface, gl_info);
5056 context_release(context);
5058 return;
5061 if (surface->flags & SFLAG_INDRAWABLE)
5063 read_from_framebuffer(surface, surface->resource.allocatedMemory,
5064 wined3d_surface_get_pitch(surface));
5065 return;
5068 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5069 surface, surface->flags & SFLAG_LOCATIONS);
5072 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5073 const struct wined3d_gl_info *gl_info)
5075 RECT r;
5077 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5079 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5080 return WINED3DERR_INVALIDCALL;
5083 surface_get_rect(surface, NULL, &r);
5084 surface_load_location(surface, SFLAG_INTEXTURE);
5085 surface_blt_to_drawable(surface->resource.device,
5086 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
5088 return WINED3D_OK;
5091 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5092 const struct wined3d_gl_info *gl_info, BOOL srgb)
5094 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5095 struct wined3d_device *device = surface->resource.device;
5096 enum wined3d_conversion_type convert;
5097 struct wined3d_context *context;
5098 UINT width, src_pitch, dst_pitch;
5099 struct wined3d_bo_address data;
5100 struct wined3d_format format;
5101 POINT dst_point = {0, 0};
5102 BYTE *mem;
5104 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
5105 && surface_is_offscreen(surface)
5106 && (surface->flags & SFLAG_INDRAWABLE))
5108 surface_load_fb_texture(surface, srgb);
5110 return WINED3D_OK;
5113 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
5114 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
5115 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5116 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5117 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5119 if (srgb)
5120 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INTEXTURE,
5121 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
5122 else
5123 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INSRGBTEX,
5124 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
5126 return WINED3D_OK;
5129 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
5130 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
5131 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5132 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5133 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5135 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
5136 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
5137 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5139 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
5140 &rect, surface, dst_location, &rect);
5142 return WINED3D_OK;
5145 /* Upload from system memory */
5147 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
5148 TRUE /* We will use textures */, &format, &convert);
5150 if (srgb)
5152 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
5154 /* Performance warning... */
5155 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
5156 surface_load_location(surface, SFLAG_INSYSMEM);
5159 else
5161 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
5163 /* Performance warning... */
5164 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
5165 surface_load_location(surface, SFLAG_INSYSMEM);
5169 if (!(surface->flags & SFLAG_INSYSMEM))
5171 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
5172 /* Lets hope we get it from somewhere... */
5173 surface_load_location(surface, SFLAG_INSYSMEM);
5176 /* TODO: Use already acquired context when possible. */
5177 context = context_acquire(device, NULL);
5179 surface_prepare_texture(surface, context, srgb);
5180 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
5182 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
5184 surface->flags |= SFLAG_GLCKEY;
5185 surface->gl_color_key = surface->src_blt_color_key;
5187 else surface->flags &= ~SFLAG_GLCKEY;
5189 width = surface->resource.width;
5190 src_pitch = wined3d_surface_get_pitch(surface);
5192 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5193 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
5194 * called. */
5195 if ((convert != WINED3D_CT_NONE || format.convert) && (surface->flags & SFLAG_PBO))
5197 TRACE("Removing the pbo attached to surface %p.\n", surface);
5198 surface_remove_pbo(surface, gl_info);
5201 if (format.convert)
5203 /* This code is entered for texture formats which need a fixup. */
5204 UINT height = surface->resource.height;
5206 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
5207 dst_pitch = width * format.conv_byte_count;
5208 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5210 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5212 ERR("Out of memory (%u).\n", dst_pitch * height);
5213 context_release(context);
5214 return E_OUTOFMEMORY;
5216 format.convert(surface->resource.allocatedMemory, mem, src_pitch, src_pitch * height,
5217 dst_pitch, dst_pitch * height, width, height, 1);
5218 format.byte_count = format.conv_byte_count;
5219 src_pitch = dst_pitch;
5221 else if (convert != WINED3D_CT_NONE && surface->resource.allocatedMemory)
5223 /* This code is only entered for color keying fixups */
5224 UINT height = surface->resource.height;
5226 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
5227 dst_pitch = width * format.conv_byte_count;
5228 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5230 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5232 ERR("Out of memory (%u).\n", dst_pitch * height);
5233 context_release(context);
5234 return E_OUTOFMEMORY;
5236 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
5237 width, height, dst_pitch, convert, surface);
5238 format.byte_count = format.conv_byte_count;
5239 src_pitch = dst_pitch;
5241 else
5243 mem = surface->resource.allocatedMemory;
5246 data.buffer_object = surface->pbo;
5247 data.addr = mem;
5248 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
5250 context_release(context);
5252 /* Don't delete PBO memory. */
5253 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5254 HeapFree(GetProcessHeap(), 0, mem);
5256 return WINED3D_OK;
5259 static void surface_multisample_resolve(struct wined3d_surface *surface)
5261 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5263 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
5264 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
5266 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
5267 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
5270 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
5272 struct wined3d_device *device = surface->resource.device;
5273 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5274 HRESULT hr;
5276 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location));
5278 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5280 if (location == SFLAG_INTEXTURE && surface->flags & SFLAG_INDRAWABLE)
5282 struct wined3d_context *context = context_acquire(device, NULL);
5283 surface_load_ds_location(surface, context, location);
5284 context_release(context);
5285 return WINED3D_OK;
5287 else if (location & surface->flags && surface->draw_binding != SFLAG_INDRAWABLE)
5289 /* Already up to date, nothing to do. */
5290 return WINED3D_OK;
5292 else
5294 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
5295 debug_surflocation(surface->flags & SFLAG_LOCATIONS), debug_surflocation(location));
5296 return WINED3DERR_INVALIDCALL;
5300 if (surface->flags & location)
5302 TRACE("Location already up to date.\n");
5304 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
5305 && surface_need_pbo(surface, gl_info))
5306 surface_load_pbo(surface, gl_info);
5308 return WINED3D_OK;
5311 if (WARN_ON(d3d_surface))
5313 DWORD required_access = resource_access_from_location(location);
5314 if ((surface->resource.access_flags & required_access) != required_access)
5315 WARN("Operation requires %#x access, but surface only has %#x.\n",
5316 required_access, surface->resource.access_flags);
5319 if (!(surface->flags & SFLAG_LOCATIONS))
5321 ERR("Surface %p does not have any up to date location.\n", surface);
5322 surface->flags |= SFLAG_LOST;
5323 return WINED3DERR_DEVICELOST;
5326 switch (location)
5328 case SFLAG_INSYSMEM:
5329 surface_load_sysmem(surface, gl_info);
5330 break;
5332 case SFLAG_INDRAWABLE:
5333 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
5334 return hr;
5335 break;
5337 case SFLAG_INRB_RESOLVED:
5338 surface_multisample_resolve(surface);
5339 break;
5341 case SFLAG_INTEXTURE:
5342 case SFLAG_INSRGBTEX:
5343 if (FAILED(hr = surface_load_texture(surface, gl_info, location == SFLAG_INSRGBTEX)))
5344 return hr;
5345 break;
5347 default:
5348 ERR("Don't know how to handle location %#x.\n", location);
5349 break;
5352 surface->flags |= location;
5354 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
5355 surface_evict_sysmem(surface);
5357 return WINED3D_OK;
5360 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
5362 struct wined3d_swapchain *swapchain;
5364 /* Not on a swapchain - must be offscreen */
5365 if (!(swapchain = surface->swapchain))
5366 return TRUE;
5368 /* The front buffer is always onscreen */
5369 if (surface == swapchain->front_buffer) return FALSE;
5371 /* If the swapchain is rendered to an FBO, the backbuffer is
5372 * offscreen, otherwise onscreen */
5373 return swapchain->render_to_fbo;
5376 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
5377 /* Context activation is done by the caller. */
5378 static void ffp_blit_free(struct wined3d_device *device) { }
5380 /* Context activation is done by the caller. */
5381 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5383 const struct wined3d_gl_info *gl_info = context->gl_info;
5385 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
5386 checkGLcall("glEnable(target)");
5388 return WINED3D_OK;
5391 /* Context activation is done by the caller. */
5392 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
5394 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
5395 checkGLcall("glDisable(GL_TEXTURE_2D)");
5396 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
5398 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5399 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5401 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
5403 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
5404 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5408 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5409 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5410 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5412 switch (blit_op)
5414 case WINED3D_BLIT_OP_COLOR_BLIT:
5415 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
5416 return FALSE;
5418 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5420 TRACE("Checking support for fixup:\n");
5421 dump_color_fixup_desc(src_format->color_fixup);
5424 /* We only support identity conversions. */
5425 if (!is_identity_fixup(src_format->color_fixup)
5426 || !is_identity_fixup(dst_format->color_fixup))
5428 TRACE("Fixups are not supported.\n");
5429 return FALSE;
5432 return TRUE;
5434 case WINED3D_BLIT_OP_COLOR_FILL:
5435 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
5436 return FALSE;
5438 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5440 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
5441 return FALSE;
5443 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
5445 TRACE("Color fill not supported\n");
5446 return FALSE;
5449 /* FIXME: We should reject color fills on formats with fixups,
5450 * but this would break P8 color fills for example. */
5452 return TRUE;
5454 case WINED3D_BLIT_OP_DEPTH_FILL:
5455 return TRUE;
5457 default:
5458 TRACE("Unsupported blit_op=%d\n", blit_op);
5459 return FALSE;
5463 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5464 const RECT *dst_rect, const struct wined3d_color *color)
5466 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
5467 struct wined3d_fb_state fb = {&dst_surface, NULL};
5469 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
5471 return WINED3D_OK;
5474 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
5475 struct wined3d_surface *surface, const RECT *rect, float depth)
5477 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
5478 struct wined3d_fb_state fb = {NULL, surface};
5480 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
5482 return WINED3D_OK;
5485 const struct blit_shader ffp_blit = {
5486 ffp_blit_alloc,
5487 ffp_blit_free,
5488 ffp_blit_set,
5489 ffp_blit_unset,
5490 ffp_blit_supported,
5491 ffp_blit_color_fill,
5492 ffp_blit_depth_fill,
5495 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
5497 return WINED3D_OK;
5500 /* Context activation is done by the caller. */
5501 static void cpu_blit_free(struct wined3d_device *device)
5505 /* Context activation is done by the caller. */
5506 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5508 return WINED3D_OK;
5511 /* Context activation is done by the caller. */
5512 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
5516 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5517 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5518 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5520 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
5522 return TRUE;
5525 return FALSE;
5528 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
5529 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
5530 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
5532 UINT row_block_count;
5533 const BYTE *src_row;
5534 BYTE *dst_row;
5535 UINT x, y;
5537 src_row = src_data;
5538 dst_row = dst_data;
5540 row_block_count = (update_w + format->block_width - 1) / format->block_width;
5542 if (!flags)
5544 for (y = 0; y < update_h; y += format->block_height)
5546 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
5547 src_row += src_pitch;
5548 dst_row += dst_pitch;
5551 return WINED3D_OK;
5554 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
5556 src_row += (((update_h / format->block_height) - 1) * src_pitch);
5558 switch (format->id)
5560 case WINED3DFMT_DXT1:
5561 for (y = 0; y < update_h; y += format->block_height)
5563 struct block
5565 WORD color[2];
5566 BYTE control_row[4];
5569 const struct block *s = (const struct block *)src_row;
5570 struct block *d = (struct block *)dst_row;
5572 for (x = 0; x < row_block_count; ++x)
5574 d[x].color[0] = s[x].color[0];
5575 d[x].color[1] = s[x].color[1];
5576 d[x].control_row[0] = s[x].control_row[3];
5577 d[x].control_row[1] = s[x].control_row[2];
5578 d[x].control_row[2] = s[x].control_row[1];
5579 d[x].control_row[3] = s[x].control_row[0];
5581 src_row -= src_pitch;
5582 dst_row += dst_pitch;
5584 return WINED3D_OK;
5586 case WINED3DFMT_DXT3:
5587 for (y = 0; y < update_h; y += format->block_height)
5589 struct block
5591 WORD alpha_row[4];
5592 WORD color[2];
5593 BYTE control_row[4];
5596 const struct block *s = (const struct block *)src_row;
5597 struct block *d = (struct block *)dst_row;
5599 for (x = 0; x < row_block_count; ++x)
5601 d[x].alpha_row[0] = s[x].alpha_row[3];
5602 d[x].alpha_row[1] = s[x].alpha_row[2];
5603 d[x].alpha_row[2] = s[x].alpha_row[1];
5604 d[x].alpha_row[3] = s[x].alpha_row[0];
5605 d[x].color[0] = s[x].color[0];
5606 d[x].color[1] = s[x].color[1];
5607 d[x].control_row[0] = s[x].control_row[3];
5608 d[x].control_row[1] = s[x].control_row[2];
5609 d[x].control_row[2] = s[x].control_row[1];
5610 d[x].control_row[3] = s[x].control_row[0];
5612 src_row -= src_pitch;
5613 dst_row += dst_pitch;
5615 return WINED3D_OK;
5617 default:
5618 FIXME("Compressed flip not implemented for format %s.\n",
5619 debug_d3dformat(format->id));
5620 return E_NOTIMPL;
5624 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
5625 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
5627 return E_NOTIMPL;
5630 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5631 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
5632 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5634 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
5635 const struct wined3d_format *src_format, *dst_format;
5636 struct wined3d_texture *src_texture = NULL;
5637 struct wined3d_map_desc dst_map, src_map;
5638 const BYTE *sbase = NULL;
5639 HRESULT hr = WINED3D_OK;
5640 const BYTE *sbuf;
5641 BYTE *dbuf;
5642 int x, y;
5644 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5645 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5646 flags, fx, debug_d3dtexturefiltertype(filter));
5648 if (src_surface == dst_surface)
5650 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
5651 src_map = dst_map;
5652 src_format = dst_surface->resource.format;
5653 dst_format = src_format;
5655 else
5657 dst_format = dst_surface->resource.format;
5658 if (src_surface)
5660 if (dst_surface->resource.format->id != src_surface->resource.format->id)
5662 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
5664 /* The conv function writes a FIXME */
5665 WARN("Cannot convert source surface format to dest format.\n");
5666 goto release;
5668 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
5670 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
5671 src_format = src_surface->resource.format;
5673 else
5675 src_format = dst_format;
5678 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
5681 bpp = dst_surface->resource.format->byte_count;
5682 srcheight = src_rect->bottom - src_rect->top;
5683 srcwidth = src_rect->right - src_rect->left;
5684 dstheight = dst_rect->bottom - dst_rect->top;
5685 dstwidth = dst_rect->right - dst_rect->left;
5686 width = (dst_rect->right - dst_rect->left) * bpp;
5688 if (src_surface)
5689 sbase = (BYTE *)src_map.data
5690 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
5691 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
5692 if (src_surface != dst_surface)
5693 dbuf = dst_map.data;
5694 else
5695 dbuf = (BYTE *)dst_map.data
5696 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
5697 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
5699 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
5701 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
5703 if (src_surface == dst_surface)
5705 FIXME("Only plain blits supported on compressed surfaces.\n");
5706 hr = E_NOTIMPL;
5707 goto release;
5710 if (srcheight != dstheight || srcwidth != dstwidth)
5712 WARN("Stretching not supported on compressed surfaces.\n");
5713 hr = WINED3DERR_INVALIDCALL;
5714 goto release;
5717 if (!surface_check_block_align(src_surface, src_rect))
5719 WARN("Source rectangle not block-aligned.\n");
5720 hr = WINED3DERR_INVALIDCALL;
5721 goto release;
5724 if (!surface_check_block_align(dst_surface, dst_rect))
5726 WARN("Destination rectangle not block-aligned.\n");
5727 hr = WINED3DERR_INVALIDCALL;
5728 goto release;
5731 hr = surface_cpu_blt_compressed(sbase, dbuf,
5732 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
5733 src_format, flags, fx);
5734 goto release;
5737 /* First, all the 'source-less' blits */
5738 if (flags & WINEDDBLT_COLORFILL)
5740 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
5741 flags &= ~WINEDDBLT_COLORFILL;
5744 if (flags & WINEDDBLT_DEPTHFILL)
5746 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
5748 if (flags & WINEDDBLT_ROP)
5750 /* Catch some degenerate cases here. */
5751 switch (fx->dwROP)
5753 case BLACKNESS:
5754 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
5755 break;
5756 case 0xaa0029: /* No-op */
5757 break;
5758 case WHITENESS:
5759 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
5760 break;
5761 case SRCCOPY: /* Well, we do that below? */
5762 break;
5763 default:
5764 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
5765 goto error;
5767 flags &= ~WINEDDBLT_ROP;
5769 if (flags & WINEDDBLT_DDROPS)
5771 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
5773 /* Now the 'with source' blits. */
5774 if (src_surface)
5776 int sx, xinc, sy, yinc;
5778 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
5779 goto release;
5781 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
5782 && (srcwidth != dstwidth || srcheight != dstheight))
5784 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
5785 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
5788 xinc = (srcwidth << 16) / dstwidth;
5789 yinc = (srcheight << 16) / dstheight;
5791 if (!flags)
5793 /* No effects, we can cheat here. */
5794 if (dstwidth == srcwidth)
5796 if (dstheight == srcheight)
5798 /* No stretching in either direction. This needs to be as
5799 * fast as possible. */
5800 sbuf = sbase;
5802 /* Check for overlapping surfaces. */
5803 if (src_surface != dst_surface || dst_rect->top < src_rect->top
5804 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
5806 /* No overlap, or dst above src, so copy from top downwards. */
5807 for (y = 0; y < dstheight; ++y)
5809 memcpy(dbuf, sbuf, width);
5810 sbuf += src_map.row_pitch;
5811 dbuf += dst_map.row_pitch;
5814 else if (dst_rect->top > src_rect->top)
5816 /* Copy from bottom upwards. */
5817 sbuf += src_map.row_pitch * dstheight;
5818 dbuf += dst_map.row_pitch * dstheight;
5819 for (y = 0; y < dstheight; ++y)
5821 sbuf -= src_map.row_pitch;
5822 dbuf -= dst_map.row_pitch;
5823 memcpy(dbuf, sbuf, width);
5826 else
5828 /* Src and dst overlapping on the same line, use memmove. */
5829 for (y = 0; y < dstheight; ++y)
5831 memmove(dbuf, sbuf, width);
5832 sbuf += src_map.row_pitch;
5833 dbuf += dst_map.row_pitch;
5837 else
5839 /* Stretching in y direction only. */
5840 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5842 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5843 memcpy(dbuf, sbuf, width);
5844 dbuf += dst_map.row_pitch;
5848 else
5850 /* Stretching in X direction. */
5851 int last_sy = -1;
5852 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5854 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5856 if ((sy >> 16) == (last_sy >> 16))
5858 /* This source row is the same as last source row -
5859 * Copy the already stretched row. */
5860 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
5862 else
5864 #define STRETCH_ROW(type) \
5865 do { \
5866 const type *s = (const type *)sbuf; \
5867 type *d = (type *)dbuf; \
5868 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5869 d[x] = s[sx >> 16]; \
5870 } while(0)
5872 switch(bpp)
5874 case 1:
5875 STRETCH_ROW(BYTE);
5876 break;
5877 case 2:
5878 STRETCH_ROW(WORD);
5879 break;
5880 case 4:
5881 STRETCH_ROW(DWORD);
5882 break;
5883 case 3:
5885 const BYTE *s;
5886 BYTE *d = dbuf;
5887 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
5889 DWORD pixel;
5891 s = sbuf + 3 * (sx >> 16);
5892 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5893 d[0] = (pixel ) & 0xff;
5894 d[1] = (pixel >> 8) & 0xff;
5895 d[2] = (pixel >> 16) & 0xff;
5896 d += 3;
5898 break;
5900 default:
5901 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
5902 hr = WINED3DERR_NOTAVAILABLE;
5903 goto error;
5905 #undef STRETCH_ROW
5907 dbuf += dst_map.row_pitch;
5908 last_sy = sy;
5912 else
5914 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
5915 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
5916 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
5917 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
5919 /* The color keying flags are checked for correctness in ddraw */
5920 if (flags & WINEDDBLT_KEYSRC)
5922 keylow = src_surface->src_blt_color_key.color_space_low_value;
5923 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
5925 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5927 keylow = fx->ddckSrcColorkey.color_space_low_value;
5928 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
5931 if (flags & WINEDDBLT_KEYDEST)
5933 /* Destination color keys are taken from the source surface! */
5934 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
5935 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
5937 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
5939 destkeylow = fx->ddckDestColorkey.color_space_low_value;
5940 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
5943 if (bpp == 1)
5945 keymask = 0xff;
5947 else
5949 DWORD masks[3];
5950 get_color_masks(src_format, masks);
5951 keymask = masks[0]
5952 | masks[1]
5953 | masks[2];
5955 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
5958 if (flags & WINEDDBLT_DDFX)
5960 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
5961 LONG tmpxy;
5962 dTopLeft = dbuf;
5963 dTopRight = dbuf + ((dstwidth - 1) * bpp);
5964 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
5965 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
5967 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
5969 /* I don't think we need to do anything about this flag */
5970 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
5972 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
5974 tmp = dTopRight;
5975 dTopRight = dTopLeft;
5976 dTopLeft = tmp;
5977 tmp = dBottomRight;
5978 dBottomRight = dBottomLeft;
5979 dBottomLeft = tmp;
5980 dstxinc = dstxinc * -1;
5982 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
5984 tmp = dTopLeft;
5985 dTopLeft = dBottomLeft;
5986 dBottomLeft = tmp;
5987 tmp = dTopRight;
5988 dTopRight = dBottomRight;
5989 dBottomRight = tmp;
5990 dstyinc = dstyinc * -1;
5992 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
5994 /* I don't think we need to do anything about this flag */
5995 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
5997 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
5999 tmp = dBottomRight;
6000 dBottomRight = dTopLeft;
6001 dTopLeft = tmp;
6002 tmp = dBottomLeft;
6003 dBottomLeft = dTopRight;
6004 dTopRight = tmp;
6005 dstxinc = dstxinc * -1;
6006 dstyinc = dstyinc * -1;
6008 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6010 tmp = dTopLeft;
6011 dTopLeft = dBottomLeft;
6012 dBottomLeft = dBottomRight;
6013 dBottomRight = dTopRight;
6014 dTopRight = tmp;
6015 tmpxy = dstxinc;
6016 dstxinc = dstyinc;
6017 dstyinc = tmpxy;
6018 dstxinc = dstxinc * -1;
6020 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6022 tmp = dTopLeft;
6023 dTopLeft = dTopRight;
6024 dTopRight = dBottomRight;
6025 dBottomRight = dBottomLeft;
6026 dBottomLeft = tmp;
6027 tmpxy = dstxinc;
6028 dstxinc = dstyinc;
6029 dstyinc = tmpxy;
6030 dstyinc = dstyinc * -1;
6032 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6034 /* I don't think we need to do anything about this flag */
6035 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6037 dbuf = dTopLeft;
6038 flags &= ~(WINEDDBLT_DDFX);
6041 #define COPY_COLORKEY_FX(type) \
6042 do { \
6043 const type *s; \
6044 type *d = (type *)dbuf, *dx, tmp; \
6045 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6047 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
6048 dx = d; \
6049 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6051 tmp = s[sx >> 16]; \
6052 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6053 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6055 dx[0] = tmp; \
6057 dx = (type *)(((BYTE *)dx) + dstxinc); \
6059 d = (type *)(((BYTE *)d) + dstyinc); \
6061 } while(0)
6063 switch (bpp)
6065 case 1:
6066 COPY_COLORKEY_FX(BYTE);
6067 break;
6068 case 2:
6069 COPY_COLORKEY_FX(WORD);
6070 break;
6071 case 4:
6072 COPY_COLORKEY_FX(DWORD);
6073 break;
6074 case 3:
6076 const BYTE *s;
6077 BYTE *d = dbuf, *dx;
6078 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6080 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6081 dx = d;
6082 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
6084 DWORD pixel, dpixel = 0;
6085 s = sbuf + 3 * (sx>>16);
6086 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6087 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
6088 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
6089 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
6091 dx[0] = (pixel ) & 0xff;
6092 dx[1] = (pixel >> 8) & 0xff;
6093 dx[2] = (pixel >> 16) & 0xff;
6095 dx += dstxinc;
6097 d += dstyinc;
6099 break;
6101 default:
6102 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
6103 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
6104 hr = WINED3DERR_NOTAVAILABLE;
6105 goto error;
6106 #undef COPY_COLORKEY_FX
6111 error:
6112 if (flags && FIXME_ON(d3d_surface))
6114 FIXME("\tUnsupported flags: %#x.\n", flags);
6117 release:
6118 wined3d_surface_unmap(dst_surface);
6119 if (src_surface && src_surface != dst_surface)
6120 wined3d_surface_unmap(src_surface);
6121 /* Release the converted surface, if any. */
6122 if (src_texture)
6123 wined3d_texture_decref(src_texture);
6125 return hr;
6128 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6129 const RECT *dst_rect, const struct wined3d_color *color)
6131 static const RECT src_rect;
6132 WINEDDBLTFX BltFx;
6134 memset(&BltFx, 0, sizeof(BltFx));
6135 BltFx.dwSize = sizeof(BltFx);
6136 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
6137 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
6138 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
6141 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
6142 struct wined3d_surface *surface, const RECT *rect, float depth)
6144 FIXME("Depth filling not implemented by cpu_blit.\n");
6145 return WINED3DERR_INVALIDCALL;
6148 const struct blit_shader cpu_blit = {
6149 cpu_blit_alloc,
6150 cpu_blit_free,
6151 cpu_blit_set,
6152 cpu_blit_unset,
6153 cpu_blit_supported,
6154 cpu_blit_color_fill,
6155 cpu_blit_depth_fill,
6158 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
6159 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
6160 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
6162 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
6163 struct wined3d_device *device = dst_surface->resource.device;
6164 DWORD src_ds_flags, dst_ds_flags;
6165 RECT src_rect, dst_rect;
6166 BOOL scale, convert;
6168 static const DWORD simple_blit = WINEDDBLT_ASYNC
6169 | WINEDDBLT_COLORFILL
6170 | WINEDDBLT_WAIT
6171 | WINEDDBLT_DEPTHFILL
6172 | WINEDDBLT_DONOTWAIT;
6174 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6175 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
6176 flags, fx, debug_d3dtexturefiltertype(filter));
6177 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
6179 if (fx)
6181 TRACE("dwSize %#x.\n", fx->dwSize);
6182 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
6183 TRACE("dwROP %#x.\n", fx->dwROP);
6184 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
6185 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
6186 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
6187 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
6188 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
6189 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
6190 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
6191 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
6192 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
6193 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
6194 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
6195 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
6196 TRACE("dwReserved %#x.\n", fx->dwReserved);
6197 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
6198 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
6199 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
6200 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
6201 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
6202 TRACE("ddckDestColorkey {%#x, %#x}.\n",
6203 fx->ddckDestColorkey.color_space_low_value,
6204 fx->ddckDestColorkey.color_space_high_value);
6205 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
6206 fx->ddckSrcColorkey.color_space_low_value,
6207 fx->ddckSrcColorkey.color_space_high_value);
6210 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
6212 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
6213 return WINEDDERR_SURFACEBUSY;
6216 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
6218 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
6219 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
6220 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
6221 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
6222 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
6224 WARN("The application gave us a bad destination rectangle.\n");
6225 return WINEDDERR_INVALIDRECT;
6228 if (src_surface)
6230 surface_get_rect(src_surface, src_rect_in, &src_rect);
6232 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
6233 || src_rect.left > src_surface->resource.width || src_rect.left < 0
6234 || src_rect.top > src_surface->resource.height || src_rect.top < 0
6235 || src_rect.right > src_surface->resource.width || src_rect.right < 0
6236 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
6238 WARN("Application gave us bad source rectangle for Blt.\n");
6239 return WINEDDERR_INVALIDRECT;
6242 else
6244 memset(&src_rect, 0, sizeof(src_rect));
6247 if (!fx || !(fx->dwDDFX))
6248 flags &= ~WINEDDBLT_DDFX;
6250 if (flags & WINEDDBLT_WAIT)
6251 flags &= ~WINEDDBLT_WAIT;
6253 if (flags & WINEDDBLT_ASYNC)
6255 static unsigned int once;
6257 if (!once++)
6258 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
6259 flags &= ~WINEDDBLT_ASYNC;
6262 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
6263 if (flags & WINEDDBLT_DONOTWAIT)
6265 static unsigned int once;
6267 if (!once++)
6268 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
6269 flags &= ~WINEDDBLT_DONOTWAIT;
6272 if (!device->d3d_initialized)
6274 WARN("D3D not initialized, using fallback.\n");
6275 goto cpu;
6278 /* We want to avoid invalidating the sysmem location for converted
6279 * surfaces, since otherwise we'd have to convert the data back when
6280 * locking them. */
6281 if (dst_surface->flags & SFLAG_CONVERTED)
6283 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
6284 goto cpu;
6287 if (flags & ~simple_blit)
6289 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
6290 goto fallback;
6293 if (src_surface)
6294 src_swapchain = src_surface->swapchain;
6295 else
6296 src_swapchain = NULL;
6298 dst_swapchain = dst_surface->swapchain;
6300 /* This isn't strictly needed. FBO blits for example could deal with
6301 * cross-swapchain blits by first downloading the source to a texture
6302 * before switching to the destination context. We just have this here to
6303 * not have to deal with the issue, since cross-swapchain blits should be
6304 * rare. */
6305 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
6307 FIXME("Using fallback for cross-swapchain blit.\n");
6308 goto fallback;
6311 scale = src_surface
6312 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
6313 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
6314 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
6316 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
6317 if (src_surface)
6318 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
6319 else
6320 src_ds_flags = 0;
6322 if (src_ds_flags || dst_ds_flags)
6324 if (flags & WINEDDBLT_DEPTHFILL)
6326 float depth;
6328 TRACE("Depth fill.\n");
6330 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
6331 return WINED3DERR_INVALIDCALL;
6333 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
6334 return WINED3D_OK;
6336 else
6338 if (src_ds_flags != dst_ds_flags)
6340 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
6341 return WINED3DERR_INVALIDCALL;
6344 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->draw_binding, &src_rect,
6345 dst_surface, dst_surface->draw_binding, &dst_rect)))
6346 return WINED3D_OK;
6349 else
6351 /* In principle this would apply to depth blits as well, but we don't
6352 * implement those in the CPU blitter at the moment. */
6353 if ((dst_surface->flags & SFLAG_INSYSMEM)
6354 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
6356 if (scale)
6357 TRACE("Not doing sysmem blit because of scaling.\n");
6358 else if (convert)
6359 TRACE("Not doing sysmem blit because of format conversion.\n");
6360 else
6361 goto cpu;
6364 if (flags & WINEDDBLT_COLORFILL)
6366 struct wined3d_color color;
6368 TRACE("Color fill.\n");
6370 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
6371 goto fallback;
6373 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
6374 return WINED3D_OK;
6376 else
6378 TRACE("Color blit.\n");
6380 /* Upload */
6381 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
6383 if (scale)
6384 TRACE("Not doing upload because of scaling.\n");
6385 else if (convert)
6386 TRACE("Not doing upload because of format conversion.\n");
6387 else
6389 POINT dst_point = {dst_rect.left, dst_rect.top};
6391 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
6393 if (!surface_is_offscreen(dst_surface))
6394 surface_load_location(dst_surface, dst_surface->draw_binding);
6395 return WINED3D_OK;
6400 /* Use present for back -> front blits. The idea behind this is
6401 * that present is potentially faster than a blit, in particular
6402 * when FBO blits aren't available. Some ddraw applications like
6403 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
6404 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
6405 * applications can't blit directly to the frontbuffer. */
6406 if (dst_swapchain && dst_swapchain->back_buffers
6407 && dst_surface == dst_swapchain->front_buffer
6408 && src_surface == dst_swapchain->back_buffers[0])
6410 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
6412 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
6414 /* Set the swap effect to COPY, we don't want the backbuffer
6415 * to become undefined. */
6416 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
6417 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
6418 dst_swapchain->desc.swap_effect = swap_effect;
6420 return WINED3D_OK;
6423 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6424 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6425 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6427 TRACE("Using FBO blit.\n");
6429 surface_blt_fbo(device, filter,
6430 src_surface, src_surface->draw_binding, &src_rect,
6431 dst_surface, dst_surface->draw_binding, &dst_rect);
6432 surface_validate_location(dst_surface, dst_surface->draw_binding);
6433 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
6435 return WINED3D_OK;
6438 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6439 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6440 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6442 TRACE("Using arbfp blit.\n");
6444 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
6445 return WINED3D_OK;
6450 fallback:
6451 /* Special cases for render targets. */
6452 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
6453 return WINED3D_OK;
6455 cpu:
6457 /* For the rest call the X11 surface implementation. For render targets
6458 * this should be implemented OpenGL accelerated in surface_blt_special(),
6459 * other blits are rather rare. */
6460 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
6463 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
6464 const struct wined3d_resource_desc *desc, DWORD flags)
6466 struct wined3d_device *device = container->resource.device;
6467 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6468 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
6469 UINT multisample_quality = desc->multisample_quality;
6470 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
6471 unsigned int resource_size;
6472 HRESULT hr;
6474 if (multisample_quality > 0)
6476 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
6477 multisample_quality = 0;
6480 /* Quick lockable sanity check.
6481 * TODO: remove this after surfaces, usage and lockability have been debugged properly
6482 * this function is too deep to need to care about things like this.
6483 * Levels need to be checked too, since they all affect what can be done. */
6484 switch (desc->pool)
6486 case WINED3D_POOL_MANAGED:
6487 if (desc->usage & WINED3DUSAGE_DYNAMIC)
6488 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
6489 break;
6491 case WINED3D_POOL_DEFAULT:
6492 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
6493 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
6494 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
6495 break;
6497 case WINED3D_POOL_SCRATCH:
6498 case WINED3D_POOL_SYSTEM_MEM:
6499 break;
6501 default:
6502 FIXME("Unknown pool %#x.\n", desc->pool);
6503 break;
6506 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
6507 FIXME("Trying to create a render target that isn't in the default pool.\n");
6509 /* FIXME: Check that the format is supported by the device. */
6511 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
6512 if (!resource_size)
6513 return WINED3DERR_INVALIDCALL;
6515 if (device->wined3d->flags & WINED3D_NO3D)
6516 surface->surface_ops = &gdi_surface_ops;
6517 else
6518 surface->surface_ops = &surface_ops;
6520 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
6521 desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height, 1,
6522 resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
6524 WARN("Failed to initialize resource, returning %#x.\n", hr);
6525 return hr;
6528 surface_set_container(surface, container);
6529 list_init(&surface->overlays);
6531 /* Flags */
6532 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
6533 if (flags & WINED3D_SURFACE_DISCARD)
6534 surface->flags |= SFLAG_DISCARD;
6535 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
6536 surface->flags |= SFLAG_PIN_SYSMEM;
6537 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
6538 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
6540 /* I'm not sure if this qualifies as a hack or as an optimization. It
6541 * seems reasonable to assume that lockable render targets will get
6542 * locked, so we might as well set SFLAG_DYNLOCK right at surface
6543 * creation. However, the other reason we want to do this is that several
6544 * ddraw applications access surface memory while the surface isn't
6545 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
6546 * future locks prevents these from crashing. */
6547 if (lockable && (desc->usage & WINED3DUSAGE_RENDERTARGET))
6548 surface->flags |= SFLAG_DYNLOCK;
6550 /* Mark the texture as dirty so that it gets loaded first time around. */
6551 surface_set_dirty(surface);
6552 list_init(&surface->renderbuffers);
6554 TRACE("surface %p, memory %p, size %u\n",
6555 surface, surface->resource.allocatedMemory, surface->resource.size);
6557 /* Call the private setup routine */
6558 hr = surface->surface_ops->surface_private_setup(surface);
6559 if (FAILED(hr))
6561 ERR("Private setup failed, returning %#x\n", hr);
6562 surface_set_container(surface, NULL);
6563 surface_cleanup(surface);
6564 return hr;
6567 /* Similar to lockable rendertargets above, creating the DIB section
6568 * during surface initialization prevents the sysmem pointer from changing
6569 * after a wined3d_surface_getdc() call. */
6570 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
6571 && SUCCEEDED(surface_create_dib_section(surface)))
6573 wined3d_resource_free_sysmem(&surface->resource);
6574 surface->resource.allocatedMemory = surface->dib.bitmap_data;
6577 return hr;
6580 HRESULT CDECL wined3d_surface_create(struct wined3d_texture *container,
6581 const struct wined3d_resource_desc *desc, DWORD flags, struct wined3d_surface **surface)
6583 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
6584 const struct wined3d_parent_ops *parent_ops;
6585 struct wined3d_surface *object;
6586 void *parent;
6587 HRESULT hr;
6589 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), "
6590 "pool %s, multisample_type %#x, multisample_quality %u, flags %#x, surface %p.\n",
6591 container, desc->width, desc->height, debug_d3dformat(desc->format),
6592 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
6593 desc->multisample_type, desc->multisample_quality, flags, surface);
6595 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
6596 return E_OUTOFMEMORY;
6598 if (FAILED(hr = surface_init(object, container, desc, flags)))
6600 WARN("Failed to initialize surface, returning %#x.\n", hr);
6601 HeapFree(GetProcessHeap(), 0, object);
6602 return hr;
6605 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
6606 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
6608 WARN("Failed to create surface parent, hr %#x.\n", hr);
6609 surface_set_container(object, NULL);
6610 wined3d_surface_decref(object);
6611 return hr;
6614 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
6616 object->resource.parent = parent;
6617 object->resource.parent_ops = parent_ops;
6618 *surface = object;
6620 return hr;