wined3d: Get rid of the WINED3DSRGB enum.
[wine/wine-gecko.git] / dlls / wined3d / surface.c
blob6f6b5de7200bcecb76f8063aabfb3c9b6f2609b2
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 void 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)
2266 ERR("Not supported on scratch surfaces.\n");
2268 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2270 /* Reload if either the texture and sysmem have different ideas about the
2271 * color key, or the actual key values changed. */
2272 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2273 && (surface->gl_color_key.color_space_low_value != surface->src_blt_color_key.color_space_low_value
2274 || surface->gl_color_key.color_space_high_value != surface->src_blt_color_key.color_space_high_value)))
2276 TRACE("Reloading because of color keying\n");
2277 /* To perform the color key conversion we need a sysmem copy of
2278 * the surface. Make sure we have it. */
2280 surface_load_location(surface, SFLAG_INSYSMEM);
2281 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
2282 /* Switching color keying on / off may change the internal format. */
2283 if (ck_changed)
2284 surface_force_reload(surface);
2286 else if (!(surface->flags & flag))
2288 TRACE("Reloading because surface is dirty.\n");
2290 else
2292 TRACE("surface is already in texture\n");
2293 return;
2296 surface_load_location(surface, flag);
2297 surface_evict_sysmem(surface);
2300 /* See also float_16_to_32() in wined3d_private.h */
2301 static inline unsigned short float_32_to_16(const float *in)
2303 int exp = 0;
2304 float tmp = fabsf(*in);
2305 unsigned int mantissa;
2306 unsigned short ret;
2308 /* Deal with special numbers */
2309 if (*in == 0.0f)
2310 return 0x0000;
2311 if (isnan(*in))
2312 return 0x7c01;
2313 if (isinf(*in))
2314 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2316 if (tmp < powf(2, 10))
2320 tmp = tmp * 2.0f;
2321 exp--;
2322 } while (tmp < powf(2, 10));
2324 else if (tmp >= powf(2, 11))
2328 tmp /= 2.0f;
2329 exp++;
2330 } while (tmp >= powf(2, 11));
2333 mantissa = (unsigned int)tmp;
2334 if (tmp - mantissa >= 0.5f)
2335 ++mantissa; /* Round to nearest, away from zero. */
2337 exp += 10; /* Normalize the mantissa. */
2338 exp += 15; /* Exponent is encoded with excess 15. */
2340 if (exp > 30) /* too big */
2342 ret = 0x7c00; /* INF */
2344 else if (exp <= 0)
2346 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2347 while (exp <= 0)
2349 mantissa = mantissa >> 1;
2350 ++exp;
2352 ret = mantissa & 0x3ff;
2354 else
2356 ret = (exp << 10) | (mantissa & 0x3ff);
2359 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2360 return ret;
2363 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2365 ULONG refcount;
2367 TRACE("surface %p, swapchain %p, container %p.\n",
2368 surface, surface->swapchain, surface->container);
2370 if (surface->swapchain)
2371 return wined3d_swapchain_incref(surface->swapchain);
2373 if (surface->container)
2374 return wined3d_texture_incref(surface->container);
2376 refcount = InterlockedIncrement(&surface->resource.ref);
2377 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2379 return refcount;
2382 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2384 ULONG refcount;
2386 TRACE("surface %p, swapchain %p, container %p.\n",
2387 surface, surface->swapchain, surface->container);
2389 if (surface->swapchain)
2390 return wined3d_swapchain_decref(surface->swapchain);
2392 if (surface->container)
2393 return wined3d_texture_decref(surface->container);
2395 refcount = InterlockedDecrement(&surface->resource.ref);
2396 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2398 if (!refcount)
2400 surface_cleanup(surface);
2401 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2403 TRACE("Destroyed surface %p.\n", surface);
2404 HeapFree(GetProcessHeap(), 0, surface);
2407 return refcount;
2410 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2412 return resource_set_priority(&surface->resource, priority);
2415 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2417 return resource_get_priority(&surface->resource);
2420 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2422 TRACE("surface %p.\n", surface);
2424 if (!surface->resource.device->d3d_initialized)
2426 ERR("D3D not initialized.\n");
2427 return;
2430 wined3d_texture_preload(surface->container);
2433 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2435 TRACE("surface %p.\n", surface);
2437 return surface->resource.parent;
2440 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2442 TRACE("surface %p.\n", surface);
2444 return &surface->resource;
2447 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2449 TRACE("surface %p, flags %#x.\n", surface, flags);
2451 switch (flags)
2453 case WINEDDGBS_CANBLT:
2454 case WINEDDGBS_ISBLTDONE:
2455 return WINED3D_OK;
2457 default:
2458 return WINED3DERR_INVALIDCALL;
2462 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2464 TRACE("surface %p, flags %#x.\n", surface, flags);
2466 /* XXX: DDERR_INVALIDSURFACETYPE */
2468 switch (flags)
2470 case WINEDDGFS_CANFLIP:
2471 case WINEDDGFS_ISFLIPDONE:
2472 return WINED3D_OK;
2474 default:
2475 return WINED3DERR_INVALIDCALL;
2479 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2481 TRACE("surface %p.\n", surface);
2483 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2484 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2487 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2489 TRACE("surface %p.\n", surface);
2491 surface->flags &= ~SFLAG_LOST;
2492 return WINED3D_OK;
2495 void CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2497 TRACE("surface %p, palette %p.\n", surface, palette);
2499 if (surface->palette == palette)
2501 TRACE("Nop palette change.\n");
2502 return;
2505 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2506 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2508 surface->palette = palette;
2510 if (palette)
2512 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2513 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
2515 surface->surface_ops->surface_realize_palette(surface);
2519 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
2520 DWORD flags, const struct wined3d_color_key *color_key)
2522 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
2524 if (flags & WINEDDCKEY_COLORSPACE)
2526 FIXME(" colorkey value not supported (%08x) !\n", flags);
2527 return WINED3DERR_INVALIDCALL;
2530 /* Dirtify the surface, but only if a key was changed. */
2531 if (color_key)
2533 switch (flags & ~WINEDDCKEY_COLORSPACE)
2535 case WINEDDCKEY_DESTBLT:
2536 surface->dst_blt_color_key = *color_key;
2537 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
2538 break;
2540 case WINEDDCKEY_DESTOVERLAY:
2541 surface->dst_overlay_color_key = *color_key;
2542 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
2543 break;
2545 case WINEDDCKEY_SRCOVERLAY:
2546 surface->src_overlay_color_key = *color_key;
2547 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
2548 break;
2550 case WINEDDCKEY_SRCBLT:
2551 surface->src_blt_color_key = *color_key;
2552 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
2553 break;
2556 else
2558 switch (flags & ~WINEDDCKEY_COLORSPACE)
2560 case WINEDDCKEY_DESTBLT:
2561 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
2562 break;
2564 case WINEDDCKEY_DESTOVERLAY:
2565 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
2566 break;
2568 case WINEDDCKEY_SRCOVERLAY:
2569 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
2570 break;
2572 case WINEDDCKEY_SRCBLT:
2573 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
2574 break;
2578 return WINED3D_OK;
2581 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
2583 TRACE("surface %p.\n", surface);
2585 return surface->palette;
2588 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2590 const struct wined3d_format *format = surface->resource.format;
2591 DWORD pitch;
2593 TRACE("surface %p.\n", surface);
2595 if (surface->pitch)
2596 return surface->pitch;
2598 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
2600 /* Since compressed formats are block based, pitch means the amount of
2601 * bytes to the next row of block rather than the next row of pixels. */
2602 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
2603 pitch = row_block_count * format->block_byte_count;
2605 else
2607 unsigned char alignment = surface->resource.device->surface_alignment;
2608 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
2609 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2612 TRACE("Returning %u.\n", pitch);
2614 return pitch;
2617 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem, UINT pitch)
2619 TRACE("surface %p, mem %p.\n", surface, mem);
2621 if (surface->resource.map_count || (surface->flags & SFLAG_DCINUSE))
2623 WARN("Surface is mapped or the DC is in use.\n");
2624 return WINED3DERR_INVALIDCALL;
2627 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
2628 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2630 ERR("Not supported on render targets.\n");
2631 return WINED3DERR_INVALIDCALL;
2634 if (mem && mem != surface->resource.allocatedMemory)
2636 /* Do I have to copy the old surface content? */
2637 if (surface->flags & SFLAG_DIBSECTION)
2639 DeleteDC(surface->hDC);
2640 DeleteObject(surface->dib.DIBsection);
2641 surface->dib.bitmap_data = NULL;
2642 surface->resource.allocatedMemory = NULL;
2643 surface->hDC = NULL;
2644 surface->flags &= ~SFLAG_DIBSECTION;
2646 else if (!(surface->flags & SFLAG_USERPTR))
2648 wined3d_resource_free_sysmem(&surface->resource);
2650 surface->resource.allocatedMemory = mem;
2651 surface->flags |= SFLAG_USERPTR;
2653 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
2654 surface_validate_location(surface, SFLAG_INSYSMEM);
2655 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
2657 /* For client textures OpenGL has to be notified. */
2658 if (surface->flags & SFLAG_CLIENT)
2659 surface_release_client_storage(surface);
2661 else if (surface->flags & SFLAG_USERPTR)
2663 /* heap_memory should be NULL already. */
2664 if (surface->resource.heap_memory)
2665 ERR("User pointer surface has heap memory allocated.\n");
2667 if (!mem)
2669 surface->resource.allocatedMemory = NULL;
2670 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
2672 if (surface->flags & SFLAG_CLIENT)
2673 surface_release_client_storage(surface);
2675 surface_prepare_system_memory(surface);
2678 surface_validate_location(surface, SFLAG_INSYSMEM);
2679 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
2682 surface->pitch = pitch;
2684 return WINED3D_OK;
2687 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2689 LONG w, h;
2691 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2693 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2695 WARN("Not an overlay surface.\n");
2696 return WINEDDERR_NOTAOVERLAYSURFACE;
2699 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2700 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2701 surface->overlay_destrect.left = x;
2702 surface->overlay_destrect.top = y;
2703 surface->overlay_destrect.right = x + w;
2704 surface->overlay_destrect.bottom = y + h;
2706 return WINED3D_OK;
2709 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2711 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2713 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2715 TRACE("Not an overlay surface.\n");
2716 return WINEDDERR_NOTAOVERLAYSURFACE;
2719 if (!surface->overlay_dest)
2721 TRACE("Overlay not visible.\n");
2722 *x = 0;
2723 *y = 0;
2724 return WINEDDERR_OVERLAYNOTVISIBLE;
2727 *x = surface->overlay_destrect.left;
2728 *y = surface->overlay_destrect.top;
2730 TRACE("Returning position %d, %d.\n", *x, *y);
2732 return WINED3D_OK;
2735 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2736 DWORD flags, struct wined3d_surface *ref)
2738 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2740 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2742 TRACE("Not an overlay surface.\n");
2743 return WINEDDERR_NOTAOVERLAYSURFACE;
2746 return WINED3D_OK;
2749 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2750 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2752 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2753 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2755 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2757 WARN("Not an overlay surface.\n");
2758 return WINEDDERR_NOTAOVERLAYSURFACE;
2760 else if (!dst_surface)
2762 WARN("Dest surface is NULL.\n");
2763 return WINED3DERR_INVALIDCALL;
2766 if (src_rect)
2768 surface->overlay_srcrect = *src_rect;
2770 else
2772 surface->overlay_srcrect.left = 0;
2773 surface->overlay_srcrect.top = 0;
2774 surface->overlay_srcrect.right = surface->resource.width;
2775 surface->overlay_srcrect.bottom = surface->resource.height;
2778 if (dst_rect)
2780 surface->overlay_destrect = *dst_rect;
2782 else
2784 surface->overlay_destrect.left = 0;
2785 surface->overlay_destrect.top = 0;
2786 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2787 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2790 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2792 surface->overlay_dest = NULL;
2793 list_remove(&surface->overlay_entry);
2796 if (flags & WINEDDOVER_SHOW)
2798 if (surface->overlay_dest != dst_surface)
2800 surface->overlay_dest = dst_surface;
2801 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2804 else if (flags & WINEDDOVER_HIDE)
2806 /* tests show that the rectangles are erased on hide */
2807 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2808 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2809 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2810 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2811 surface->overlay_dest = NULL;
2814 return WINED3D_OK;
2817 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
2818 UINT width, UINT height, enum wined3d_format_id format_id,
2819 enum wined3d_multisample_type multisample_type, UINT multisample_quality)
2821 struct wined3d_device *device = surface->resource.device;
2822 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2823 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
2824 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height, 1);
2826 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u.\n",
2827 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type);
2829 if (!resource_size)
2830 return WINED3DERR_INVALIDCALL;
2832 if (device->d3d_initialized)
2833 surface->resource.resource_ops->resource_unload(&surface->resource);
2835 if (surface->flags & SFLAG_DIBSECTION)
2837 DeleteDC(surface->hDC);
2838 DeleteObject(surface->dib.DIBsection);
2839 surface->dib.bitmap_data = NULL;
2840 surface->flags &= ~SFLAG_DIBSECTION;
2843 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_USERPTR);
2844 surface->resource.allocatedMemory = NULL;
2845 wined3d_resource_free_sysmem(&surface->resource);
2847 surface->resource.width = width;
2848 surface->resource.height = height;
2849 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2850 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2852 surface->pow2Width = width;
2853 surface->pow2Height = height;
2855 else
2857 surface->pow2Width = surface->pow2Height = 1;
2858 while (surface->pow2Width < width)
2859 surface->pow2Width <<= 1;
2860 while (surface->pow2Height < height)
2861 surface->pow2Height <<= 1;
2864 if (surface->pow2Width != width || surface->pow2Height != height)
2865 surface->flags |= SFLAG_NONPOW2;
2866 else
2867 surface->flags &= ~SFLAG_NONPOW2;
2869 surface->resource.format = format;
2870 surface->resource.multisample_type = multisample_type;
2871 surface->resource.multisample_quality = multisample_quality;
2872 surface->resource.size = resource_size;
2874 if (!surface_init_sysmem(surface))
2875 return E_OUTOFMEMORY;
2877 return WINED3D_OK;
2880 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2881 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2883 unsigned short *dst_s;
2884 const float *src_f;
2885 unsigned int x, y;
2887 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2889 for (y = 0; y < h; ++y)
2891 src_f = (const float *)(src + y * pitch_in);
2892 dst_s = (unsigned short *) (dst + y * pitch_out);
2893 for (x = 0; x < w; ++x)
2895 dst_s[x] = float_32_to_16(src_f + x);
2900 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2901 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2903 static const unsigned char convert_5to8[] =
2905 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2906 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2907 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2908 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2910 static const unsigned char convert_6to8[] =
2912 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2913 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2914 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2915 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2916 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2917 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2918 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2919 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2921 unsigned int x, y;
2923 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2925 for (y = 0; y < h; ++y)
2927 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2928 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2929 for (x = 0; x < w; ++x)
2931 WORD pixel = src_line[x];
2932 dst_line[x] = 0xff000000
2933 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2934 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2935 | convert_5to8[(pixel & 0x001f)];
2940 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2941 * in both cases we're just setting the X / Alpha channel to 0xff. */
2942 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2943 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2945 unsigned int x, y;
2947 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2949 for (y = 0; y < h; ++y)
2951 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2952 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2954 for (x = 0; x < w; ++x)
2956 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2961 static inline BYTE cliptobyte(int x)
2963 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2966 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2967 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2969 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2970 unsigned int x, y;
2972 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2974 for (y = 0; y < h; ++y)
2976 const BYTE *src_line = src + y * pitch_in;
2977 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2978 for (x = 0; x < w; ++x)
2980 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2981 * C = Y - 16; D = U - 128; E = V - 128;
2982 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2983 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2984 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2985 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2986 * U and V are shared between the pixels. */
2987 if (!(x & 1)) /* For every even pixel, read new U and V. */
2989 d = (int) src_line[1] - 128;
2990 e = (int) src_line[3] - 128;
2991 r2 = 409 * e + 128;
2992 g2 = - 100 * d - 208 * e + 128;
2993 b2 = 516 * d + 128;
2995 c2 = 298 * ((int) src_line[0] - 16);
2996 dst_line[x] = 0xff000000
2997 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2998 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2999 | cliptobyte((c2 + b2) >> 8); /* blue */
3000 /* Scale RGB values to 0..255 range,
3001 * then clip them if still not in range (may be negative),
3002 * then shift them within DWORD if necessary. */
3003 src_line += 2;
3008 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3009 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3011 unsigned int x, y;
3012 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3014 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3016 for (y = 0; y < h; ++y)
3018 const BYTE *src_line = src + y * pitch_in;
3019 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3020 for (x = 0; x < w; ++x)
3022 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3023 * C = Y - 16; D = U - 128; E = V - 128;
3024 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3025 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3026 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3027 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3028 * U and V are shared between the pixels. */
3029 if (!(x & 1)) /* For every even pixel, read new U and V. */
3031 d = (int) src_line[1] - 128;
3032 e = (int) src_line[3] - 128;
3033 r2 = 409 * e + 128;
3034 g2 = - 100 * d - 208 * e + 128;
3035 b2 = 516 * d + 128;
3037 c2 = 298 * ((int) src_line[0] - 16);
3038 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3039 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3040 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3041 /* Scale RGB values to 0..255 range,
3042 * then clip them if still not in range (may be negative),
3043 * then shift them within DWORD if necessary. */
3044 src_line += 2;
3049 struct d3dfmt_converter_desc
3051 enum wined3d_format_id from, to;
3052 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3055 static const struct d3dfmt_converter_desc converters[] =
3057 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3058 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3059 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3060 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3061 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3062 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3065 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
3066 enum wined3d_format_id to)
3068 unsigned int i;
3070 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
3072 if (converters[i].from == from && converters[i].to == to)
3073 return &converters[i];
3076 return NULL;
3079 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3081 struct wined3d_map_desc src_map, dst_map;
3082 const struct d3dfmt_converter_desc *conv;
3083 struct wined3d_texture *ret = NULL;
3084 struct wined3d_resource_desc desc;
3085 struct wined3d_surface *dst;
3087 conv = find_converter(source->resource.format->id, to_fmt);
3088 if (!conv)
3090 FIXME("Cannot find a conversion function from format %s to %s.\n",
3091 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3092 return NULL;
3095 /* FIXME: Multisampled conversion? */
3096 wined3d_resource_get_desc(&source->resource, &desc);
3097 desc.format = to_fmt;
3098 desc.usage = 0;
3099 desc.pool = WINED3D_POOL_SCRATCH;
3100 if (FAILED(wined3d_texture_create_2d(source->resource.device, &desc, 1,
3101 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, &wined3d_null_parent_ops, &ret)))
3103 ERR("Failed to create a destination surface for conversion.\n");
3104 return NULL;
3106 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
3108 memset(&src_map, 0, sizeof(src_map));
3109 memset(&dst_map, 0, sizeof(dst_map));
3111 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
3113 ERR("Failed to lock the source surface.\n");
3114 wined3d_texture_decref(ret);
3115 return NULL;
3117 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
3119 ERR("Failed to lock the destination surface.\n");
3120 wined3d_surface_unmap(source);
3121 wined3d_texture_decref(ret);
3122 return NULL;
3125 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
3126 source->resource.width, source->resource.height);
3128 wined3d_surface_unmap(dst);
3129 wined3d_surface_unmap(source);
3131 return ret;
3134 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3135 unsigned int bpp, UINT pitch, DWORD color)
3137 BYTE *first;
3138 unsigned int x, y;
3140 /* Do first row */
3142 #define COLORFILL_ROW(type) \
3143 do { \
3144 type *d = (type *)buf; \
3145 for (x = 0; x < width; ++x) \
3146 d[x] = (type)color; \
3147 } while(0)
3149 switch (bpp)
3151 case 1:
3152 COLORFILL_ROW(BYTE);
3153 break;
3155 case 2:
3156 COLORFILL_ROW(WORD);
3157 break;
3159 case 3:
3161 BYTE *d = buf;
3162 for (x = 0; x < width; ++x, d += 3)
3164 d[0] = (color ) & 0xff;
3165 d[1] = (color >> 8) & 0xff;
3166 d[2] = (color >> 16) & 0xff;
3168 break;
3170 case 4:
3171 COLORFILL_ROW(DWORD);
3172 break;
3174 default:
3175 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3176 return WINED3DERR_NOTAVAILABLE;
3179 #undef COLORFILL_ROW
3181 /* Now copy first row. */
3182 first = buf;
3183 for (y = 1; y < height; ++y)
3185 buf += pitch;
3186 memcpy(buf, first, width * bpp);
3189 return WINED3D_OK;
3192 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
3194 return surface_from_resource(resource);
3197 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3199 TRACE("surface %p.\n", surface);
3201 if (!surface->resource.map_count)
3203 WARN("Trying to unmap unmapped surface.\n");
3204 return WINEDDERR_NOTLOCKED;
3206 --surface->resource.map_count;
3208 surface->surface_ops->surface_unmap(surface);
3210 return WINED3D_OK;
3213 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3214 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
3216 const struct wined3d_format *format = surface->resource.format;
3218 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
3219 surface, map_desc, wine_dbgstr_rect(rect), flags);
3221 if (surface->resource.map_count)
3223 WARN("Surface is already mapped.\n");
3224 return WINED3DERR_INVALIDCALL;
3227 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
3228 && !surface_check_block_align(surface, rect))
3230 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3231 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3233 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
3234 return WINED3DERR_INVALIDCALL;
3237 ++surface->resource.map_count;
3239 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
3240 WARN("Trying to lock unlockable surface.\n");
3242 /* Performance optimization: Count how often a surface is mapped, if it is
3243 * mapped regularly do not throw away the system memory copy. This avoids
3244 * the need to download the surface from OpenGL all the time. The surface
3245 * is still downloaded if the OpenGL texture is changed. */
3246 if (!(surface->flags & SFLAG_DYNLOCK))
3248 if (++surface->lockCount > MAXLOCKCOUNT)
3250 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3251 surface->flags |= SFLAG_DYNLOCK;
3255 surface->surface_ops->surface_map(surface, rect, flags);
3257 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3258 map_desc->row_pitch = surface->resource.width * format->byte_count;
3259 else
3260 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
3261 map_desc->slice_pitch = 0;
3263 if (!rect)
3265 map_desc->data = surface->resource.allocatedMemory;
3266 surface->lockedRect.left = 0;
3267 surface->lockedRect.top = 0;
3268 surface->lockedRect.right = surface->resource.width;
3269 surface->lockedRect.bottom = surface->resource.height;
3271 else
3273 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3275 /* Compressed textures are block based, so calculate the offset of
3276 * the block that contains the top-left pixel of the locked rectangle. */
3277 map_desc->data = surface->resource.allocatedMemory
3278 + ((rect->top / format->block_height) * map_desc->row_pitch)
3279 + ((rect->left / format->block_width) * format->block_byte_count);
3281 else
3283 map_desc->data = surface->resource.allocatedMemory
3284 + (map_desc->row_pitch * rect->top)
3285 + (rect->left * format->byte_count);
3287 surface->lockedRect.left = rect->left;
3288 surface->lockedRect.top = rect->top;
3289 surface->lockedRect.right = rect->right;
3290 surface->lockedRect.bottom = rect->bottom;
3293 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3294 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
3296 return WINED3D_OK;
3299 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3301 struct wined3d_map_desc map;
3302 HRESULT hr;
3304 TRACE("surface %p, dc %p.\n", surface, dc);
3306 if (surface->flags & SFLAG_USERPTR)
3308 ERR("Not supported on surfaces with application-provided memory.\n");
3309 return WINEDDERR_NODC;
3312 /* Give more detailed info for ddraw. */
3313 if (surface->flags & SFLAG_DCINUSE)
3314 return WINEDDERR_DCALREADYCREATED;
3316 /* Can't GetDC if the surface is locked. */
3317 if (surface->resource.map_count)
3318 return WINED3DERR_INVALIDCALL;
3320 /* Create a DIB section if there isn't a dc yet. */
3321 if (!surface->hDC)
3323 if (surface->flags & SFLAG_CLIENT)
3325 surface_load_location(surface, SFLAG_INSYSMEM);
3326 surface_release_client_storage(surface);
3328 hr = surface_create_dib_section(surface);
3329 if (FAILED(hr))
3330 return WINED3DERR_INVALIDCALL;
3332 /* Use the DIB section from now on if we are not using a PBO. */
3333 if (!(surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)))
3335 wined3d_resource_free_sysmem(&surface->resource);
3336 surface->resource.allocatedMemory = surface->dib.bitmap_data;
3340 /* Map the surface. */
3341 hr = wined3d_surface_map(surface, &map, NULL, 0);
3342 if (FAILED(hr))
3344 ERR("Map failed, hr %#x.\n", hr);
3345 return hr;
3348 /* Sync the DIB with the PBO. This can't be done earlier because Map()
3349 * activates the allocatedMemory. */
3350 if (surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM))
3351 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
3353 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3354 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3356 /* GetDC on palettized formats is unsupported in D3D9, and the method
3357 * is missing in D3D8, so this should only be used for DX <=7
3358 * surfaces (with non-device palettes). */
3359 const PALETTEENTRY *pal = NULL;
3361 if (surface->palette)
3363 pal = surface->palette->palents;
3365 else
3367 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3368 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3370 if (dds_primary && dds_primary->palette)
3371 pal = dds_primary->palette->palents;
3374 if (pal)
3376 RGBQUAD col[256];
3377 unsigned int i;
3379 for (i = 0; i < 256; ++i)
3381 col[i].rgbRed = pal[i].peRed;
3382 col[i].rgbGreen = pal[i].peGreen;
3383 col[i].rgbBlue = pal[i].peBlue;
3384 col[i].rgbReserved = 0;
3386 SetDIBColorTable(surface->hDC, 0, 256, col);
3390 surface->flags |= SFLAG_DCINUSE;
3392 *dc = surface->hDC;
3393 TRACE("Returning dc %p.\n", *dc);
3395 return WINED3D_OK;
3398 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3400 TRACE("surface %p, dc %p.\n", surface, dc);
3402 if (!(surface->flags & SFLAG_DCINUSE))
3403 return WINEDDERR_NODC;
3405 if (surface->hDC != dc)
3407 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3408 dc, surface->hDC);
3409 return WINEDDERR_NODC;
3412 /* Copy the contents of the DIB over to the PBO. */
3413 if ((surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)) && surface->resource.allocatedMemory)
3414 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3416 /* We locked first, so unlock now. */
3417 wined3d_surface_unmap(surface);
3419 surface->flags &= ~SFLAG_DCINUSE;
3421 return WINED3D_OK;
3424 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3426 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3428 if (flags)
3430 static UINT once;
3431 if (!once++)
3432 FIXME("Ignoring flags %#x.\n", flags);
3433 else
3434 WARN("Ignoring flags %#x.\n", flags);
3437 if (surface->swapchain)
3439 ERR("Not supported on swapchain surfaces.\n");
3440 return WINEDDERR_NOTFLIPPABLE;
3443 flip_surface(surface, override);
3444 return WINED3D_OK;
3447 /* Read the framebuffer back into the surface */
3448 static void read_from_framebuffer(struct wined3d_surface *surface, void *dest, UINT pitch)
3450 struct wined3d_device *device = surface->resource.device;
3451 const struct wined3d_gl_info *gl_info;
3452 struct wined3d_context *context;
3453 BYTE *mem;
3454 GLint fmt;
3455 GLint type;
3456 BYTE *row, *top, *bottom;
3457 int i;
3458 BOOL bpp;
3459 BOOL srcIsUpsideDown;
3460 GLint rowLen = 0;
3461 GLint skipPix = 0;
3462 GLint skipRow = 0;
3464 context = context_acquire(device, surface);
3465 context_apply_blit_state(context, device);
3466 gl_info = context->gl_info;
3468 /* Select the correct read buffer, and give some debug output.
3469 * There is no need to keep track of the current read buffer or reset it, every part of the code
3470 * that reads sets the read buffer as desired.
3472 if (surface_is_offscreen(surface))
3474 /* Mapping the primary render target which is not on a swapchain.
3475 * Read from the back buffer. */
3476 TRACE("Mapping offscreen render target.\n");
3477 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3478 srcIsUpsideDown = TRUE;
3480 else
3482 /* Onscreen surfaces are always part of a swapchain */
3483 GLenum buffer = surface_get_gl_buffer(surface);
3484 TRACE("Mapping %#x buffer.\n", buffer);
3485 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
3486 checkGLcall("glReadBuffer");
3487 srcIsUpsideDown = FALSE;
3490 switch (surface->resource.format->id)
3492 case WINED3DFMT_P8_UINT:
3494 if (swapchain_is_p8(context->swapchain))
3496 /* In case of P8 render targets the index is stored in the alpha component */
3497 fmt = GL_ALPHA;
3498 type = GL_UNSIGNED_BYTE;
3499 mem = dest;
3500 bpp = surface->resource.format->byte_count;
3502 else
3504 /* GL can't return palettized data, so read ARGB pixels into a
3505 * separate block of memory and convert them into palettized format
3506 * in software. Slow, but if the app means to use palettized render
3507 * targets and locks it...
3509 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
3510 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
3511 * for the color channels when palettizing the colors.
3513 fmt = GL_RGB;
3514 type = GL_UNSIGNED_BYTE;
3515 pitch *= 3;
3516 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
3517 if (!mem)
3519 ERR("Out of memory\n");
3520 return;
3522 bpp = surface->resource.format->byte_count * 3;
3525 break;
3527 default:
3528 mem = dest;
3529 fmt = surface->resource.format->glFormat;
3530 type = surface->resource.format->glType;
3531 bpp = surface->resource.format->byte_count;
3534 if (surface->flags & SFLAG_PBO)
3536 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
3537 checkGLcall("glBindBufferARB");
3538 if (mem)
3540 ERR("mem not null for pbo -- unexpected\n");
3541 mem = NULL;
3545 /* Save old pixel store pack state */
3546 gl_info->gl_ops.gl.p_glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
3547 checkGLcall("glGetIntegerv");
3548 gl_info->gl_ops.gl.p_glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
3549 checkGLcall("glGetIntegerv");
3550 gl_info->gl_ops.gl.p_glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
3551 checkGLcall("glGetIntegerv");
3553 /* Setup pixel store pack state -- to glReadPixels into the correct place */
3554 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
3555 checkGLcall("glPixelStorei");
3556 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
3557 checkGLcall("glPixelStorei");
3558 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_ROWS, 0);
3559 checkGLcall("glPixelStorei");
3561 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
3562 surface->resource.width, surface->resource.height,
3563 fmt, type, mem);
3564 checkGLcall("glReadPixels");
3566 /* Reset previous pixel store pack state */
3567 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
3568 checkGLcall("glPixelStorei");
3569 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
3570 checkGLcall("glPixelStorei");
3571 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
3572 checkGLcall("glPixelStorei");
3574 if (surface->flags & SFLAG_PBO)
3576 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
3577 checkGLcall("glBindBufferARB");
3579 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
3580 * to get a pointer to it and perform the flipping in software. This is a lot
3581 * faster than calling glReadPixels for each line. In case we want more speed
3582 * we should rerender it flipped in a FBO and read the data back from the FBO. */
3583 if (!srcIsUpsideDown)
3585 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
3586 checkGLcall("glBindBufferARB");
3588 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3589 checkGLcall("glMapBufferARB");
3593 /* TODO: Merge this with the palettization loop below for P8 targets */
3594 if (!srcIsUpsideDown)
3596 UINT len;
3597 /* glReadPixels returns the image upside down, and there is no way to prevent this.
3598 Flip the lines in software */
3599 len = surface->resource.width * bpp;
3601 row = HeapAlloc(GetProcessHeap(), 0, len);
3602 if (!row)
3604 ERR("Out of memory\n");
3605 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
3606 HeapFree(GetProcessHeap(), 0, mem);
3607 return;
3610 top = mem;
3611 bottom = mem + pitch * (surface->resource.height - 1);
3612 for (i = 0; i < surface->resource.height / 2; i++)
3614 memcpy(row, top, len);
3615 memcpy(top, bottom, len);
3616 memcpy(bottom, row, len);
3617 top += pitch;
3618 bottom -= pitch;
3620 HeapFree(GetProcessHeap(), 0, row);
3622 /* Unmap the temp PBO buffer */
3623 if (surface->flags & SFLAG_PBO)
3625 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
3626 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
3630 /* For P8 textures we need to perform an inverse palette lookup. This is
3631 * done by searching for a palette index which matches the RGB value.
3632 * Note this isn't guaranteed to work when there are multiple entries for
3633 * the same color but we have no choice. In case of P8 render targets,
3634 * the index is stored in the alpha component so no conversion is needed. */
3635 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !swapchain_is_p8(context->swapchain))
3637 const PALETTEENTRY *pal = NULL;
3638 DWORD width = pitch / 3;
3639 int x, y, c;
3641 if (surface->palette)
3643 pal = surface->palette->palents;
3645 else
3647 ERR("Palette is missing, cannot perform inverse palette lookup\n");
3648 HeapFree(GetProcessHeap(), 0, mem);
3649 return;
3652 for (y = 0; y < surface->resource.height; y++)
3654 for (x = 0; x < surface->resource.width; x++)
3656 /* start lines pixels */
3657 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
3658 const BYTE *green = blue + 1;
3659 const BYTE *red = green + 1;
3661 for (c = 0; c < 256; c++)
3663 if (*red == pal[c].peRed
3664 && *green == pal[c].peGreen
3665 && *blue == pal[c].peBlue)
3667 *((BYTE *) dest + y * width + x) = c;
3668 break;
3673 HeapFree(GetProcessHeap(), 0, mem);
3676 context_release(context);
3679 /* Read the framebuffer contents into a texture. Note that this function
3680 * doesn't do any kind of flipping. Using this on an onscreen surface will
3681 * result in a flipped D3D texture. */
3682 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
3684 struct wined3d_device *device = surface->resource.device;
3685 const struct wined3d_gl_info *gl_info;
3686 struct wined3d_context *context;
3688 context = context_acquire(device, surface);
3689 gl_info = context->gl_info;
3690 device_invalidate_state(device, STATE_FRAMEBUFFER);
3692 surface_prepare_texture(surface, context, srgb);
3693 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3695 TRACE("Reading back offscreen render target %p.\n", surface);
3697 if (surface_is_offscreen(surface))
3698 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3699 else
3700 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
3701 checkGLcall("glReadBuffer");
3703 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
3704 0, 0, 0, 0, surface->resource.width, surface->resource.height);
3705 checkGLcall("glCopyTexSubImage2D");
3707 context_release(context);
3710 /* Context activation is done by the caller. */
3711 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
3712 struct wined3d_context *context, BOOL srgb)
3714 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
3715 enum wined3d_conversion_type convert;
3716 struct wined3d_format format;
3718 if (surface->flags & alloc_flag) return;
3720 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
3721 if (convert != WINED3D_CT_NONE || format.convert)
3722 surface->flags |= SFLAG_CONVERTED;
3723 else surface->flags &= ~SFLAG_CONVERTED;
3725 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3726 surface_allocate_surface(surface, context->gl_info, &format, srgb);
3727 surface->flags |= alloc_flag;
3730 /* Context activation is done by the caller. */
3731 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
3733 struct wined3d_texture *texture = surface->container;
3734 UINT sub_count = texture->level_count * texture->layer_count;
3735 UINT i;
3737 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
3739 for (i = 0; i < sub_count; ++i)
3741 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
3742 surface_prepare_texture_internal(s, context, srgb);
3745 return;
3748 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
3750 if (multisample)
3752 if (surface->rb_multisample)
3753 return;
3755 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
3756 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
3757 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
3758 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
3759 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
3761 else
3763 if (surface->rb_resolved)
3764 return;
3766 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
3767 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
3768 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
3769 surface->pow2Width, surface->pow2Height);
3770 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
3774 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
3776 /* FIXME: Is this really how color keys are supposed to work? I think it
3777 * makes more sense to compare the individual channels. */
3778 return color >= color_key->color_space_low_value
3779 && color <= color_key->color_space_high_value;
3782 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
3784 const struct wined3d_device *device = surface->resource.device;
3785 const struct wined3d_palette *pal = surface->palette;
3786 BOOL index_in_alpha = FALSE;
3787 unsigned int i;
3789 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
3790 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
3791 * is slow. Further RGB->P8 conversion is not possible because palettes can have
3792 * duplicate entries. Store the color key in the unused alpha component to speed the
3793 * download up and to make conversion unneeded. */
3794 index_in_alpha = swapchain_is_p8(device->swapchains[0]);
3796 if (!pal)
3798 FIXME("No palette set.\n");
3799 if (index_in_alpha)
3801 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
3802 * there's no palette at this time. */
3803 for (i = 0; i < 256; i++) table[i][3] = i;
3806 else
3808 TRACE("Using surface palette %p\n", pal);
3809 /* Get the surface's palette */
3810 for (i = 0; i < 256; ++i)
3812 table[i][0] = pal->palents[i].peRed;
3813 table[i][1] = pal->palents[i].peGreen;
3814 table[i][2] = pal->palents[i].peBlue;
3816 /* When index_in_alpha is set the palette index is stored in the
3817 * alpha component. In case of a readback we can then read
3818 * GL_ALPHA. Color keying is handled in surface_blt_special() using a
3819 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
3820 * color key itself is passed to glAlphaFunc in other cases the
3821 * alpha component of pixels that should be masked away is set to 0. */
3822 if (index_in_alpha)
3823 table[i][3] = i;
3824 else if (colorkey && color_in_range(&surface->src_blt_color_key, i))
3825 table[i][3] = 0x00;
3826 else if (pal->flags & WINEDDPCAPS_ALPHA)
3827 table[i][3] = pal->palents[i].peFlags;
3828 else
3829 table[i][3] = 0xff;
3834 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
3835 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
3837 const BYTE *source;
3838 BYTE *dest;
3840 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
3841 src, dst, pitch, width, height, outpitch, conversion_type, surface);
3843 switch (conversion_type)
3845 case WINED3D_CT_NONE:
3847 memcpy(dst, src, pitch * height);
3848 break;
3851 case WINED3D_CT_PALETTED:
3852 case WINED3D_CT_PALETTED_CK:
3854 BYTE table[256][4];
3855 unsigned int x, y;
3857 d3dfmt_p8_init_palette(surface, table, (conversion_type == WINED3D_CT_PALETTED_CK));
3859 for (y = 0; y < height; y++)
3861 source = src + pitch * y;
3862 dest = dst + outpitch * y;
3863 /* This is an 1 bpp format, using the width here is fine */
3864 for (x = 0; x < width; x++) {
3865 BYTE color = *source++;
3866 *dest++ = table[color][0];
3867 *dest++ = table[color][1];
3868 *dest++ = table[color][2];
3869 *dest++ = table[color][3];
3873 break;
3875 case WINED3D_CT_CK_565:
3877 /* Converting the 565 format in 5551 packed to emulate color-keying.
3879 Note : in all these conversion, it would be best to average the averaging
3880 pixels to get the color of the pixel that will be color-keyed to
3881 prevent 'color bleeding'. This will be done later on if ever it is
3882 too visible.
3884 Note2: Nvidia documents say that their driver does not support alpha + color keying
3885 on the same surface and disables color keying in such a case
3887 unsigned int x, y;
3888 const WORD *Source;
3889 WORD *Dest;
3891 TRACE("Color keyed 565\n");
3893 for (y = 0; y < height; y++) {
3894 Source = (const WORD *)(src + y * pitch);
3895 Dest = (WORD *) (dst + y * outpitch);
3896 for (x = 0; x < width; x++ ) {
3897 WORD color = *Source++;
3898 *Dest = ((color & 0xffc0) | ((color & 0x1f) << 1));
3899 if (!color_in_range(&surface->src_blt_color_key, color))
3900 *Dest |= 0x0001;
3901 Dest++;
3905 break;
3907 case WINED3D_CT_CK_5551:
3909 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
3910 unsigned int x, y;
3911 const WORD *Source;
3912 WORD *Dest;
3913 TRACE("Color keyed 5551\n");
3914 for (y = 0; y < height; y++) {
3915 Source = (const WORD *)(src + y * pitch);
3916 Dest = (WORD *) (dst + y * outpitch);
3917 for (x = 0; x < width; x++ ) {
3918 WORD color = *Source++;
3919 *Dest = color;
3920 if (!color_in_range(&surface->src_blt_color_key, color))
3921 *Dest |= (1 << 15);
3922 else
3923 *Dest &= ~(1 << 15);
3924 Dest++;
3928 break;
3930 case WINED3D_CT_CK_RGB24:
3932 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
3933 unsigned int x, y;
3934 for (y = 0; y < height; y++)
3936 source = src + pitch * y;
3937 dest = dst + outpitch * y;
3938 for (x = 0; x < width; x++) {
3939 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
3940 DWORD dstcolor = color << 8;
3941 if (!color_in_range(&surface->src_blt_color_key, color))
3942 dstcolor |= 0xff;
3943 *(DWORD*)dest = dstcolor;
3944 source += 3;
3945 dest += 4;
3949 break;
3951 case WINED3D_CT_RGB32_888:
3953 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
3954 unsigned int x, y;
3955 for (y = 0; y < height; y++)
3957 source = src + pitch * y;
3958 dest = dst + outpitch * y;
3959 for (x = 0; x < width; x++) {
3960 DWORD color = 0xffffff & *(const DWORD*)source;
3961 DWORD dstcolor = color << 8;
3962 if (!color_in_range(&surface->src_blt_color_key, color))
3963 dstcolor |= 0xff;
3964 *(DWORD*)dest = dstcolor;
3965 source += 4;
3966 dest += 4;
3970 break;
3972 case WINED3D_CT_CK_ARGB32:
3974 unsigned int x, y;
3975 for (y = 0; y < height; ++y)
3977 source = src + pitch * y;
3978 dest = dst + outpitch * y;
3979 for (x = 0; x < width; ++x)
3981 DWORD color = *(const DWORD *)source;
3982 if (color_in_range(&surface->src_blt_color_key, color))
3983 color &= ~0xff000000;
3984 *(DWORD*)dest = color;
3985 source += 4;
3986 dest += 4;
3990 break;
3992 default:
3993 ERR("Unsupported conversion type %#x.\n", conversion_type);
3995 return WINED3D_OK;
3998 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4000 /* Flip the surface contents */
4001 /* Flip the DC */
4003 HDC tmp;
4004 tmp = front->hDC;
4005 front->hDC = back->hDC;
4006 back->hDC = tmp;
4009 /* Flip the DIBsection */
4011 HBITMAP tmp = front->dib.DIBsection;
4012 front->dib.DIBsection = back->dib.DIBsection;
4013 back->dib.DIBsection = tmp;
4016 /* Flip the surface data */
4018 void* tmp;
4020 tmp = front->dib.bitmap_data;
4021 front->dib.bitmap_data = back->dib.bitmap_data;
4022 back->dib.bitmap_data = tmp;
4024 tmp = front->resource.allocatedMemory;
4025 front->resource.allocatedMemory = back->resource.allocatedMemory;
4026 back->resource.allocatedMemory = tmp;
4028 tmp = front->resource.heap_memory;
4029 front->resource.heap_memory = back->resource.heap_memory;
4030 back->resource.heap_memory = tmp;
4033 /* Flip the PBO */
4035 GLuint tmp_pbo = front->pbo;
4036 front->pbo = back->pbo;
4037 back->pbo = tmp_pbo;
4040 /* Flip the opengl texture */
4042 GLuint tmp;
4044 tmp = back->rb_multisample;
4045 back->rb_multisample = front->rb_multisample;
4046 front->rb_multisample = tmp;
4048 tmp = back->rb_resolved;
4049 back->rb_resolved = front->rb_resolved;
4050 front->rb_resolved = tmp;
4052 resource_unload(&back->resource);
4053 resource_unload(&front->resource);
4057 DWORD tmp_flags = back->flags;
4058 back->flags = front->flags;
4059 front->flags = tmp_flags;
4063 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4064 * pixel copy calls. */
4065 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4066 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4068 struct wined3d_device *device = dst_surface->resource.device;
4069 const struct wined3d_gl_info *gl_info;
4070 float xrel, yrel;
4071 struct wined3d_context *context;
4072 BOOL upsidedown = FALSE;
4073 RECT dst_rect = *dst_rect_in;
4075 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4076 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4078 if(dst_rect.top > dst_rect.bottom) {
4079 UINT tmp = dst_rect.bottom;
4080 dst_rect.bottom = dst_rect.top;
4081 dst_rect.top = tmp;
4082 upsidedown = TRUE;
4085 context = context_acquire(device, src_surface);
4086 gl_info = context->gl_info;
4087 context_apply_blit_state(context, device);
4088 wined3d_texture_load(dst_surface->container, context, FALSE);
4090 /* Bind the target texture */
4091 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
4092 if (surface_is_offscreen(src_surface))
4094 TRACE("Reading from an offscreen target\n");
4095 upsidedown = !upsidedown;
4096 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
4098 else
4100 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
4102 checkGLcall("glReadBuffer");
4104 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4105 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4107 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4109 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4111 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4112 ERR("Texture filtering not supported in direct blit.\n");
4114 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4115 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4117 ERR("Texture filtering not supported in direct blit\n");
4120 if (upsidedown
4121 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4122 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4124 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
4125 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4126 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4127 src_rect->left, src_surface->resource.height - src_rect->bottom,
4128 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4130 else
4132 LONG row;
4133 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4134 /* I have to process this row by row to swap the image,
4135 * otherwise it would be upside down, so stretching in y direction
4136 * doesn't cost extra time
4138 * However, stretching in x direction can be avoided if not necessary
4140 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4141 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4143 /* Well, that stuff works, but it's very slow.
4144 * find a better way instead
4146 LONG col;
4148 for (col = dst_rect.left; col < dst_rect.right; ++col)
4150 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4151 dst_rect.left + col /* x offset */, row /* y offset */,
4152 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4155 else
4157 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4158 dst_rect.left /* x offset */, row /* y offset */,
4159 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4163 checkGLcall("glCopyTexSubImage2D");
4165 context_release(context);
4167 /* The texture is now most up to date - If the surface is a render target
4168 * and has a drawable, this path is never entered. */
4169 surface_validate_location(dst_surface, SFLAG_INTEXTURE);
4170 surface_invalidate_location(dst_surface, ~SFLAG_INTEXTURE);
4173 /* Uses the hardware to stretch and flip the image */
4174 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4175 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4177 struct wined3d_device *device = dst_surface->resource.device;
4178 GLuint src, backup = 0;
4179 float left, right, top, bottom; /* Texture coordinates */
4180 UINT fbwidth = src_surface->resource.width;
4181 UINT fbheight = src_surface->resource.height;
4182 const struct wined3d_gl_info *gl_info;
4183 struct wined3d_context *context;
4184 GLenum drawBuffer = GL_BACK;
4185 GLenum texture_target;
4186 BOOL noBackBufferBackup;
4187 BOOL src_offscreen;
4188 BOOL upsidedown = FALSE;
4189 RECT dst_rect = *dst_rect_in;
4191 TRACE("Using hwstretch blit\n");
4192 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4193 context = context_acquire(device, src_surface);
4194 gl_info = context->gl_info;
4195 context_apply_blit_state(context, device);
4196 wined3d_texture_load(dst_surface->container, context, FALSE);
4198 src_offscreen = surface_is_offscreen(src_surface);
4199 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4200 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
4202 /* Get it a description */
4203 wined3d_texture_load(src_surface->container, context, FALSE);
4206 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4207 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4209 if (context->aux_buffers >= 2)
4211 /* Got more than one aux buffer? Use the 2nd aux buffer */
4212 drawBuffer = GL_AUX1;
4214 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4216 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4217 drawBuffer = GL_AUX0;
4220 if (noBackBufferBackup)
4222 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
4223 checkGLcall("glGenTextures");
4224 context_bind_texture(context, GL_TEXTURE_2D, backup);
4225 texture_target = GL_TEXTURE_2D;
4227 else
4229 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4230 * we are reading from the back buffer, the backup can be used as source texture
4232 texture_target = src_surface->texture_target;
4233 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
4234 gl_info->gl_ops.gl.p_glEnable(texture_target);
4235 checkGLcall("glEnable(texture_target)");
4237 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4238 src_surface->flags &= ~SFLAG_INTEXTURE;
4241 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4242 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4244 if(dst_rect.top > dst_rect.bottom) {
4245 UINT tmp = dst_rect.bottom;
4246 dst_rect.bottom = dst_rect.top;
4247 dst_rect.top = tmp;
4248 upsidedown = TRUE;
4251 if (src_offscreen)
4253 TRACE("Reading from an offscreen target\n");
4254 upsidedown = !upsidedown;
4255 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
4257 else
4259 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
4262 /* TODO: Only back up the part that will be overwritten */
4263 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
4265 checkGLcall("glCopyTexSubImage2D");
4267 /* No issue with overriding these - the sampler is dirty due to blit usage */
4268 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
4269 wined3d_gl_mag_filter(magLookup, filter));
4270 checkGLcall("glTexParameteri");
4271 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
4272 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
4273 checkGLcall("glTexParameteri");
4275 if (!src_surface->swapchain || src_surface == src_surface->swapchain->back_buffers[0])
4277 src = backup ? backup : src_surface->container->texture_rgb.name;
4279 else
4281 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
4282 checkGLcall("glReadBuffer(GL_FRONT)");
4284 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
4285 checkGLcall("glGenTextures(1, &src)");
4286 context_bind_texture(context, GL_TEXTURE_2D, src);
4288 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
4289 * out for power of 2 sizes
4291 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
4292 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
4293 checkGLcall("glTexImage2D");
4294 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
4296 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4297 checkGLcall("glTexParameteri");
4298 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4299 checkGLcall("glTexParameteri");
4301 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
4302 checkGLcall("glReadBuffer(GL_BACK)");
4304 if (texture_target != GL_TEXTURE_2D)
4306 gl_info->gl_ops.gl.p_glDisable(texture_target);
4307 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
4308 texture_target = GL_TEXTURE_2D;
4311 checkGLcall("glEnd and previous");
4313 left = src_rect->left;
4314 right = src_rect->right;
4316 if (!upsidedown)
4318 top = src_surface->resource.height - src_rect->top;
4319 bottom = src_surface->resource.height - src_rect->bottom;
4321 else
4323 top = src_surface->resource.height - src_rect->bottom;
4324 bottom = src_surface->resource.height - src_rect->top;
4327 if (src_surface->flags & SFLAG_NORMCOORD)
4329 left /= src_surface->pow2Width;
4330 right /= src_surface->pow2Width;
4331 top /= src_surface->pow2Height;
4332 bottom /= src_surface->pow2Height;
4335 /* draw the source texture stretched and upside down. The correct surface is bound already */
4336 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
4337 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
4339 context_set_draw_buffer(context, drawBuffer);
4340 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
4342 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
4343 /* bottom left */
4344 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
4345 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
4347 /* top left */
4348 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
4349 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
4351 /* top right */
4352 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
4353 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4355 /* bottom right */
4356 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
4357 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
4358 gl_info->gl_ops.gl.p_glEnd();
4359 checkGLcall("glEnd and previous");
4361 if (texture_target != dst_surface->texture_target)
4363 gl_info->gl_ops.gl.p_glDisable(texture_target);
4364 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
4365 texture_target = dst_surface->texture_target;
4368 /* Now read the stretched and upside down image into the destination texture */
4369 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
4370 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
4372 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
4373 0, 0, /* We blitted the image to the origin */
4374 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4375 checkGLcall("glCopyTexSubImage2D");
4377 if (drawBuffer == GL_BACK)
4379 /* Write the back buffer backup back. */
4380 if (backup)
4382 if (texture_target != GL_TEXTURE_2D)
4384 gl_info->gl_ops.gl.p_glDisable(texture_target);
4385 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
4386 texture_target = GL_TEXTURE_2D;
4388 context_bind_texture(context, GL_TEXTURE_2D, backup);
4390 else
4392 if (texture_target != src_surface->texture_target)
4394 gl_info->gl_ops.gl.p_glDisable(texture_target);
4395 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
4396 texture_target = src_surface->texture_target;
4398 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
4401 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
4402 /* top left */
4403 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
4404 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
4406 /* bottom left */
4407 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
4408 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
4410 /* bottom right */
4411 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
4412 (float)fbheight / (float)src_surface->pow2Height);
4413 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
4415 /* top right */
4416 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
4417 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
4418 gl_info->gl_ops.gl.p_glEnd();
4420 gl_info->gl_ops.gl.p_glDisable(texture_target);
4421 checkGLcall("glDisable(texture_target)");
4423 /* Cleanup */
4424 if (src != src_surface->container->texture_rgb.name && src != backup)
4426 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
4427 checkGLcall("glDeleteTextures(1, &src)");
4429 if (backup)
4431 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
4432 checkGLcall("glDeleteTextures(1, &backup)");
4435 if (wined3d_settings.strict_draw_ordering)
4436 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4438 context_release(context);
4440 /* The texture is now most up to date - If the surface is a render target
4441 * and has a drawable, this path is never entered. */
4442 surface_validate_location(dst_surface, SFLAG_INTEXTURE);
4443 surface_invalidate_location(dst_surface, ~SFLAG_INTEXTURE);
4446 /* Front buffer coordinates are always full screen coordinates, but our GL
4447 * drawable is limited to the window's client area. The sysmem and texture
4448 * copies do have the full screen size. Note that GL has a bottom-left
4449 * origin, while D3D has a top-left origin. */
4450 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
4452 UINT drawable_height;
4454 if (surface->swapchain && surface == surface->swapchain->front_buffer)
4456 POINT offset = {0, 0};
4457 RECT windowsize;
4459 ScreenToClient(window, &offset);
4460 OffsetRect(rect, offset.x, offset.y);
4462 GetClientRect(window, &windowsize);
4463 drawable_height = windowsize.bottom - windowsize.top;
4465 else
4467 drawable_height = surface->resource.height;
4470 rect->top = drawable_height - rect->top;
4471 rect->bottom = drawable_height - rect->bottom;
4474 static void surface_blt_to_drawable(const struct wined3d_device *device,
4475 enum wined3d_texture_filter_type filter, BOOL color_key,
4476 struct wined3d_surface *src_surface, const RECT *src_rect_in,
4477 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
4479 const struct wined3d_gl_info *gl_info;
4480 struct wined3d_context *context;
4481 RECT src_rect, dst_rect;
4483 src_rect = *src_rect_in;
4484 dst_rect = *dst_rect_in;
4486 context = context_acquire(device, dst_surface);
4487 gl_info = context->gl_info;
4489 /* Make sure the surface is up-to-date. This should probably use
4490 * surface_load_location() and worry about the destination surface too,
4491 * unless we're overwriting it completely. */
4492 wined3d_texture_load(src_surface->container, context, FALSE);
4494 /* Activate the destination context, set it up for blitting */
4495 context_apply_blit_state(context, device);
4497 if (!surface_is_offscreen(dst_surface))
4498 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
4500 device->blitter->set_shader(device->blit_priv, context, src_surface);
4502 if (color_key)
4504 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
4505 checkGLcall("glEnable(GL_ALPHA_TEST)");
4507 /* When the primary render target uses P8, the alpha component
4508 * contains the palette index. Which means that the colorkey is one of
4509 * the palette entries. In other cases pixels that should be masked
4510 * away have alpha set to 0. */
4511 if (swapchain_is_p8(context->swapchain))
4512 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
4513 (float)src_surface->src_blt_color_key.color_space_low_value / 256.0f);
4514 else
4515 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
4516 checkGLcall("glAlphaFunc");
4518 else
4520 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4521 checkGLcall("glDisable(GL_ALPHA_TEST)");
4524 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
4526 if (color_key)
4528 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4529 checkGLcall("glDisable(GL_ALPHA_TEST)");
4532 /* Leave the opengl state valid for blitting */
4533 device->blitter->unset_shader(context->gl_info);
4535 if (wined3d_settings.strict_draw_ordering
4536 || (dst_surface->swapchain && dst_surface->swapchain->front_buffer == dst_surface))
4537 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4539 context_release(context);
4542 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
4544 struct wined3d_device *device = s->resource.device;
4545 const struct blit_shader *blitter;
4547 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
4548 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
4549 if (!blitter)
4551 FIXME("No blitter is capable of performing the requested color fill operation.\n");
4552 return WINED3DERR_INVALIDCALL;
4555 return blitter->color_fill(device, s, rect, color);
4558 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4559 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
4560 enum wined3d_texture_filter_type filter)
4562 struct wined3d_device *device = dst_surface->resource.device;
4563 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4564 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
4566 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
4567 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4568 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
4570 /* Get the swapchain. One of the surfaces has to be a primary surface */
4571 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4573 WARN("Destination is in sysmem, rejecting gl blt\n");
4574 return WINED3DERR_INVALIDCALL;
4577 dst_swapchain = dst_surface->swapchain;
4579 if (src_surface)
4581 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4583 WARN("Src is in sysmem, rejecting gl blt\n");
4584 return WINED3DERR_INVALIDCALL;
4587 src_swapchain = src_surface->swapchain;
4589 else
4591 src_swapchain = NULL;
4594 /* Early sort out of cases where no render target is used */
4595 if (!dst_swapchain && !src_swapchain
4596 && src_surface != device->fb.render_targets[0]
4597 && dst_surface != device->fb.render_targets[0])
4599 TRACE("No surface is render target, not using hardware blit.\n");
4600 return WINED3DERR_INVALIDCALL;
4603 /* No destination color keying supported */
4604 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
4606 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
4607 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
4608 return WINED3DERR_INVALIDCALL;
4611 if (dst_swapchain && dst_swapchain == src_swapchain)
4613 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
4614 return WINED3DERR_INVALIDCALL;
4617 if (dst_swapchain && src_swapchain)
4619 FIXME("Implement hardware blit between two different swapchains\n");
4620 return WINED3DERR_INVALIDCALL;
4623 if (dst_swapchain)
4625 /* Handled with regular texture -> swapchain blit */
4626 if (src_surface == device->fb.render_targets[0])
4627 TRACE("Blit from active render target to a swapchain\n");
4629 else if (src_swapchain && dst_surface == device->fb.render_targets[0])
4631 FIXME("Implement blit from a swapchain to the active render target\n");
4632 return WINED3DERR_INVALIDCALL;
4635 if ((src_swapchain || src_surface == device->fb.render_targets[0]) && !dst_swapchain)
4637 /* Blit from render target to texture */
4638 BOOL stretchx;
4640 /* P8 read back is not implemented */
4641 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
4642 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
4644 TRACE("P8 read back not supported by frame buffer to texture blit\n");
4645 return WINED3DERR_INVALIDCALL;
4648 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
4650 TRACE("Color keying not supported by frame buffer to texture blit\n");
4651 return WINED3DERR_INVALIDCALL;
4652 /* Destination color key is checked above */
4655 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
4656 stretchx = TRUE;
4657 else
4658 stretchx = FALSE;
4660 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
4661 * flip the image nor scale it.
4663 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
4664 * -> If the app wants a image width an unscaled width, copy it line per line
4665 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
4666 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
4667 * back buffer. This is slower than reading line per line, thus not used for flipping
4668 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
4669 * pixel by pixel. */
4670 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
4671 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
4673 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
4674 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
4676 else
4678 TRACE("Using hardware stretching to flip / stretch the texture.\n");
4679 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
4682 surface_evict_sysmem(dst_surface);
4684 return WINED3D_OK;
4686 else if (src_surface)
4688 /* Blit from offscreen surface to render target */
4689 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
4690 DWORD oldCKeyFlags = src_surface->CKeyFlags;
4692 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4694 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4695 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
4696 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
4698 FIXME("Unsupported blit operation falling back to software\n");
4699 return WINED3DERR_INVALIDCALL;
4702 /* Color keying: Check if we have to do a color keyed blt,
4703 * and if not check if a color key is activated.
4705 * Just modify the color keying parameters in the surface and restore them afterwards
4706 * The surface keeps track of the color key last used to load the opengl surface.
4707 * PreLoad will catch the change to the flags and color key and reload if necessary.
4709 if (flags & WINEDDBLT_KEYSRC)
4711 /* Use color key from surface */
4713 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4715 /* Use color key from DDBltFx */
4716 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
4717 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
4719 else
4721 /* Do not use color key */
4722 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
4725 surface_blt_to_drawable(device, filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
4726 src_surface, src_rect, dst_surface, dst_rect);
4728 /* Restore the color key parameters */
4729 src_surface->CKeyFlags = oldCKeyFlags;
4730 src_surface->src_blt_color_key = old_blt_key;
4732 surface_validate_location(dst_surface, dst_surface->draw_binding);
4733 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
4735 return WINED3D_OK;
4738 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
4739 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
4740 return WINED3DERR_INVALIDCALL;
4743 /* Context activation is done by the caller. */
4744 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
4745 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
4747 struct wined3d_device *device = surface->resource.device;
4748 const struct wined3d_gl_info *gl_info = context->gl_info;
4749 GLint compare_mode = GL_NONE;
4750 struct blt_info info;
4751 GLint old_binding = 0;
4752 RECT rect;
4754 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4756 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
4757 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
4758 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4759 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
4760 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
4761 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
4762 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
4763 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
4764 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4765 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
4766 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
4768 SetRect(&rect, 0, h, w, 0);
4769 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
4770 context_active_texture(context, context->gl_info, 0);
4771 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
4772 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
4773 if (gl_info->supported[ARB_SHADOW])
4775 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
4776 if (compare_mode != GL_NONE)
4777 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
4780 device->shader_backend->shader_select_depth_blt(device->shader_priv,
4781 gl_info, info.tex_type, &surface->ds_current_size);
4783 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
4784 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
4785 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
4786 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
4787 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
4788 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
4789 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
4790 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
4791 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
4792 gl_info->gl_ops.gl.p_glEnd();
4794 if (compare_mode != GL_NONE)
4795 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
4796 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
4798 gl_info->gl_ops.gl.p_glPopAttrib();
4800 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
4803 void surface_modify_ds_location(struct wined3d_surface *surface,
4804 DWORD location, UINT w, UINT h)
4806 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
4808 if (location & ~(SFLAG_LOCATIONS | SFLAG_DISCARDED))
4809 FIXME("Invalid location (%#x) specified.\n", location);
4811 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
4812 || (!(surface->flags & SFLAG_INTEXTURE) && (location & SFLAG_INTEXTURE)))
4813 wined3d_texture_set_dirty(surface->container);
4815 surface->ds_current_size.cx = w;
4816 surface->ds_current_size.cy = h;
4817 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_DISCARDED);
4818 surface->flags |= location;
4821 /* Context activation is done by the caller. */
4822 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4824 const struct wined3d_gl_info *gl_info = context->gl_info;
4825 struct wined3d_device *device = surface->resource.device;
4826 GLsizei w, h;
4828 TRACE("surface %p, new location %#x.\n", surface, location);
4830 /* TODO: Make this work for modes other than FBO */
4831 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4833 if (!(surface->flags & location))
4835 w = surface->ds_current_size.cx;
4836 h = surface->ds_current_size.cy;
4837 surface->ds_current_size.cx = 0;
4838 surface->ds_current_size.cy = 0;
4840 else
4842 w = surface->resource.width;
4843 h = surface->resource.height;
4846 if (surface->ds_current_size.cx == surface->resource.width
4847 && surface->ds_current_size.cy == surface->resource.height)
4849 TRACE("Location (%#x) is already up to date.\n", location);
4850 return;
4853 if (surface->current_renderbuffer)
4855 FIXME("Not supported with fixed up depth stencil.\n");
4856 return;
4859 if (surface->flags & SFLAG_DISCARDED)
4861 TRACE("Surface was discarded, no need copy data.\n");
4862 switch (location)
4864 case SFLAG_INTEXTURE:
4865 surface_prepare_texture(surface, context, FALSE);
4866 break;
4867 case SFLAG_INRB_MULTISAMPLE:
4868 surface_prepare_rb(surface, gl_info, TRUE);
4869 break;
4870 case SFLAG_INDRAWABLE:
4871 /* Nothing to do */
4872 break;
4873 default:
4874 FIXME("Unhandled location %#x\n", location);
4876 surface->flags &= ~SFLAG_DISCARDED;
4877 surface->flags |= location;
4878 surface->ds_current_size.cx = surface->resource.width;
4879 surface->ds_current_size.cy = surface->resource.height;
4880 return;
4883 if (!(surface->flags & SFLAG_LOCATIONS))
4885 FIXME("No up to date depth stencil location.\n");
4886 surface->flags |= location;
4887 surface->ds_current_size.cx = surface->resource.width;
4888 surface->ds_current_size.cy = surface->resource.height;
4889 return;
4892 if (location == SFLAG_INTEXTURE)
4894 GLint old_binding = 0;
4895 GLenum bind_target;
4897 /* The render target is allowed to be smaller than the depth/stencil
4898 * buffer, so the onscreen depth/stencil buffer is potentially smaller
4899 * than the offscreen surface. Don't overwrite the offscreen surface
4900 * with undefined data. */
4901 w = min(w, context->swapchain->desc.backbuffer_width);
4902 h = min(h, context->swapchain->desc.backbuffer_height);
4904 TRACE("Copying onscreen depth buffer to depth texture.\n");
4906 if (!device->depth_blt_texture)
4907 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
4909 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4910 * directly on the FBO texture. That's because we need to flip. */
4911 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4912 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
4913 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4915 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4916 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4918 else
4920 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4921 bind_target = GL_TEXTURE_2D;
4923 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
4924 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
4925 * internal format, because the internal format might include stencil
4926 * data. In principle we should copy stencil data as well, but unless
4927 * the driver supports stencil export it's hard to do, and doesn't
4928 * seem to be needed in practice. If the hardware doesn't support
4929 * writing stencil data, the glCopyTexImage2D() call might trigger
4930 * software fallbacks. */
4931 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
4932 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4933 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4934 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4935 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4936 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4937 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4938 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
4940 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4941 NULL, surface, SFLAG_INTEXTURE);
4942 context_set_draw_buffer(context, GL_NONE);
4944 /* Do the actual blit */
4945 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
4946 checkGLcall("depth_blt");
4948 context_invalidate_state(context, STATE_FRAMEBUFFER);
4950 if (wined3d_settings.strict_draw_ordering)
4951 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4953 else if (location == SFLAG_INDRAWABLE)
4955 TRACE("Copying depth texture to onscreen depth buffer.\n");
4957 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4958 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
4959 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
4960 0, surface->pow2Height - h, w, h, surface->texture_target);
4961 checkGLcall("depth_blt");
4963 context_invalidate_state(context, STATE_FRAMEBUFFER);
4965 if (wined3d_settings.strict_draw_ordering)
4966 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4968 else
4970 ERR("Invalid location (%#x) specified.\n", location);
4973 surface->flags |= location;
4974 surface->ds_current_size.cx = surface->resource.width;
4975 surface->ds_current_size.cy = surface->resource.height;
4978 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
4980 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location & SFLAG_LOCATIONS));
4982 surface->flags |= (location & SFLAG_LOCATIONS);
4985 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
4987 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location & SFLAG_LOCATIONS));
4989 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
4990 wined3d_texture_set_dirty(surface->container);
4991 surface->flags &= ~(location & SFLAG_LOCATIONS);
4993 if (!(surface->flags & SFLAG_LOCATIONS))
4994 ERR("Surface %p does not have any up to date location.\n", surface);
4997 static DWORD resource_access_from_location(DWORD location)
4999 switch (location)
5001 case SFLAG_INSYSMEM:
5002 return WINED3D_RESOURCE_ACCESS_CPU;
5004 case SFLAG_INDRAWABLE:
5005 case SFLAG_INSRGBTEX:
5006 case SFLAG_INTEXTURE:
5007 case SFLAG_INRB_MULTISAMPLE:
5008 case SFLAG_INRB_RESOLVED:
5009 return WINED3D_RESOURCE_ACCESS_GPU;
5011 default:
5012 FIXME("Unhandled location %#x.\n", location);
5013 return 0;
5017 static void surface_load_sysmem(struct wined3d_surface *surface,
5018 const struct wined3d_gl_info *gl_info)
5020 surface_prepare_system_memory(surface);
5022 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5023 surface_load_location(surface, SFLAG_INTEXTURE);
5025 /* Download the surface to system memory. */
5026 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5028 struct wined3d_device *device = surface->resource.device;
5029 struct wined3d_context *context;
5031 /* TODO: Use already acquired context when possible. */
5032 context = context_acquire(device, NULL);
5034 wined3d_texture_bind_and_dirtify(surface->container, context, !(surface->flags & SFLAG_INTEXTURE));
5035 surface_download_data(surface, gl_info);
5037 context_release(context);
5039 return;
5042 if (surface->flags & SFLAG_INDRAWABLE)
5044 read_from_framebuffer(surface, surface->resource.allocatedMemory,
5045 wined3d_surface_get_pitch(surface));
5046 return;
5049 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5050 surface, surface->flags & SFLAG_LOCATIONS);
5053 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5054 const struct wined3d_gl_info *gl_info)
5056 RECT r;
5058 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5060 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5061 return WINED3DERR_INVALIDCALL;
5064 surface_get_rect(surface, NULL, &r);
5065 surface_load_location(surface, SFLAG_INTEXTURE);
5066 surface_blt_to_drawable(surface->resource.device,
5067 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
5069 return WINED3D_OK;
5072 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5073 const struct wined3d_gl_info *gl_info, BOOL srgb)
5075 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5076 struct wined3d_device *device = surface->resource.device;
5077 enum wined3d_conversion_type convert;
5078 struct wined3d_context *context;
5079 UINT width, src_pitch, dst_pitch;
5080 struct wined3d_bo_address data;
5081 struct wined3d_format format;
5082 POINT dst_point = {0, 0};
5083 BYTE *mem;
5085 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
5086 && surface_is_offscreen(surface)
5087 && (surface->flags & SFLAG_INDRAWABLE))
5089 surface_load_fb_texture(surface, srgb);
5091 return WINED3D_OK;
5094 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
5095 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
5096 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5097 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5098 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5100 if (srgb)
5101 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INTEXTURE,
5102 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
5103 else
5104 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INSRGBTEX,
5105 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
5107 return WINED3D_OK;
5110 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
5111 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
5112 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5113 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5114 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5116 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
5117 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
5118 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5120 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
5121 &rect, surface, dst_location, &rect);
5123 return WINED3D_OK;
5126 /* Upload from system memory */
5128 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
5129 TRUE /* We will use textures */, &format, &convert);
5131 if (srgb)
5133 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
5135 /* Performance warning... */
5136 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
5137 surface_load_location(surface, SFLAG_INSYSMEM);
5140 else
5142 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
5144 /* Performance warning... */
5145 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
5146 surface_load_location(surface, SFLAG_INSYSMEM);
5150 if (!(surface->flags & SFLAG_INSYSMEM))
5152 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
5153 /* Lets hope we get it from somewhere... */
5154 surface_load_location(surface, SFLAG_INSYSMEM);
5157 /* TODO: Use already acquired context when possible. */
5158 context = context_acquire(device, NULL);
5160 surface_prepare_texture(surface, context, srgb);
5161 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
5163 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
5165 surface->flags |= SFLAG_GLCKEY;
5166 surface->gl_color_key = surface->src_blt_color_key;
5168 else surface->flags &= ~SFLAG_GLCKEY;
5170 width = surface->resource.width;
5171 src_pitch = wined3d_surface_get_pitch(surface);
5173 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5174 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
5175 * called. */
5176 if ((convert != WINED3D_CT_NONE || format.convert) && (surface->flags & SFLAG_PBO))
5178 TRACE("Removing the pbo attached to surface %p.\n", surface);
5179 surface_remove_pbo(surface, gl_info);
5182 if (format.convert)
5184 /* This code is entered for texture formats which need a fixup. */
5185 UINT height = surface->resource.height;
5187 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
5188 dst_pitch = width * format.conv_byte_count;
5189 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5191 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5193 ERR("Out of memory (%u).\n", dst_pitch * height);
5194 context_release(context);
5195 return E_OUTOFMEMORY;
5197 format.convert(surface->resource.allocatedMemory, mem, src_pitch, src_pitch * height,
5198 dst_pitch, dst_pitch * height, width, height, 1);
5199 format.byte_count = format.conv_byte_count;
5200 src_pitch = dst_pitch;
5202 else if (convert != WINED3D_CT_NONE && surface->resource.allocatedMemory)
5204 /* This code is only entered for color keying fixups */
5205 UINT height = surface->resource.height;
5207 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
5208 dst_pitch = width * format.conv_byte_count;
5209 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5211 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5213 ERR("Out of memory (%u).\n", dst_pitch * height);
5214 context_release(context);
5215 return E_OUTOFMEMORY;
5217 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
5218 width, height, dst_pitch, convert, surface);
5219 format.byte_count = format.conv_byte_count;
5220 src_pitch = dst_pitch;
5222 else
5224 mem = surface->resource.allocatedMemory;
5227 data.buffer_object = surface->pbo;
5228 data.addr = mem;
5229 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
5231 context_release(context);
5233 /* Don't delete PBO memory. */
5234 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5235 HeapFree(GetProcessHeap(), 0, mem);
5237 return WINED3D_OK;
5240 static void surface_multisample_resolve(struct wined3d_surface *surface)
5242 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5244 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
5245 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
5247 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
5248 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
5251 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
5253 struct wined3d_device *device = surface->resource.device;
5254 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5255 HRESULT hr;
5257 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location));
5259 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5261 if (location == SFLAG_INTEXTURE && surface->flags & SFLAG_INDRAWABLE)
5263 struct wined3d_context *context = context_acquire(device, NULL);
5264 surface_load_ds_location(surface, context, location);
5265 context_release(context);
5266 return WINED3D_OK;
5268 else if (location & surface->flags && surface->draw_binding != SFLAG_INDRAWABLE)
5270 /* Already up to date, nothing to do. */
5271 return WINED3D_OK;
5273 else
5275 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
5276 debug_surflocation(surface->flags & SFLAG_LOCATIONS), debug_surflocation(location));
5277 return WINED3DERR_INVALIDCALL;
5281 if (surface->flags & location)
5283 TRACE("Location already up to date.\n");
5285 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
5286 && surface_need_pbo(surface, gl_info))
5287 surface_load_pbo(surface, gl_info);
5289 return WINED3D_OK;
5292 if (WARN_ON(d3d_surface))
5294 DWORD required_access = resource_access_from_location(location);
5295 if ((surface->resource.access_flags & required_access) != required_access)
5296 WARN("Operation requires %#x access, but surface only has %#x.\n",
5297 required_access, surface->resource.access_flags);
5300 if (!(surface->flags & SFLAG_LOCATIONS))
5302 ERR("Surface %p does not have any up to date location.\n", surface);
5303 surface->flags |= SFLAG_LOST;
5304 return WINED3DERR_DEVICELOST;
5307 switch (location)
5309 case SFLAG_INSYSMEM:
5310 surface_load_sysmem(surface, gl_info);
5311 break;
5313 case SFLAG_INDRAWABLE:
5314 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
5315 return hr;
5316 break;
5318 case SFLAG_INRB_RESOLVED:
5319 surface_multisample_resolve(surface);
5320 break;
5322 case SFLAG_INTEXTURE:
5323 case SFLAG_INSRGBTEX:
5324 if (FAILED(hr = surface_load_texture(surface, gl_info, location == SFLAG_INSRGBTEX)))
5325 return hr;
5326 break;
5328 default:
5329 ERR("Don't know how to handle location %#x.\n", location);
5330 break;
5333 surface->flags |= location;
5335 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
5336 surface_evict_sysmem(surface);
5338 return WINED3D_OK;
5341 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
5343 struct wined3d_swapchain *swapchain;
5345 /* Not on a swapchain - must be offscreen */
5346 if (!(swapchain = surface->swapchain))
5347 return TRUE;
5349 /* The front buffer is always onscreen */
5350 if (surface == swapchain->front_buffer) return FALSE;
5352 /* If the swapchain is rendered to an FBO, the backbuffer is
5353 * offscreen, otherwise onscreen */
5354 return swapchain->render_to_fbo;
5357 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
5358 /* Context activation is done by the caller. */
5359 static void ffp_blit_free(struct wined3d_device *device) { }
5361 /* Context activation is done by the caller. */
5362 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5364 const struct wined3d_gl_info *gl_info = context->gl_info;
5366 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
5367 checkGLcall("glEnable(target)");
5369 return WINED3D_OK;
5372 /* Context activation is done by the caller. */
5373 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
5375 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
5376 checkGLcall("glDisable(GL_TEXTURE_2D)");
5377 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
5379 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5380 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5382 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
5384 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
5385 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5389 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5390 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5391 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5393 switch (blit_op)
5395 case WINED3D_BLIT_OP_COLOR_BLIT:
5396 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
5397 return FALSE;
5399 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5401 TRACE("Checking support for fixup:\n");
5402 dump_color_fixup_desc(src_format->color_fixup);
5405 /* We only support identity conversions. */
5406 if (!is_identity_fixup(src_format->color_fixup)
5407 || !is_identity_fixup(dst_format->color_fixup))
5409 TRACE("Fixups are not supported.\n");
5410 return FALSE;
5413 return TRUE;
5415 case WINED3D_BLIT_OP_COLOR_FILL:
5416 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
5417 return FALSE;
5419 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5421 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
5422 return FALSE;
5424 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
5426 TRACE("Color fill not supported\n");
5427 return FALSE;
5430 /* FIXME: We should reject color fills on formats with fixups,
5431 * but this would break P8 color fills for example. */
5433 return TRUE;
5435 case WINED3D_BLIT_OP_DEPTH_FILL:
5436 return TRUE;
5438 default:
5439 TRACE("Unsupported blit_op=%d\n", blit_op);
5440 return FALSE;
5444 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5445 const RECT *dst_rect, const struct wined3d_color *color)
5447 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
5448 struct wined3d_fb_state fb = {&dst_surface, NULL};
5450 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
5452 return WINED3D_OK;
5455 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
5456 struct wined3d_surface *surface, const RECT *rect, float depth)
5458 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
5459 struct wined3d_fb_state fb = {NULL, surface};
5461 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
5463 return WINED3D_OK;
5466 const struct blit_shader ffp_blit = {
5467 ffp_blit_alloc,
5468 ffp_blit_free,
5469 ffp_blit_set,
5470 ffp_blit_unset,
5471 ffp_blit_supported,
5472 ffp_blit_color_fill,
5473 ffp_blit_depth_fill,
5476 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
5478 return WINED3D_OK;
5481 /* Context activation is done by the caller. */
5482 static void cpu_blit_free(struct wined3d_device *device)
5486 /* Context activation is done by the caller. */
5487 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5489 return WINED3D_OK;
5492 /* Context activation is done by the caller. */
5493 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
5497 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5498 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5499 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5501 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
5503 return TRUE;
5506 return FALSE;
5509 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
5510 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
5511 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
5513 UINT row_block_count;
5514 const BYTE *src_row;
5515 BYTE *dst_row;
5516 UINT x, y;
5518 src_row = src_data;
5519 dst_row = dst_data;
5521 row_block_count = (update_w + format->block_width - 1) / format->block_width;
5523 if (!flags)
5525 for (y = 0; y < update_h; y += format->block_height)
5527 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
5528 src_row += src_pitch;
5529 dst_row += dst_pitch;
5532 return WINED3D_OK;
5535 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
5537 src_row += (((update_h / format->block_height) - 1) * src_pitch);
5539 switch (format->id)
5541 case WINED3DFMT_DXT1:
5542 for (y = 0; y < update_h; y += format->block_height)
5544 struct block
5546 WORD color[2];
5547 BYTE control_row[4];
5550 const struct block *s = (const struct block *)src_row;
5551 struct block *d = (struct block *)dst_row;
5553 for (x = 0; x < row_block_count; ++x)
5555 d[x].color[0] = s[x].color[0];
5556 d[x].color[1] = s[x].color[1];
5557 d[x].control_row[0] = s[x].control_row[3];
5558 d[x].control_row[1] = s[x].control_row[2];
5559 d[x].control_row[2] = s[x].control_row[1];
5560 d[x].control_row[3] = s[x].control_row[0];
5562 src_row -= src_pitch;
5563 dst_row += dst_pitch;
5565 return WINED3D_OK;
5567 case WINED3DFMT_DXT3:
5568 for (y = 0; y < update_h; y += format->block_height)
5570 struct block
5572 WORD alpha_row[4];
5573 WORD color[2];
5574 BYTE control_row[4];
5577 const struct block *s = (const struct block *)src_row;
5578 struct block *d = (struct block *)dst_row;
5580 for (x = 0; x < row_block_count; ++x)
5582 d[x].alpha_row[0] = s[x].alpha_row[3];
5583 d[x].alpha_row[1] = s[x].alpha_row[2];
5584 d[x].alpha_row[2] = s[x].alpha_row[1];
5585 d[x].alpha_row[3] = s[x].alpha_row[0];
5586 d[x].color[0] = s[x].color[0];
5587 d[x].color[1] = s[x].color[1];
5588 d[x].control_row[0] = s[x].control_row[3];
5589 d[x].control_row[1] = s[x].control_row[2];
5590 d[x].control_row[2] = s[x].control_row[1];
5591 d[x].control_row[3] = s[x].control_row[0];
5593 src_row -= src_pitch;
5594 dst_row += dst_pitch;
5596 return WINED3D_OK;
5598 default:
5599 FIXME("Compressed flip not implemented for format %s.\n",
5600 debug_d3dformat(format->id));
5601 return E_NOTIMPL;
5605 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
5606 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
5608 return E_NOTIMPL;
5611 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5612 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
5613 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5615 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
5616 const struct wined3d_format *src_format, *dst_format;
5617 struct wined3d_texture *src_texture = NULL;
5618 struct wined3d_map_desc dst_map, src_map;
5619 const BYTE *sbase = NULL;
5620 HRESULT hr = WINED3D_OK;
5621 const BYTE *sbuf;
5622 BYTE *dbuf;
5623 int x, y;
5625 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5626 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5627 flags, fx, debug_d3dtexturefiltertype(filter));
5629 if (src_surface == dst_surface)
5631 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
5632 src_map = dst_map;
5633 src_format = dst_surface->resource.format;
5634 dst_format = src_format;
5636 else
5638 dst_format = dst_surface->resource.format;
5639 if (src_surface)
5641 if (dst_surface->resource.format->id != src_surface->resource.format->id)
5643 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
5645 /* The conv function writes a FIXME */
5646 WARN("Cannot convert source surface format to dest format.\n");
5647 goto release;
5649 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
5651 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
5652 src_format = src_surface->resource.format;
5654 else
5656 src_format = dst_format;
5659 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
5662 bpp = dst_surface->resource.format->byte_count;
5663 srcheight = src_rect->bottom - src_rect->top;
5664 srcwidth = src_rect->right - src_rect->left;
5665 dstheight = dst_rect->bottom - dst_rect->top;
5666 dstwidth = dst_rect->right - dst_rect->left;
5667 width = (dst_rect->right - dst_rect->left) * bpp;
5669 if (src_surface)
5670 sbase = (BYTE *)src_map.data
5671 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
5672 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
5673 if (src_surface != dst_surface)
5674 dbuf = dst_map.data;
5675 else
5676 dbuf = (BYTE *)dst_map.data
5677 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
5678 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
5680 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
5682 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
5684 if (src_surface == dst_surface)
5686 FIXME("Only plain blits supported on compressed surfaces.\n");
5687 hr = E_NOTIMPL;
5688 goto release;
5691 if (srcheight != dstheight || srcwidth != dstwidth)
5693 WARN("Stretching not supported on compressed surfaces.\n");
5694 hr = WINED3DERR_INVALIDCALL;
5695 goto release;
5698 if (!surface_check_block_align(src_surface, src_rect))
5700 WARN("Source rectangle not block-aligned.\n");
5701 hr = WINED3DERR_INVALIDCALL;
5702 goto release;
5705 if (!surface_check_block_align(dst_surface, dst_rect))
5707 WARN("Destination rectangle not block-aligned.\n");
5708 hr = WINED3DERR_INVALIDCALL;
5709 goto release;
5712 hr = surface_cpu_blt_compressed(sbase, dbuf,
5713 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
5714 src_format, flags, fx);
5715 goto release;
5718 /* First, all the 'source-less' blits */
5719 if (flags & WINEDDBLT_COLORFILL)
5721 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
5722 flags &= ~WINEDDBLT_COLORFILL;
5725 if (flags & WINEDDBLT_DEPTHFILL)
5727 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
5729 if (flags & WINEDDBLT_ROP)
5731 /* Catch some degenerate cases here. */
5732 switch (fx->dwROP)
5734 case BLACKNESS:
5735 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
5736 break;
5737 case 0xaa0029: /* No-op */
5738 break;
5739 case WHITENESS:
5740 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
5741 break;
5742 case SRCCOPY: /* Well, we do that below? */
5743 break;
5744 default:
5745 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
5746 goto error;
5748 flags &= ~WINEDDBLT_ROP;
5750 if (flags & WINEDDBLT_DDROPS)
5752 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
5754 /* Now the 'with source' blits. */
5755 if (src_surface)
5757 int sx, xinc, sy, yinc;
5759 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
5760 goto release;
5762 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
5763 && (srcwidth != dstwidth || srcheight != dstheight))
5765 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
5766 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
5769 xinc = (srcwidth << 16) / dstwidth;
5770 yinc = (srcheight << 16) / dstheight;
5772 if (!flags)
5774 /* No effects, we can cheat here. */
5775 if (dstwidth == srcwidth)
5777 if (dstheight == srcheight)
5779 /* No stretching in either direction. This needs to be as
5780 * fast as possible. */
5781 sbuf = sbase;
5783 /* Check for overlapping surfaces. */
5784 if (src_surface != dst_surface || dst_rect->top < src_rect->top
5785 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
5787 /* No overlap, or dst above src, so copy from top downwards. */
5788 for (y = 0; y < dstheight; ++y)
5790 memcpy(dbuf, sbuf, width);
5791 sbuf += src_map.row_pitch;
5792 dbuf += dst_map.row_pitch;
5795 else if (dst_rect->top > src_rect->top)
5797 /* Copy from bottom upwards. */
5798 sbuf += src_map.row_pitch * dstheight;
5799 dbuf += dst_map.row_pitch * dstheight;
5800 for (y = 0; y < dstheight; ++y)
5802 sbuf -= src_map.row_pitch;
5803 dbuf -= dst_map.row_pitch;
5804 memcpy(dbuf, sbuf, width);
5807 else
5809 /* Src and dst overlapping on the same line, use memmove. */
5810 for (y = 0; y < dstheight; ++y)
5812 memmove(dbuf, sbuf, width);
5813 sbuf += src_map.row_pitch;
5814 dbuf += dst_map.row_pitch;
5818 else
5820 /* Stretching in y direction only. */
5821 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5823 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5824 memcpy(dbuf, sbuf, width);
5825 dbuf += dst_map.row_pitch;
5829 else
5831 /* Stretching in X direction. */
5832 int last_sy = -1;
5833 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5835 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5837 if ((sy >> 16) == (last_sy >> 16))
5839 /* This source row is the same as last source row -
5840 * Copy the already stretched row. */
5841 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
5843 else
5845 #define STRETCH_ROW(type) \
5846 do { \
5847 const type *s = (const type *)sbuf; \
5848 type *d = (type *)dbuf; \
5849 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5850 d[x] = s[sx >> 16]; \
5851 } while(0)
5853 switch(bpp)
5855 case 1:
5856 STRETCH_ROW(BYTE);
5857 break;
5858 case 2:
5859 STRETCH_ROW(WORD);
5860 break;
5861 case 4:
5862 STRETCH_ROW(DWORD);
5863 break;
5864 case 3:
5866 const BYTE *s;
5867 BYTE *d = dbuf;
5868 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
5870 DWORD pixel;
5872 s = sbuf + 3 * (sx >> 16);
5873 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5874 d[0] = (pixel ) & 0xff;
5875 d[1] = (pixel >> 8) & 0xff;
5876 d[2] = (pixel >> 16) & 0xff;
5877 d += 3;
5879 break;
5881 default:
5882 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
5883 hr = WINED3DERR_NOTAVAILABLE;
5884 goto error;
5886 #undef STRETCH_ROW
5888 dbuf += dst_map.row_pitch;
5889 last_sy = sy;
5893 else
5895 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
5896 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
5897 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
5898 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
5900 /* The color keying flags are checked for correctness in ddraw */
5901 if (flags & WINEDDBLT_KEYSRC)
5903 keylow = src_surface->src_blt_color_key.color_space_low_value;
5904 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
5906 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5908 keylow = fx->ddckSrcColorkey.color_space_low_value;
5909 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
5912 if (flags & WINEDDBLT_KEYDEST)
5914 /* Destination color keys are taken from the source surface! */
5915 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
5916 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
5918 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
5920 destkeylow = fx->ddckDestColorkey.color_space_low_value;
5921 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
5924 if (bpp == 1)
5926 keymask = 0xff;
5928 else
5930 DWORD masks[3];
5931 get_color_masks(src_format, masks);
5932 keymask = masks[0]
5933 | masks[1]
5934 | masks[2];
5936 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
5939 if (flags & WINEDDBLT_DDFX)
5941 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
5942 LONG tmpxy;
5943 dTopLeft = dbuf;
5944 dTopRight = dbuf + ((dstwidth - 1) * bpp);
5945 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
5946 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
5948 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
5950 /* I don't think we need to do anything about this flag */
5951 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
5953 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
5955 tmp = dTopRight;
5956 dTopRight = dTopLeft;
5957 dTopLeft = tmp;
5958 tmp = dBottomRight;
5959 dBottomRight = dBottomLeft;
5960 dBottomLeft = tmp;
5961 dstxinc = dstxinc * -1;
5963 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
5965 tmp = dTopLeft;
5966 dTopLeft = dBottomLeft;
5967 dBottomLeft = tmp;
5968 tmp = dTopRight;
5969 dTopRight = dBottomRight;
5970 dBottomRight = tmp;
5971 dstyinc = dstyinc * -1;
5973 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
5975 /* I don't think we need to do anything about this flag */
5976 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
5978 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
5980 tmp = dBottomRight;
5981 dBottomRight = dTopLeft;
5982 dTopLeft = tmp;
5983 tmp = dBottomLeft;
5984 dBottomLeft = dTopRight;
5985 dTopRight = tmp;
5986 dstxinc = dstxinc * -1;
5987 dstyinc = dstyinc * -1;
5989 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
5991 tmp = dTopLeft;
5992 dTopLeft = dBottomLeft;
5993 dBottomLeft = dBottomRight;
5994 dBottomRight = dTopRight;
5995 dTopRight = tmp;
5996 tmpxy = dstxinc;
5997 dstxinc = dstyinc;
5998 dstyinc = tmpxy;
5999 dstxinc = dstxinc * -1;
6001 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6003 tmp = dTopLeft;
6004 dTopLeft = dTopRight;
6005 dTopRight = dBottomRight;
6006 dBottomRight = dBottomLeft;
6007 dBottomLeft = tmp;
6008 tmpxy = dstxinc;
6009 dstxinc = dstyinc;
6010 dstyinc = tmpxy;
6011 dstyinc = dstyinc * -1;
6013 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6015 /* I don't think we need to do anything about this flag */
6016 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6018 dbuf = dTopLeft;
6019 flags &= ~(WINEDDBLT_DDFX);
6022 #define COPY_COLORKEY_FX(type) \
6023 do { \
6024 const type *s; \
6025 type *d = (type *)dbuf, *dx, tmp; \
6026 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6028 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
6029 dx = d; \
6030 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6032 tmp = s[sx >> 16]; \
6033 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6034 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6036 dx[0] = tmp; \
6038 dx = (type *)(((BYTE *)dx) + dstxinc); \
6040 d = (type *)(((BYTE *)d) + dstyinc); \
6042 } while(0)
6044 switch (bpp)
6046 case 1:
6047 COPY_COLORKEY_FX(BYTE);
6048 break;
6049 case 2:
6050 COPY_COLORKEY_FX(WORD);
6051 break;
6052 case 4:
6053 COPY_COLORKEY_FX(DWORD);
6054 break;
6055 case 3:
6057 const BYTE *s;
6058 BYTE *d = dbuf, *dx;
6059 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6061 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6062 dx = d;
6063 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
6065 DWORD pixel, dpixel = 0;
6066 s = sbuf + 3 * (sx>>16);
6067 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6068 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
6069 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
6070 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
6072 dx[0] = (pixel ) & 0xff;
6073 dx[1] = (pixel >> 8) & 0xff;
6074 dx[2] = (pixel >> 16) & 0xff;
6076 dx += dstxinc;
6078 d += dstyinc;
6080 break;
6082 default:
6083 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
6084 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
6085 hr = WINED3DERR_NOTAVAILABLE;
6086 goto error;
6087 #undef COPY_COLORKEY_FX
6092 error:
6093 if (flags && FIXME_ON(d3d_surface))
6095 FIXME("\tUnsupported flags: %#x.\n", flags);
6098 release:
6099 wined3d_surface_unmap(dst_surface);
6100 if (src_surface && src_surface != dst_surface)
6101 wined3d_surface_unmap(src_surface);
6102 /* Release the converted surface, if any. */
6103 if (src_texture)
6104 wined3d_texture_decref(src_texture);
6106 return hr;
6109 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6110 const RECT *dst_rect, const struct wined3d_color *color)
6112 static const RECT src_rect;
6113 WINEDDBLTFX BltFx;
6115 memset(&BltFx, 0, sizeof(BltFx));
6116 BltFx.dwSize = sizeof(BltFx);
6117 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
6118 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
6119 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
6122 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
6123 struct wined3d_surface *surface, const RECT *rect, float depth)
6125 FIXME("Depth filling not implemented by cpu_blit.\n");
6126 return WINED3DERR_INVALIDCALL;
6129 const struct blit_shader cpu_blit = {
6130 cpu_blit_alloc,
6131 cpu_blit_free,
6132 cpu_blit_set,
6133 cpu_blit_unset,
6134 cpu_blit_supported,
6135 cpu_blit_color_fill,
6136 cpu_blit_depth_fill,
6139 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
6140 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
6141 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
6143 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
6144 struct wined3d_device *device = dst_surface->resource.device;
6145 DWORD src_ds_flags, dst_ds_flags;
6146 RECT src_rect, dst_rect;
6147 BOOL scale, convert;
6149 static const DWORD simple_blit = WINEDDBLT_ASYNC
6150 | WINEDDBLT_COLORFILL
6151 | WINEDDBLT_WAIT
6152 | WINEDDBLT_DEPTHFILL
6153 | WINEDDBLT_DONOTWAIT;
6155 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6156 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
6157 flags, fx, debug_d3dtexturefiltertype(filter));
6158 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
6160 if (fx)
6162 TRACE("dwSize %#x.\n", fx->dwSize);
6163 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
6164 TRACE("dwROP %#x.\n", fx->dwROP);
6165 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
6166 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
6167 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
6168 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
6169 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
6170 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
6171 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
6172 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
6173 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
6174 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
6175 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
6176 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
6177 TRACE("dwReserved %#x.\n", fx->dwReserved);
6178 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
6179 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
6180 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
6181 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
6182 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
6183 TRACE("ddckDestColorkey {%#x, %#x}.\n",
6184 fx->ddckDestColorkey.color_space_low_value,
6185 fx->ddckDestColorkey.color_space_high_value);
6186 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
6187 fx->ddckSrcColorkey.color_space_low_value,
6188 fx->ddckSrcColorkey.color_space_high_value);
6191 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
6193 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
6194 return WINEDDERR_SURFACEBUSY;
6197 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
6199 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
6200 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
6201 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
6202 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
6203 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
6205 WARN("The application gave us a bad destination rectangle.\n");
6206 return WINEDDERR_INVALIDRECT;
6209 if (src_surface)
6211 surface_get_rect(src_surface, src_rect_in, &src_rect);
6213 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
6214 || src_rect.left > src_surface->resource.width || src_rect.left < 0
6215 || src_rect.top > src_surface->resource.height || src_rect.top < 0
6216 || src_rect.right > src_surface->resource.width || src_rect.right < 0
6217 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
6219 WARN("Application gave us bad source rectangle for Blt.\n");
6220 return WINEDDERR_INVALIDRECT;
6223 else
6225 memset(&src_rect, 0, sizeof(src_rect));
6228 if (!fx || !(fx->dwDDFX))
6229 flags &= ~WINEDDBLT_DDFX;
6231 if (flags & WINEDDBLT_WAIT)
6232 flags &= ~WINEDDBLT_WAIT;
6234 if (flags & WINEDDBLT_ASYNC)
6236 static unsigned int once;
6238 if (!once++)
6239 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
6240 flags &= ~WINEDDBLT_ASYNC;
6243 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
6244 if (flags & WINEDDBLT_DONOTWAIT)
6246 static unsigned int once;
6248 if (!once++)
6249 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
6250 flags &= ~WINEDDBLT_DONOTWAIT;
6253 if (!device->d3d_initialized)
6255 WARN("D3D not initialized, using fallback.\n");
6256 goto cpu;
6259 /* We want to avoid invalidating the sysmem location for converted
6260 * surfaces, since otherwise we'd have to convert the data back when
6261 * locking them. */
6262 if (dst_surface->flags & SFLAG_CONVERTED)
6264 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
6265 goto cpu;
6268 if (flags & ~simple_blit)
6270 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
6271 goto fallback;
6274 if (src_surface)
6275 src_swapchain = src_surface->swapchain;
6276 else
6277 src_swapchain = NULL;
6279 dst_swapchain = dst_surface->swapchain;
6281 /* This isn't strictly needed. FBO blits for example could deal with
6282 * cross-swapchain blits by first downloading the source to a texture
6283 * before switching to the destination context. We just have this here to
6284 * not have to deal with the issue, since cross-swapchain blits should be
6285 * rare. */
6286 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
6288 FIXME("Using fallback for cross-swapchain blit.\n");
6289 goto fallback;
6292 scale = src_surface
6293 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
6294 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
6295 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
6297 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
6298 if (src_surface)
6299 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
6300 else
6301 src_ds_flags = 0;
6303 if (src_ds_flags || dst_ds_flags)
6305 if (flags & WINEDDBLT_DEPTHFILL)
6307 float depth;
6309 TRACE("Depth fill.\n");
6311 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
6312 return WINED3DERR_INVALIDCALL;
6314 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
6315 return WINED3D_OK;
6317 else
6319 if (src_ds_flags != dst_ds_flags)
6321 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
6322 return WINED3DERR_INVALIDCALL;
6325 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->draw_binding, &src_rect,
6326 dst_surface, dst_surface->draw_binding, &dst_rect)))
6327 return WINED3D_OK;
6330 else
6332 /* In principle this would apply to depth blits as well, but we don't
6333 * implement those in the CPU blitter at the moment. */
6334 if ((dst_surface->flags & SFLAG_INSYSMEM)
6335 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
6337 if (scale)
6338 TRACE("Not doing sysmem blit because of scaling.\n");
6339 else if (convert)
6340 TRACE("Not doing sysmem blit because of format conversion.\n");
6341 else
6342 goto cpu;
6345 if (flags & WINEDDBLT_COLORFILL)
6347 struct wined3d_color color;
6349 TRACE("Color fill.\n");
6351 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
6352 goto fallback;
6354 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
6355 return WINED3D_OK;
6357 else
6359 TRACE("Color blit.\n");
6361 /* Upload */
6362 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
6364 if (scale)
6365 TRACE("Not doing upload because of scaling.\n");
6366 else if (convert)
6367 TRACE("Not doing upload because of format conversion.\n");
6368 else
6370 POINT dst_point = {dst_rect.left, dst_rect.top};
6372 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
6374 if (!surface_is_offscreen(dst_surface))
6375 surface_load_location(dst_surface, dst_surface->draw_binding);
6376 return WINED3D_OK;
6381 /* Use present for back -> front blits. The idea behind this is
6382 * that present is potentially faster than a blit, in particular
6383 * when FBO blits aren't available. Some ddraw applications like
6384 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
6385 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
6386 * applications can't blit directly to the frontbuffer. */
6387 if (dst_swapchain && dst_swapchain->back_buffers
6388 && dst_surface == dst_swapchain->front_buffer
6389 && src_surface == dst_swapchain->back_buffers[0])
6391 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
6393 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
6395 /* Set the swap effect to COPY, we don't want the backbuffer
6396 * to become undefined. */
6397 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
6398 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
6399 dst_swapchain->desc.swap_effect = swap_effect;
6401 return WINED3D_OK;
6404 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6405 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6406 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6408 TRACE("Using FBO blit.\n");
6410 surface_blt_fbo(device, filter,
6411 src_surface, src_surface->draw_binding, &src_rect,
6412 dst_surface, dst_surface->draw_binding, &dst_rect);
6413 surface_validate_location(dst_surface, dst_surface->draw_binding);
6414 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
6416 return WINED3D_OK;
6419 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6420 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6421 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6423 TRACE("Using arbfp blit.\n");
6425 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
6426 return WINED3D_OK;
6431 fallback:
6432 /* Special cases for render targets. */
6433 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
6434 return WINED3D_OK;
6436 cpu:
6438 /* For the rest call the X11 surface implementation. For render targets
6439 * this should be implemented OpenGL accelerated in surface_blt_special(),
6440 * other blits are rather rare. */
6441 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
6444 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
6445 const struct wined3d_resource_desc *desc, DWORD flags)
6447 struct wined3d_device *device = container->resource.device;
6448 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6449 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
6450 UINT multisample_quality = desc->multisample_quality;
6451 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
6452 unsigned int resource_size;
6453 HRESULT hr;
6455 if (multisample_quality > 0)
6457 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
6458 multisample_quality = 0;
6461 /* Quick lockable sanity check.
6462 * TODO: remove this after surfaces, usage and lockability have been debugged properly
6463 * this function is too deep to need to care about things like this.
6464 * Levels need to be checked too, since they all affect what can be done. */
6465 switch (desc->pool)
6467 case WINED3D_POOL_MANAGED:
6468 if (desc->usage & WINED3DUSAGE_DYNAMIC)
6469 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
6470 break;
6472 case WINED3D_POOL_DEFAULT:
6473 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
6474 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
6475 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
6476 break;
6478 case WINED3D_POOL_SCRATCH:
6479 case WINED3D_POOL_SYSTEM_MEM:
6480 break;
6482 default:
6483 FIXME("Unknown pool %#x.\n", desc->pool);
6484 break;
6487 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
6488 FIXME("Trying to create a render target that isn't in the default pool.\n");
6490 /* FIXME: Check that the format is supported by the device. */
6492 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
6493 if (!resource_size)
6494 return WINED3DERR_INVALIDCALL;
6496 if (device->wined3d->flags & WINED3D_NO3D)
6497 surface->surface_ops = &gdi_surface_ops;
6498 else
6499 surface->surface_ops = &surface_ops;
6501 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
6502 desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height, 1,
6503 resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
6505 WARN("Failed to initialize resource, returning %#x.\n", hr);
6506 return hr;
6509 surface_set_container(surface, container);
6510 list_init(&surface->overlays);
6512 /* Flags */
6513 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
6514 if (flags & WINED3D_SURFACE_DISCARD)
6515 surface->flags |= SFLAG_DISCARD;
6516 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
6517 surface->flags |= SFLAG_PIN_SYSMEM;
6518 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
6519 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
6521 /* I'm not sure if this qualifies as a hack or as an optimization. It
6522 * seems reasonable to assume that lockable render targets will get
6523 * locked, so we might as well set SFLAG_DYNLOCK right at surface
6524 * creation. However, the other reason we want to do this is that several
6525 * ddraw applications access surface memory while the surface isn't
6526 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
6527 * future locks prevents these from crashing. */
6528 if (lockable && (desc->usage & WINED3DUSAGE_RENDERTARGET))
6529 surface->flags |= SFLAG_DYNLOCK;
6531 /* Mark the texture as dirty so that it gets loaded first time around. */
6532 surface_set_dirty(surface);
6533 list_init(&surface->renderbuffers);
6535 TRACE("surface %p, memory %p, size %u\n",
6536 surface, surface->resource.allocatedMemory, surface->resource.size);
6538 /* Call the private setup routine */
6539 hr = surface->surface_ops->surface_private_setup(surface);
6540 if (FAILED(hr))
6542 ERR("Private setup failed, returning %#x\n", hr);
6543 surface_set_container(surface, NULL);
6544 surface_cleanup(surface);
6545 return hr;
6548 /* Similar to lockable rendertargets above, creating the DIB section
6549 * during surface initialization prevents the sysmem pointer from changing
6550 * after a wined3d_surface_getdc() call. */
6551 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
6552 && SUCCEEDED(surface_create_dib_section(surface)))
6554 wined3d_resource_free_sysmem(&surface->resource);
6555 surface->resource.allocatedMemory = surface->dib.bitmap_data;
6558 return hr;
6561 HRESULT CDECL wined3d_surface_create(struct wined3d_texture *container,
6562 const struct wined3d_resource_desc *desc, DWORD flags, struct wined3d_surface **surface)
6564 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
6565 const struct wined3d_parent_ops *parent_ops;
6566 struct wined3d_surface *object;
6567 void *parent;
6568 HRESULT hr;
6570 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), "
6571 "pool %s, multisample_type %#x, multisample_quality %u, flags %#x, surface %p.\n",
6572 container, desc->width, desc->height, debug_d3dformat(desc->format),
6573 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
6574 desc->multisample_type, desc->multisample_quality, flags, surface);
6576 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
6577 return E_OUTOFMEMORY;
6579 if (FAILED(hr = surface_init(object, container, desc, flags)))
6581 WARN("Failed to initialize surface, returning %#x.\n", hr);
6582 HeapFree(GetProcessHeap(), 0, object);
6583 return hr;
6586 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
6587 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
6589 WARN("Failed to create surface parent, hr %#x.\n", hr);
6590 surface_set_container(object, NULL);
6591 wined3d_surface_decref(object);
6592 return hr;
6595 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
6597 object->resource.parent = parent;
6598 object->resource.parent_ops = parent_ops;
6599 *surface = object;
6601 return hr;