wined3d: Pass the destination pitch to format conversion functions.
[wine.git] / dlls / wined3d / surface.c
bloba36664e9dfd7eaf4ba99ee88e022cfca2c58e216
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->texture_name || (surface->flags & SFLAG_PBO)
46 || surface->rb_multisample || surface->rb_resolved
47 || !list_empty(&surface->renderbuffers))
49 struct wined3d_renderbuffer_entry *entry, *entry2;
50 const struct wined3d_gl_info *gl_info;
51 struct wined3d_context *context;
53 context = context_acquire(surface->resource.device, NULL);
54 gl_info = context->gl_info;
56 if (surface->texture_name)
58 TRACE("Deleting texture %u.\n", surface->texture_name);
59 gl_info->gl_ops.gl.p_glDeleteTextures(1, &surface->texture_name);
62 if (surface->flags & SFLAG_PBO)
64 TRACE("Deleting PBO %u.\n", surface->pbo);
65 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
68 if (surface->rb_multisample)
70 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
71 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
74 if (surface->rb_resolved)
76 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
77 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
80 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
82 TRACE("Deleting renderbuffer %u.\n", entry->id);
83 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
84 HeapFree(GetProcessHeap(), 0, entry);
87 context_release(context);
90 if (surface->flags & SFLAG_DIBSECTION)
92 DeleteDC(surface->hDC);
93 DeleteObject(surface->dib.DIBsection);
94 surface->dib.bitmap_data = NULL;
95 surface->resource.allocatedMemory = NULL;
98 if (surface->flags & SFLAG_USERPTR)
99 wined3d_surface_set_mem(surface, NULL, 0);
100 if (surface->overlay_dest)
101 list_remove(&surface->overlay_entry);
103 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
105 list_remove(&overlay->overlay_entry);
106 overlay->overlay_dest = NULL;
109 resource_cleanup(&surface->resource);
112 void surface_update_draw_binding(struct wined3d_surface *surface)
114 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
115 surface->draw_binding = SFLAG_INDRAWABLE;
116 else if (surface->resource.multisample_type)
117 surface->draw_binding = SFLAG_INRB_MULTISAMPLE;
118 else
119 surface->draw_binding = SFLAG_INTEXTURE;
122 void surface_set_swapchain(struct wined3d_surface *surface, struct wined3d_swapchain *swapchain)
124 TRACE("surface %p, swapchain %p.\n", surface, swapchain);
126 if (swapchain)
128 surface->get_drawable_size = get_drawable_size_swapchain;
130 else
132 switch (wined3d_settings.offscreen_rendering_mode)
134 case ORM_FBO:
135 surface->get_drawable_size = get_drawable_size_fbo;
136 break;
138 case ORM_BACKBUFFER:
139 surface->get_drawable_size = get_drawable_size_backbuffer;
140 break;
142 default:
143 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
144 return;
148 surface->swapchain = swapchain;
149 surface_update_draw_binding(surface);
152 void surface_set_container(struct wined3d_surface *surface, struct wined3d_texture *container)
154 TRACE("surface %p, container %p.\n", surface, container);
156 if (!surface->swapchain)
158 switch (wined3d_settings.offscreen_rendering_mode)
160 case ORM_FBO:
161 surface->get_drawable_size = get_drawable_size_fbo;
162 break;
164 case ORM_BACKBUFFER:
165 surface->get_drawable_size = get_drawable_size_backbuffer;
166 break;
168 default:
169 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
170 return;
174 surface->container = container;
175 surface_update_draw_binding(surface);
178 struct blt_info
180 GLenum binding;
181 GLenum bind_target;
182 enum tex_types tex_type;
183 GLfloat coords[4][3];
186 struct float_rect
188 float l;
189 float t;
190 float r;
191 float b;
194 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
196 f->l = ((r->left * 2.0f) / w) - 1.0f;
197 f->t = ((r->top * 2.0f) / h) - 1.0f;
198 f->r = ((r->right * 2.0f) / w) - 1.0f;
199 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
202 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
204 GLfloat (*coords)[3] = info->coords;
205 struct float_rect f;
207 switch (target)
209 default:
210 FIXME("Unsupported texture target %#x\n", target);
211 /* Fall back to GL_TEXTURE_2D */
212 case GL_TEXTURE_2D:
213 info->binding = GL_TEXTURE_BINDING_2D;
214 info->bind_target = GL_TEXTURE_2D;
215 info->tex_type = tex_2d;
216 coords[0][0] = (float)rect->left / w;
217 coords[0][1] = (float)rect->top / h;
218 coords[0][2] = 0.0f;
220 coords[1][0] = (float)rect->right / w;
221 coords[1][1] = (float)rect->top / h;
222 coords[1][2] = 0.0f;
224 coords[2][0] = (float)rect->left / w;
225 coords[2][1] = (float)rect->bottom / h;
226 coords[2][2] = 0.0f;
228 coords[3][0] = (float)rect->right / w;
229 coords[3][1] = (float)rect->bottom / h;
230 coords[3][2] = 0.0f;
231 break;
233 case GL_TEXTURE_RECTANGLE_ARB:
234 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
235 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
236 info->tex_type = tex_rect;
237 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
238 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
239 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
240 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
241 break;
243 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
244 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
245 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
246 info->tex_type = tex_cube;
247 cube_coords_float(rect, w, h, &f);
249 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
250 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
251 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
252 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
253 break;
255 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
256 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
257 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
258 info->tex_type = tex_cube;
259 cube_coords_float(rect, w, h, &f);
261 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
262 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
263 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
264 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
265 break;
267 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
268 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
269 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
270 info->tex_type = tex_cube;
271 cube_coords_float(rect, w, h, &f);
273 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
274 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
275 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
276 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
277 break;
279 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
280 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
281 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
282 info->tex_type = tex_cube;
283 cube_coords_float(rect, w, h, &f);
285 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
286 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
287 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
288 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
289 break;
291 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
292 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
293 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
294 info->tex_type = tex_cube;
295 cube_coords_float(rect, w, h, &f);
297 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
298 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
299 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
300 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
301 break;
303 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
304 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
305 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
306 info->tex_type = tex_cube;
307 cube_coords_float(rect, w, h, &f);
309 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
310 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
311 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
312 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
313 break;
317 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
319 if (rect_in)
320 *rect_out = *rect_in;
321 else
323 rect_out->left = 0;
324 rect_out->top = 0;
325 rect_out->right = surface->resource.width;
326 rect_out->bottom = surface->resource.height;
330 /* Context activation is done by the caller. */
331 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
332 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
334 const struct wined3d_gl_info *gl_info = context->gl_info;
335 struct blt_info info;
337 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
339 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
340 checkGLcall("glEnable(bind_target)");
342 context_bind_texture(context, info.bind_target, src_surface->texture_name);
344 /* Filtering for StretchRect */
345 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
346 wined3d_gl_mag_filter(magLookup, filter));
347 checkGLcall("glTexParameteri");
348 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
349 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
350 checkGLcall("glTexParameteri");
351 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
352 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
353 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
354 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
355 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
356 checkGLcall("glTexEnvi");
358 /* Draw a quad */
359 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
360 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
361 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
363 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
364 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
366 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
367 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
369 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
370 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
371 gl_info->gl_ops.gl.p_glEnd();
373 /* Unbind the texture */
374 context_bind_texture(context, info.bind_target, 0);
376 /* We changed the filtering settings on the texture. Inform the
377 * container about this to get the filters reset properly next draw. */
378 if (src_surface->container)
380 struct wined3d_texture *texture = src_surface->container;
381 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_POINT;
382 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
383 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_NONE;
384 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
388 /* Works correctly only for <= 4 bpp formats. */
389 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
391 masks[0] = ((1 << format->red_size) - 1) << format->red_offset;
392 masks[1] = ((1 << format->green_size) - 1) << format->green_offset;
393 masks[2] = ((1 << format->blue_size) - 1) << format->blue_offset;
396 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
398 const struct wined3d_format *format = surface->resource.format;
399 SYSTEM_INFO sysInfo;
400 BITMAPINFO *b_info;
401 int extraline = 0;
402 DWORD *masks;
404 TRACE("surface %p.\n", surface);
406 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
408 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
409 return WINED3DERR_INVALIDCALL;
412 switch (format->byte_count)
414 case 2:
415 case 4:
416 /* Allocate extra space to store the RGB bit masks. */
417 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
418 break;
420 case 3:
421 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
422 break;
424 default:
425 /* Allocate extra space for a palette. */
426 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
427 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
428 break;
431 if (!b_info)
432 return E_OUTOFMEMORY;
434 /* Some applications access the surface in via DWORDs, and do not take
435 * the necessary care at the end of the surface. So we need at least
436 * 4 extra bytes at the end of the surface. Check against the page size,
437 * if the last page used for the surface has at least 4 spare bytes we're
438 * safe, otherwise add an extra line to the DIB section. */
439 GetSystemInfo(&sysInfo);
440 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
442 extraline = 1;
443 TRACE("Adding an extra line to the DIB section.\n");
446 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
447 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
448 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
449 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
450 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
451 * wined3d_surface_get_pitch(surface);
452 b_info->bmiHeader.biPlanes = 1;
453 b_info->bmiHeader.biBitCount = format->byte_count * 8;
455 b_info->bmiHeader.biXPelsPerMeter = 0;
456 b_info->bmiHeader.biYPelsPerMeter = 0;
457 b_info->bmiHeader.biClrUsed = 0;
458 b_info->bmiHeader.biClrImportant = 0;
460 /* Get the bit masks */
461 masks = (DWORD *)b_info->bmiColors;
462 switch (surface->resource.format->id)
464 case WINED3DFMT_B8G8R8_UNORM:
465 b_info->bmiHeader.biCompression = BI_RGB;
466 break;
468 case WINED3DFMT_B5G5R5X1_UNORM:
469 case WINED3DFMT_B5G5R5A1_UNORM:
470 case WINED3DFMT_B4G4R4A4_UNORM:
471 case WINED3DFMT_B4G4R4X4_UNORM:
472 case WINED3DFMT_B2G3R3_UNORM:
473 case WINED3DFMT_B2G3R3A8_UNORM:
474 case WINED3DFMT_R10G10B10A2_UNORM:
475 case WINED3DFMT_R8G8B8A8_UNORM:
476 case WINED3DFMT_R8G8B8X8_UNORM:
477 case WINED3DFMT_B10G10R10A2_UNORM:
478 case WINED3DFMT_B5G6R5_UNORM:
479 case WINED3DFMT_R16G16B16A16_UNORM:
480 b_info->bmiHeader.biCompression = BI_BITFIELDS;
481 get_color_masks(format, masks);
482 break;
484 default:
485 /* Don't know palette */
486 b_info->bmiHeader.biCompression = BI_RGB;
487 break;
490 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
491 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
492 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
493 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
495 if (!surface->dib.DIBsection)
497 ERR("Failed to create DIB section.\n");
498 HeapFree(GetProcessHeap(), 0, b_info);
499 return HRESULT_FROM_WIN32(GetLastError());
502 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
503 /* Copy the existing surface to the dib section. */
504 if (surface->resource.allocatedMemory)
506 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
507 surface->resource.height * wined3d_surface_get_pitch(surface));
509 else
511 /* This is to make maps read the GL texture although memory is allocated. */
512 surface->flags &= ~SFLAG_INSYSMEM;
514 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
516 HeapFree(GetProcessHeap(), 0, b_info);
518 /* Now allocate a DC. */
519 surface->hDC = CreateCompatibleDC(0);
520 SelectObject(surface->hDC, surface->dib.DIBsection);
521 TRACE("Using wined3d palette %p.\n", surface->palette);
522 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
524 surface->flags |= SFLAG_DIBSECTION;
526 return WINED3D_OK;
529 static BOOL surface_need_pbo(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
531 if (surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
532 return FALSE;
533 if (!(surface->flags & SFLAG_DYNLOCK))
534 return FALSE;
535 if (surface->flags & (SFLAG_CONVERTED | SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM))
536 return FALSE;
537 if (!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT])
538 return FALSE;
540 return TRUE;
543 static void surface_load_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
545 struct wined3d_context *context;
546 GLenum error;
548 context = context_acquire(surface->resource.device, NULL);
550 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
551 error = gl_info->gl_ops.gl.p_glGetError();
552 if (!surface->pbo || error != GL_NO_ERROR)
553 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
555 TRACE("Binding PBO %u.\n", surface->pbo);
557 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
558 checkGLcall("glBindBufferARB");
560 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
561 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
562 checkGLcall("glBufferDataARB");
564 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
565 checkGLcall("glBindBufferARB");
567 /* We don't need the system memory anymore and we can't even use it for PBOs. */
568 if (!(surface->flags & SFLAG_CLIENT))
569 wined3d_resource_free_sysmem(&surface->resource);
570 surface->resource.allocatedMemory = NULL;
571 surface->flags |= SFLAG_PBO;
572 context_release(context);
575 static void surface_prepare_system_memory(struct wined3d_surface *surface)
577 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
579 TRACE("surface %p.\n", surface);
581 if (!(surface->flags & SFLAG_PBO) && surface_need_pbo(surface, gl_info))
582 surface_load_pbo(surface, gl_info);
583 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
585 /* Whatever surface we have, make sure that there is memory allocated
586 * for the downloaded copy, or a PBO to map. */
587 if (!surface->resource.heap_memory && !wined3d_resource_allocate_sysmem(&surface->resource))
588 ERR("Failed to allocate system memory.\n");
589 surface->resource.allocatedMemory = surface->resource.heap_memory;
591 if (surface->flags & SFLAG_INSYSMEM)
592 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
596 static void surface_evict_sysmem(struct wined3d_surface *surface)
598 if (surface->resource.map_count || (surface->flags & SFLAG_DONOTFREE))
599 return;
601 wined3d_resource_free_sysmem(&surface->resource);
602 surface->resource.allocatedMemory = NULL;
603 surface_invalidate_location(surface, SFLAG_INSYSMEM);
606 /* Context activation is done by the caller. */
607 static void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
609 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
611 if (surface->container)
613 struct wined3d_texture *texture = surface->container;
615 TRACE("Passing to container (%p).\n", texture);
616 texture->texture_ops->texture_bind(texture, context, srgb);
618 else
620 const struct wined3d_gl_info *gl_info = context->gl_info;
622 if (surface->texture_level)
624 ERR("Standalone surface %p is non-zero texture level %u.\n",
625 surface, surface->texture_level);
628 if (srgb)
629 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
631 if (!surface->texture_name)
633 gl_info->gl_ops.gl.p_glGenTextures(1, &surface->texture_name);
634 checkGLcall("glGenTextures");
636 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
638 context_bind_texture(context, surface->texture_target, surface->texture_name);
639 gl_info->gl_ops.gl.p_glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
640 gl_info->gl_ops.gl.p_glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
641 gl_info->gl_ops.gl.p_glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
642 gl_info->gl_ops.gl.p_glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
643 gl_info->gl_ops.gl.p_glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
644 checkGLcall("glTexParameteri");
646 else
648 context_bind_texture(context, surface->texture_target, surface->texture_name);
653 /* Context activation is done by the caller. */
654 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
655 struct wined3d_context *context, BOOL srgb)
657 DWORD active_sampler;
659 /* We don't need a specific texture unit, but after binding the texture
660 * the current unit is dirty. Read the unit back instead of switching to
661 * 0, this avoids messing around with the state manager's GL states. The
662 * current texture unit should always be a valid one.
664 * To be more specific, this is tricky because we can implicitly be
665 * called from sampler() in state.c. This means we can't touch anything
666 * other than whatever happens to be the currently active texture, or we
667 * would risk marking already applied sampler states dirty again. */
668 active_sampler = context->rev_tex_unit_map[context->active_texture];
670 if (active_sampler != WINED3D_UNMAPPED_STAGE)
671 context_invalidate_state(context, STATE_SAMPLER(active_sampler));
672 surface_bind(surface, context, srgb);
675 static void surface_force_reload(struct wined3d_surface *surface)
677 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
680 static void surface_release_client_storage(struct wined3d_surface *surface)
682 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
683 const struct wined3d_gl_info *gl_info = context->gl_info;
685 if (surface->texture_name)
687 surface_bind_and_dirtify(surface, context, FALSE);
688 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
689 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
691 if (surface->texture_name_srgb)
693 surface_bind_and_dirtify(surface, context, TRUE);
694 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
695 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
698 context_release(context);
700 surface_invalidate_location(surface, SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
701 surface_force_reload(surface);
704 static HRESULT surface_private_setup(struct wined3d_surface *surface)
706 /* TODO: Check against the maximum texture sizes supported by the video card. */
707 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
708 unsigned int pow2Width, pow2Height;
710 TRACE("surface %p.\n", surface);
712 surface->texture_name = 0;
713 surface->texture_target = GL_TEXTURE_2D;
715 /* Non-power2 support */
716 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
718 pow2Width = surface->resource.width;
719 pow2Height = surface->resource.height;
721 else
723 /* Find the nearest pow2 match */
724 pow2Width = pow2Height = 1;
725 while (pow2Width < surface->resource.width)
726 pow2Width <<= 1;
727 while (pow2Height < surface->resource.height)
728 pow2Height <<= 1;
730 surface->pow2Width = pow2Width;
731 surface->pow2Height = pow2Height;
733 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
735 /* TODO: Add support for non power two compressed textures. */
736 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
738 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
739 surface, surface->resource.width, surface->resource.height);
740 return WINED3DERR_NOTAVAILABLE;
744 if (pow2Width != surface->resource.width
745 || pow2Height != surface->resource.height)
747 surface->flags |= SFLAG_NONPOW2;
750 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
751 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
753 /* One of three options:
754 * 1: Do the same as we do with NPOT and scale the texture, (any
755 * texture ops would require the texture to be scaled which is
756 * potentially slow)
757 * 2: Set the texture to the maximum size (bad idea).
758 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
759 * 4: Create the surface, but allow it to be used only for DirectDraw
760 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
761 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
762 * the render target. */
763 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
765 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
766 return WINED3DERR_NOTAVAILABLE;
769 /* We should never use this surface in combination with OpenGL! */
770 TRACE("Creating an oversized surface: %ux%u.\n",
771 surface->pow2Width, surface->pow2Height);
774 switch (wined3d_settings.offscreen_rendering_mode)
776 case ORM_FBO:
777 surface->get_drawable_size = get_drawable_size_fbo;
778 break;
780 case ORM_BACKBUFFER:
781 surface->get_drawable_size = get_drawable_size_backbuffer;
782 break;
784 default:
785 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
786 return WINED3DERR_INVALIDCALL;
789 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
790 surface->flags |= SFLAG_DISCARDED;
792 return WINED3D_OK;
795 static void surface_realize_palette(struct wined3d_surface *surface)
797 struct wined3d_palette *palette = surface->palette;
799 TRACE("surface %p.\n", surface);
801 if (!palette) return;
803 if (surface->resource.format->id == WINED3DFMT_P8_UINT
804 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
806 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
808 /* Make sure the texture is up to date. This call doesn't do
809 * anything if the texture is already up to date. */
810 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
812 /* We want to force a palette refresh, so mark the drawable as not being up to date */
813 if (!surface_is_offscreen(surface))
814 surface_invalidate_location(surface, SFLAG_INDRAWABLE);
816 else
818 if (!(surface->flags & SFLAG_INSYSMEM))
820 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
821 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
823 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
827 if (surface->flags & SFLAG_DIBSECTION)
829 RGBQUAD col[256];
830 unsigned int i;
832 TRACE("Updating the DC's palette.\n");
834 for (i = 0; i < 256; ++i)
836 col[i].rgbRed = palette->palents[i].peRed;
837 col[i].rgbGreen = palette->palents[i].peGreen;
838 col[i].rgbBlue = palette->palents[i].peBlue;
839 col[i].rgbReserved = 0;
841 SetDIBColorTable(surface->hDC, 0, 256, col);
844 /* Propagate the changes to the drawable when we have a palette. */
845 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
846 surface_load_location(surface, surface->draw_binding, NULL);
849 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
851 HRESULT hr;
853 /* If there's no destination surface there is nothing to do. */
854 if (!surface->overlay_dest)
855 return WINED3D_OK;
857 /* Blt calls ModifyLocation on the dest surface, which in turn calls
858 * DrawOverlay to update the overlay. Prevent an endless recursion. */
859 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
860 return WINED3D_OK;
862 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
863 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
864 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3D_TEXF_LINEAR);
865 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
867 return hr;
870 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
872 struct wined3d_device *device = surface->resource.device;
873 const RECT *pass_rect = rect;
875 TRACE("surface %p, rect %s, flags %#x.\n",
876 surface, wine_dbgstr_rect(rect), flags);
878 if (flags & WINED3D_MAP_DISCARD)
880 TRACE("WINED3D_MAP_DISCARD flag passed, marking SYSMEM as up to date.\n");
881 surface_prepare_system_memory(surface);
882 surface_validate_location(surface, SFLAG_INSYSMEM);
883 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
885 else
887 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
888 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
890 /* surface_load_location() does not check if the rectangle specifies
891 * the full surface. Most callers don't need that, so do it here. */
892 if (rect && !rect->top && !rect->left
893 && rect->right == surface->resource.width
894 && rect->bottom == surface->resource.height)
895 pass_rect = NULL;
896 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
899 if (surface->flags & SFLAG_PBO)
901 const struct wined3d_gl_info *gl_info;
902 struct wined3d_context *context;
904 context = context_acquire(device, NULL);
905 gl_info = context->gl_info;
907 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
908 checkGLcall("glBindBufferARB");
910 /* This shouldn't happen but could occur if some other function
911 * didn't handle the PBO properly. */
912 if (surface->resource.allocatedMemory)
913 ERR("The surface already has PBO memory allocated.\n");
915 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
916 checkGLcall("glMapBufferARB");
918 /* Make sure the PBO isn't set anymore in order not to break non-PBO
919 * calls. */
920 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
921 checkGLcall("glBindBufferARB");
923 context_release(context);
926 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
928 if (!rect)
929 surface_add_dirty_rect(surface, NULL);
930 else
932 struct wined3d_box b;
934 b.left = rect->left;
935 b.top = rect->top;
936 b.right = rect->right;
937 b.bottom = rect->bottom;
938 b.front = 0;
939 b.back = 1;
940 surface_add_dirty_rect(surface, &b);
945 static void surface_unmap(struct wined3d_surface *surface)
947 struct wined3d_device *device = surface->resource.device;
948 BOOL fullsurface;
950 TRACE("surface %p.\n", surface);
952 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
954 if (surface->flags & SFLAG_PBO)
956 const struct wined3d_gl_info *gl_info;
957 struct wined3d_context *context;
959 TRACE("Freeing PBO memory.\n");
961 context = context_acquire(device, NULL);
962 gl_info = context->gl_info;
964 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
965 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
966 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
967 checkGLcall("glUnmapBufferARB");
968 context_release(context);
970 surface->resource.allocatedMemory = NULL;
973 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
975 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
977 TRACE("Not dirtified, nothing to do.\n");
978 goto done;
981 if (surface->swapchain && surface->swapchain->front_buffer == surface)
983 if (!surface->dirtyRect.left && !surface->dirtyRect.top
984 && surface->dirtyRect.right == surface->resource.width
985 && surface->dirtyRect.bottom == surface->resource.height)
987 fullsurface = TRUE;
989 else
991 /* TODO: Proper partial rectangle tracking. */
992 fullsurface = FALSE;
993 surface->flags |= SFLAG_INSYSMEM;
996 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
998 /* Partial rectangle tracking is not commonly implemented, it is only
999 * done for render targets. INSYSMEM was set before to tell
1000 * surface_load_location() where to read the rectangle from.
1001 * Indrawable is set because all modifications from the partial
1002 * sysmem copy are written back to the drawable, thus the surface is
1003 * merged again in the drawable. The sysmem copy is not fully up to
1004 * date because only a subrectangle was read in Map(). */
1005 if (!fullsurface)
1007 surface_validate_location(surface, surface->draw_binding);
1008 surface_invalidate_location(surface, ~surface->draw_binding);
1009 surface_evict_sysmem(surface);
1012 surface->dirtyRect.left = surface->resource.width;
1013 surface->dirtyRect.top = surface->resource.height;
1014 surface->dirtyRect.right = 0;
1015 surface->dirtyRect.bottom = 0;
1017 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
1019 FIXME("Depth / stencil buffer locking is not implemented.\n");
1022 done:
1023 /* Overlays have to be redrawn manually after changes with the GL implementation */
1024 if (surface->overlay_dest)
1025 surface_draw_overlay(surface);
1028 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1030 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1031 return FALSE;
1032 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1033 return FALSE;
1034 return TRUE;
1037 static void surface_depth_blt_fbo(const struct wined3d_device *device,
1038 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1039 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1041 const struct wined3d_gl_info *gl_info;
1042 struct wined3d_context *context;
1043 DWORD src_mask, dst_mask;
1044 GLbitfield gl_mask;
1046 TRACE("device %p\n", device);
1047 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1048 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect));
1049 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1050 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect));
1052 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1053 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1055 if (src_mask != dst_mask)
1057 ERR("Incompatible formats %s and %s.\n",
1058 debug_d3dformat(src_surface->resource.format->id),
1059 debug_d3dformat(dst_surface->resource.format->id));
1060 return;
1063 if (!src_mask)
1065 ERR("Not a depth / stencil format: %s.\n",
1066 debug_d3dformat(src_surface->resource.format->id));
1067 return;
1070 gl_mask = 0;
1071 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1072 gl_mask |= GL_DEPTH_BUFFER_BIT;
1073 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1074 gl_mask |= GL_STENCIL_BUFFER_BIT;
1076 /* Make sure the locations are up-to-date. Loading the destination
1077 * surface isn't required if the entire surface is overwritten. */
1078 surface_load_location(src_surface, src_location, NULL);
1079 if (!surface_is_full_rect(dst_surface, dst_rect))
1080 surface_load_location(dst_surface, dst_location, NULL);
1082 context = context_acquire(device, NULL);
1083 if (!context->valid)
1085 context_release(context);
1086 WARN("Invalid context, skipping blit.\n");
1087 return;
1090 gl_info = context->gl_info;
1092 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
1093 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1095 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
1096 context_set_draw_buffer(context, GL_NONE);
1097 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1098 context_invalidate_state(context, STATE_FRAMEBUFFER);
1100 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1102 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
1103 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
1105 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1107 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1109 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1110 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
1112 gl_info->gl_ops.gl.p_glStencilMask(~0U);
1113 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
1116 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
1117 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1119 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1120 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1121 checkGLcall("glBlitFramebuffer()");
1123 if (wined3d_settings.strict_draw_ordering)
1124 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
1126 context_release(context);
1129 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1130 * Depth / stencil is not supported. */
1131 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
1132 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1133 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1135 const struct wined3d_gl_info *gl_info;
1136 struct wined3d_context *context;
1137 RECT src_rect, dst_rect;
1138 GLenum gl_filter;
1139 GLenum buffer;
1141 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1142 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1143 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1144 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1145 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1147 src_rect = *src_rect_in;
1148 dst_rect = *dst_rect_in;
1150 switch (filter)
1152 case WINED3D_TEXF_LINEAR:
1153 gl_filter = GL_LINEAR;
1154 break;
1156 default:
1157 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1158 case WINED3D_TEXF_NONE:
1159 case WINED3D_TEXF_POINT:
1160 gl_filter = GL_NEAREST;
1161 break;
1164 /* Resolve the source surface first if needed. */
1165 if (src_location == SFLAG_INRB_MULTISAMPLE
1166 && (src_surface->resource.format->id != dst_surface->resource.format->id
1167 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1168 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1169 src_location = SFLAG_INRB_RESOLVED;
1171 /* Make sure the locations are up-to-date. Loading the destination
1172 * surface isn't required if the entire surface is overwritten. (And is
1173 * in fact harmful if we're being called by surface_load_location() with
1174 * the purpose of loading the destination surface.) */
1175 surface_load_location(src_surface, src_location, NULL);
1176 if (!surface_is_full_rect(dst_surface, &dst_rect))
1177 surface_load_location(dst_surface, dst_location, NULL);
1179 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1180 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1181 else context = context_acquire(device, NULL);
1183 if (!context->valid)
1185 context_release(context);
1186 WARN("Invalid context, skipping blit.\n");
1187 return;
1190 gl_info = context->gl_info;
1192 if (src_location == SFLAG_INDRAWABLE)
1194 TRACE("Source surface %p is onscreen.\n", src_surface);
1195 buffer = surface_get_gl_buffer(src_surface);
1196 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1198 else
1200 TRACE("Source surface %p is offscreen.\n", src_surface);
1201 buffer = GL_COLOR_ATTACHMENT0;
1204 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1205 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1206 checkGLcall("glReadBuffer()");
1207 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1209 if (dst_location == SFLAG_INDRAWABLE)
1211 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1212 buffer = surface_get_gl_buffer(dst_surface);
1213 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1215 else
1217 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1218 buffer = GL_COLOR_ATTACHMENT0;
1221 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1222 context_set_draw_buffer(context, buffer);
1223 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1224 context_invalidate_state(context, STATE_FRAMEBUFFER);
1226 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1227 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
1228 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
1229 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
1230 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
1232 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
1233 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1235 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1236 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1237 checkGLcall("glBlitFramebuffer()");
1239 if (wined3d_settings.strict_draw_ordering
1240 || (dst_location == SFLAG_INDRAWABLE
1241 && dst_surface->swapchain->front_buffer == dst_surface))
1242 gl_info->gl_ops.gl.p_glFlush();
1244 context_release(context);
1247 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1248 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
1249 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
1251 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1252 return FALSE;
1254 /* Source and/or destination need to be on the GL side */
1255 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
1256 return FALSE;
1258 switch (blit_op)
1260 case WINED3D_BLIT_OP_COLOR_BLIT:
1261 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1262 return FALSE;
1263 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1264 return FALSE;
1265 break;
1267 case WINED3D_BLIT_OP_DEPTH_BLIT:
1268 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1269 return FALSE;
1270 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1271 return FALSE;
1272 break;
1274 default:
1275 return FALSE;
1278 if (!(src_format->id == dst_format->id
1279 || (is_identity_fixup(src_format->color_fixup)
1280 && is_identity_fixup(dst_format->color_fixup))))
1281 return FALSE;
1283 return TRUE;
1286 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1287 DWORD color, struct wined3d_color *float_color)
1289 const struct wined3d_format *format = surface->resource.format;
1290 const struct wined3d_device *device = surface->resource.device;
1292 switch (format->id)
1294 case WINED3DFMT_P8_UINT:
1295 if (surface->palette)
1297 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1298 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1299 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1301 else
1303 float_color->r = 0.0f;
1304 float_color->g = 0.0f;
1305 float_color->b = 0.0f;
1307 float_color->a = swapchain_is_p8(device->swapchains[0]) ? color / 255.0f : 1.0f;
1308 break;
1310 case WINED3DFMT_B5G6R5_UNORM:
1311 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1312 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1313 float_color->b = (color & 0x1f) / 31.0f;
1314 float_color->a = 1.0f;
1315 break;
1317 case WINED3DFMT_B8G8R8_UNORM:
1318 case WINED3DFMT_B8G8R8X8_UNORM:
1319 float_color->r = D3DCOLOR_R(color);
1320 float_color->g = D3DCOLOR_G(color);
1321 float_color->b = D3DCOLOR_B(color);
1322 float_color->a = 1.0f;
1323 break;
1325 case WINED3DFMT_B8G8R8A8_UNORM:
1326 float_color->r = D3DCOLOR_R(color);
1327 float_color->g = D3DCOLOR_G(color);
1328 float_color->b = D3DCOLOR_B(color);
1329 float_color->a = D3DCOLOR_A(color);
1330 break;
1332 default:
1333 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1334 return FALSE;
1337 return TRUE;
1340 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1342 const struct wined3d_format *format = surface->resource.format;
1344 switch (format->id)
1346 case WINED3DFMT_S1_UINT_D15_UNORM:
1347 *float_depth = depth / (float)0x00007fff;
1348 break;
1350 case WINED3DFMT_D16_UNORM:
1351 *float_depth = depth / (float)0x0000ffff;
1352 break;
1354 case WINED3DFMT_D24_UNORM_S8_UINT:
1355 case WINED3DFMT_X8D24_UNORM:
1356 *float_depth = depth / (float)0x00ffffff;
1357 break;
1359 case WINED3DFMT_D32_UNORM:
1360 *float_depth = depth / (float)0xffffffff;
1361 break;
1363 default:
1364 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1365 return FALSE;
1368 return TRUE;
1371 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1373 const struct wined3d_resource *resource = &surface->resource;
1374 struct wined3d_device *device = resource->device;
1375 const struct blit_shader *blitter;
1377 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1378 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1379 if (!blitter)
1381 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1382 return WINED3DERR_INVALIDCALL;
1385 return blitter->depth_fill(device, surface, rect, depth);
1388 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1389 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1391 struct wined3d_device *device = src_surface->resource.device;
1393 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1394 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1395 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1396 return WINED3DERR_INVALIDCALL;
1398 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1400 surface_modify_ds_location(dst_surface, dst_location,
1401 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1403 return WINED3D_OK;
1406 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1407 struct wined3d_surface *render_target)
1409 TRACE("surface %p, render_target %p.\n", surface, render_target);
1411 /* TODO: Check surface sizes, pools, etc. */
1413 if (render_target->resource.multisample_type)
1414 return WINED3DERR_INVALIDCALL;
1416 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1419 /* Context activation is done by the caller. */
1420 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1422 if (surface->flags & SFLAG_DIBSECTION)
1424 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1426 else
1428 if (!surface->resource.heap_memory)
1429 wined3d_resource_allocate_sysmem(&surface->resource);
1430 else if (!(surface->flags & SFLAG_CLIENT))
1431 ERR("Surface %p has heap_memory %p and flags %#x.\n",
1432 surface, surface->resource.heap_memory, surface->flags);
1434 surface->resource.allocatedMemory = surface->resource.heap_memory;
1437 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1438 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1439 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1440 surface->resource.size, surface->resource.allocatedMemory));
1441 checkGLcall("glGetBufferSubDataARB");
1442 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1443 checkGLcall("glDeleteBuffersARB");
1445 surface->pbo = 0;
1446 surface->flags &= ~SFLAG_PBO;
1449 static BOOL surface_init_sysmem(struct wined3d_surface *surface)
1451 if (!surface->resource.allocatedMemory)
1453 if (!surface->resource.heap_memory)
1455 if (!wined3d_resource_allocate_sysmem(&surface->resource))
1457 ERR("Failed to allocate system memory.\n");
1458 return FALSE;
1461 else if (!(surface->flags & SFLAG_CLIENT))
1463 ERR("Surface %p has heap_memory %p and flags %#x.\n",
1464 surface, surface->resource.heap_memory, surface->flags);
1467 surface->resource.allocatedMemory = surface->resource.heap_memory;
1469 else
1471 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
1474 surface_validate_location(surface, SFLAG_INSYSMEM);
1475 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
1477 return TRUE;
1480 static void surface_unload(struct wined3d_resource *resource)
1482 struct wined3d_surface *surface = surface_from_resource(resource);
1483 struct wined3d_renderbuffer_entry *entry, *entry2;
1484 struct wined3d_device *device = resource->device;
1485 const struct wined3d_gl_info *gl_info;
1486 struct wined3d_context *context;
1488 TRACE("surface %p.\n", surface);
1490 if (resource->pool == WINED3D_POOL_DEFAULT)
1492 /* Default pool resources are supposed to be destroyed before Reset is called.
1493 * Implicit resources stay however. So this means we have an implicit render target
1494 * or depth stencil. The content may be destroyed, but we still have to tear down
1495 * opengl resources, so we cannot leave early.
1497 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1498 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1499 * or the depth stencil into an FBO the texture or render buffer will be removed
1500 * and all flags get lost
1502 if (!(surface->flags & SFLAG_PBO))
1503 surface_init_sysmem(surface);
1504 /* We also get here when the ddraw swapchain is destroyed, for example
1505 * for a mode switch. In this case this surface won't necessarily be
1506 * an implicit surface. We have to mark it lost so that the
1507 * application can restore it after the mode switch. */
1508 surface->flags |= SFLAG_LOST;
1510 else
1512 /* Load the surface into system memory */
1513 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1514 surface_invalidate_location(surface, surface->draw_binding);
1516 surface_invalidate_location(surface, SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
1517 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1519 context = context_acquire(device, NULL);
1520 gl_info = context->gl_info;
1522 /* Destroy PBOs, but load them into real sysmem before */
1523 if (surface->flags & SFLAG_PBO)
1524 surface_remove_pbo(surface, gl_info);
1526 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1527 * all application-created targets the application has to release the surface
1528 * before calling _Reset
1530 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1532 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1533 list_remove(&entry->entry);
1534 HeapFree(GetProcessHeap(), 0, entry);
1536 list_init(&surface->renderbuffers);
1537 surface->current_renderbuffer = NULL;
1539 /* If we're in a texture, the texture name belongs to the texture.
1540 * Otherwise, destroy it. */
1541 if (!surface->container)
1543 gl_info->gl_ops.gl.p_glDeleteTextures(1, &surface->texture_name);
1544 surface->texture_name = 0;
1545 gl_info->gl_ops.gl.p_glDeleteTextures(1, &surface->texture_name_srgb);
1546 surface->texture_name_srgb = 0;
1548 if (surface->rb_multisample)
1550 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1551 surface->rb_multisample = 0;
1553 if (surface->rb_resolved)
1555 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1556 surface->rb_resolved = 0;
1559 context_release(context);
1561 resource_unload(resource);
1564 static const struct wined3d_resource_ops surface_resource_ops =
1566 surface_unload,
1569 static const struct wined3d_surface_ops surface_ops =
1571 surface_private_setup,
1572 surface_realize_palette,
1573 surface_map,
1574 surface_unmap,
1577 /*****************************************************************************
1578 * Initializes the GDI surface, aka creates the DIB section we render to
1579 * The DIB section creation is done by calling GetDC, which will create the
1580 * section and releasing the dc to allow the app to use it. The dib section
1581 * will stay until the surface is released
1583 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1584 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1585 * avoid confusion in the shared surface code.
1587 * Returns:
1588 * WINED3D_OK on success
1589 * The return values of called methods on failure
1591 *****************************************************************************/
1592 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1594 HRESULT hr;
1596 TRACE("surface %p.\n", surface);
1598 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1600 ERR("Overlays not yet supported by GDI surfaces.\n");
1601 return WINED3DERR_INVALIDCALL;
1604 /* Sysmem textures have memory already allocated - release it,
1605 * this avoids an unnecessary memcpy. */
1606 hr = surface_create_dib_section(surface);
1607 if (SUCCEEDED(hr))
1609 wined3d_resource_free_sysmem(&surface->resource);
1610 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1613 /* We don't mind the nonpow2 stuff in GDI. */
1614 surface->pow2Width = surface->resource.width;
1615 surface->pow2Height = surface->resource.height;
1617 return WINED3D_OK;
1620 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1622 struct wined3d_palette *palette = surface->palette;
1624 TRACE("surface %p.\n", surface);
1626 if (!palette) return;
1628 if (surface->flags & SFLAG_DIBSECTION)
1630 RGBQUAD col[256];
1631 unsigned int i;
1633 TRACE("Updating the DC's palette.\n");
1635 for (i = 0; i < 256; ++i)
1637 col[i].rgbRed = palette->palents[i].peRed;
1638 col[i].rgbGreen = palette->palents[i].peGreen;
1639 col[i].rgbBlue = palette->palents[i].peBlue;
1640 col[i].rgbReserved = 0;
1642 SetDIBColorTable(surface->hDC, 0, 256, col);
1645 /* Update the image because of the palette change. Some games like e.g.
1646 * Red Alert call SetEntries a lot to implement fading. */
1647 /* Tell the swapchain to update the screen. */
1648 if (surface->swapchain && surface == surface->swapchain->front_buffer)
1649 x11_copy_to_screen(surface->swapchain, NULL);
1652 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
1654 TRACE("surface %p, rect %s, flags %#x.\n",
1655 surface, wine_dbgstr_rect(rect), flags);
1657 if (!(surface->flags & SFLAG_DIBSECTION))
1659 HRESULT hr;
1661 /* This happens on gdi surfaces if the application set a user pointer
1662 * and resets it. Recreate the DIB section. */
1663 if (FAILED(hr = surface_create_dib_section(surface)))
1665 ERR("Failed to create dib section, hr %#x.\n", hr);
1666 return;
1668 wined3d_resource_free_sysmem(&surface->resource);
1669 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1673 static void gdi_surface_unmap(struct wined3d_surface *surface)
1675 TRACE("surface %p.\n", surface);
1677 /* Tell the swapchain to update the screen. */
1678 if (surface->swapchain && surface == surface->swapchain->front_buffer)
1679 x11_copy_to_screen(surface->swapchain, &surface->lockedRect);
1681 memset(&surface->lockedRect, 0, sizeof(RECT));
1684 static const struct wined3d_surface_ops gdi_surface_ops =
1686 gdi_surface_private_setup,
1687 gdi_surface_realize_palette,
1688 gdi_surface_map,
1689 gdi_surface_unmap,
1692 void surface_set_texture_name(struct wined3d_surface *surface, GLuint name, BOOL srgb)
1694 TRACE("surface %p, name %u, srgb %#x.\n", surface, name, srgb);
1696 if (srgb)
1697 surface->texture_name_srgb = name;
1698 else
1699 surface->texture_name = name;
1701 surface_force_reload(surface);
1704 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target, GLint level)
1706 TRACE("surface %p, target %#x.\n", surface, target);
1708 if (surface->texture_target != target)
1710 if (target == GL_TEXTURE_RECTANGLE_ARB)
1712 surface->flags &= ~SFLAG_NORMCOORD;
1714 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
1716 surface->flags |= SFLAG_NORMCOORD;
1719 surface->texture_target = target;
1720 surface->texture_level = level;
1721 surface_force_reload(surface);
1724 /* This call just downloads data, the caller is responsible for binding the
1725 * correct texture. */
1726 /* Context activation is done by the caller. */
1727 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1729 const struct wined3d_format *format = surface->resource.format;
1731 /* Only support read back of converted P8 surfaces. */
1732 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1734 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1735 return;
1738 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1740 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
1741 surface, surface->texture_level, format->glFormat, format->glType,
1742 surface->resource.allocatedMemory);
1744 if (surface->flags & SFLAG_PBO)
1746 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
1747 checkGLcall("glBindBufferARB");
1748 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
1749 checkGLcall("glGetCompressedTexImageARB");
1750 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1751 checkGLcall("glBindBufferARB");
1753 else
1755 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
1756 surface->texture_level, surface->resource.allocatedMemory));
1757 checkGLcall("glGetCompressedTexImageARB");
1760 else
1762 void *mem;
1763 GLenum gl_format = format->glFormat;
1764 GLenum gl_type = format->glType;
1765 int src_pitch = 0;
1766 int dst_pitch = 0;
1768 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
1769 if (format->id == WINED3DFMT_P8_UINT && swapchain_is_p8(surface->resource.device->swapchains[0]))
1771 gl_format = GL_ALPHA;
1772 gl_type = GL_UNSIGNED_BYTE;
1775 if (surface->flags & SFLAG_NONPOW2)
1777 unsigned char alignment = surface->resource.device->surface_alignment;
1778 src_pitch = format->byte_count * surface->pow2Width;
1779 dst_pitch = wined3d_surface_get_pitch(surface);
1780 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1781 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1783 else
1785 mem = surface->resource.allocatedMemory;
1788 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1789 surface, surface->texture_level, gl_format, gl_type, mem);
1791 if (surface->flags & SFLAG_PBO)
1793 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
1794 checkGLcall("glBindBufferARB");
1796 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1797 gl_format, gl_type, NULL);
1798 checkGLcall("glGetTexImage");
1800 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1801 checkGLcall("glBindBufferARB");
1803 else
1805 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1806 gl_format, gl_type, mem);
1807 checkGLcall("glGetTexImage");
1810 if (surface->flags & SFLAG_NONPOW2)
1812 const BYTE *src_data;
1813 BYTE *dst_data;
1814 UINT y;
1816 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1817 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1818 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1820 * We're doing this...
1822 * instead of boxing the texture :
1823 * |<-texture width ->| -->pow2width| /\
1824 * |111111111111111111| | |
1825 * |222 Texture 222222| boxed empty | texture height
1826 * |3333 Data 33333333| | |
1827 * |444444444444444444| | \/
1828 * ----------------------------------- |
1829 * | boxed empty | boxed empty | pow2height
1830 * | | | \/
1831 * -----------------------------------
1834 * we're repacking the data to the expected texture width
1836 * |<-texture width ->| -->pow2width| /\
1837 * |111111111111111111222222222222222| |
1838 * |222333333333333333333444444444444| texture height
1839 * |444444 | |
1840 * | | \/
1841 * | | |
1842 * | empty | pow2height
1843 * | | \/
1844 * -----------------------------------
1846 * == is the same as
1848 * |<-texture width ->| /\
1849 * |111111111111111111|
1850 * |222222222222222222|texture height
1851 * |333333333333333333|
1852 * |444444444444444444| \/
1853 * --------------------
1855 * this also means that any references to allocatedMemory should work with the data as if were a
1856 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
1858 * internally the texture is still stored in a boxed format so any references to textureName will
1859 * get a boxed texture with width pow2width and not a texture of width resource.width.
1861 * Performance should not be an issue, because applications normally do not lock the surfaces when
1862 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
1863 * and doesn't have to be re-read. */
1864 src_data = mem;
1865 dst_data = surface->resource.allocatedMemory;
1866 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1867 for (y = 0; y < surface->resource.height; ++y)
1869 memcpy(dst_data, src_data, dst_pitch);
1870 src_data += src_pitch;
1871 dst_data += dst_pitch;
1874 HeapFree(GetProcessHeap(), 0, mem);
1878 /* Surface has now been downloaded */
1879 surface->flags |= SFLAG_INSYSMEM;
1882 /* This call just uploads data, the caller is responsible for binding the
1883 * correct texture. */
1884 /* Context activation is done by the caller. */
1885 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1886 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1887 BOOL srgb, const struct wined3d_bo_address *data)
1889 UINT update_w = src_rect->right - src_rect->left;
1890 UINT update_h = src_rect->bottom - src_rect->top;
1892 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1893 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1894 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1896 if (surface->resource.map_count)
1898 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
1899 surface->flags |= SFLAG_PIN_SYSMEM;
1902 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1904 update_h *= format->height_scale.numerator;
1905 update_h /= format->height_scale.denominator;
1908 if (data->buffer_object)
1910 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
1911 checkGLcall("glBindBufferARB");
1914 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1916 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1917 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1918 const BYTE *addr = data->addr;
1919 GLenum internal;
1921 addr += (src_rect->top / format->block_height) * src_pitch;
1922 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1924 if (srgb)
1925 internal = format->glGammaInternal;
1926 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
1927 internal = format->rtInternal;
1928 else
1929 internal = format->glInternal;
1931 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
1932 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1933 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1935 if (row_length == src_pitch)
1937 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1938 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1940 else
1942 UINT row, y;
1944 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
1945 * can't use the unpack row length like below. */
1946 for (row = 0, y = dst_point->y; row < row_count; ++row)
1948 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1949 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1950 y += format->block_height;
1951 addr += src_pitch;
1954 checkGLcall("glCompressedTexSubImage2DARB");
1956 else
1958 const BYTE *addr = data->addr;
1960 addr += src_rect->top * src_pitch;
1961 addr += src_rect->left * format->byte_count;
1963 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1964 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1965 update_w, update_h, format->glFormat, format->glType, addr);
1967 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1968 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1969 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1970 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1971 checkGLcall("glTexSubImage2D");
1974 if (data->buffer_object)
1976 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1977 checkGLcall("glBindBufferARB");
1980 if (wined3d_settings.strict_draw_ordering)
1981 gl_info->gl_ops.gl.p_glFlush();
1983 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1985 struct wined3d_device *device = surface->resource.device;
1986 unsigned int i;
1988 for (i = 0; i < device->context_count; ++i)
1990 context_surface_update(device->contexts[i], surface);
1995 static HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck, BOOL use_texturing,
1996 struct wined3d_format *format, enum wined3d_conversion_type *conversion_type)
1998 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
1999 const struct wined3d_device *device = surface->resource.device;
2000 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2001 BOOL blit_supported = FALSE;
2003 /* Copy the default values from the surface. Below we might perform fixups */
2004 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
2005 *format = *surface->resource.format;
2006 *conversion_type = WINED3D_CT_NONE;
2008 /* Ok, now look if we have to do any conversion */
2009 switch (surface->resource.format->id)
2011 case WINED3DFMT_P8_UINT:
2012 /* Below the call to blit_supported is disabled for Wine 1.2
2013 * because the function isn't operating correctly yet. At the
2014 * moment 8-bit blits are handled in software and if certain GL
2015 * extensions are around, surface conversion is performed at
2016 * upload time. The blit_supported call recognizes it as a
2017 * destination fixup. This type of upload 'fixup' and 8-bit to
2018 * 8-bit blits need to be handled by the blit_shader.
2019 * TODO: get rid of this #if 0. */
2020 #if 0
2021 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
2022 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
2023 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
2024 #endif
2025 blit_supported = gl_info->supported[ARB_FRAGMENT_PROGRAM];
2027 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
2028 * texturing. Further also use conversion in case of color keying.
2029 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
2030 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
2031 * conflicts with this.
2033 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
2034 || colorkey_active || !use_texturing)
2036 format->glFormat = GL_RGBA;
2037 format->glInternal = GL_RGBA;
2038 format->glType = GL_UNSIGNED_BYTE;
2039 format->conv_byte_count = 4;
2040 if (colorkey_active)
2041 *conversion_type = WINED3D_CT_PALETTED_CK;
2042 else
2043 *conversion_type = WINED3D_CT_PALETTED;
2045 break;
2047 case WINED3DFMT_B2G3R3_UNORM:
2048 /* **********************
2049 GL_UNSIGNED_BYTE_3_3_2
2050 ********************** */
2051 if (colorkey_active) {
2052 /* This texture format will never be used.. So do not care about color keying
2053 up until the point in time it will be needed :-) */
2054 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
2056 break;
2058 case WINED3DFMT_B5G6R5_UNORM:
2059 if (colorkey_active)
2061 *conversion_type = WINED3D_CT_CK_565;
2062 format->glFormat = GL_RGBA;
2063 format->glInternal = GL_RGB5_A1;
2064 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
2065 format->conv_byte_count = 2;
2067 break;
2069 case WINED3DFMT_B5G5R5X1_UNORM:
2070 if (colorkey_active)
2072 *conversion_type = WINED3D_CT_CK_5551;
2073 format->glFormat = GL_BGRA;
2074 format->glInternal = GL_RGB5_A1;
2075 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
2076 format->conv_byte_count = 2;
2078 break;
2080 case WINED3DFMT_B8G8R8_UNORM:
2081 if (colorkey_active)
2083 *conversion_type = WINED3D_CT_CK_RGB24;
2084 format->glFormat = GL_RGBA;
2085 format->glInternal = GL_RGBA8;
2086 format->glType = GL_UNSIGNED_INT_8_8_8_8;
2087 format->conv_byte_count = 4;
2089 break;
2091 case WINED3DFMT_B8G8R8X8_UNORM:
2092 if (colorkey_active)
2094 *conversion_type = WINED3D_CT_RGB32_888;
2095 format->glFormat = GL_RGBA;
2096 format->glInternal = GL_RGBA8;
2097 format->glType = GL_UNSIGNED_INT_8_8_8_8;
2098 format->conv_byte_count = 4;
2100 break;
2102 case WINED3DFMT_B8G8R8A8_UNORM:
2103 if (colorkey_active)
2105 *conversion_type = WINED3D_CT_CK_ARGB32;
2106 format->conv_byte_count = 4;
2108 break;
2110 default:
2111 break;
2114 if (*conversion_type != WINED3D_CT_NONE)
2116 format->rtInternal = format->glInternal;
2117 format->glGammaInternal = format->glInternal;
2120 return WINED3D_OK;
2123 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
2125 UINT width_mask, height_mask;
2127 if (!rect->left && !rect->top
2128 && rect->right == surface->resource.width
2129 && rect->bottom == surface->resource.height)
2130 return TRUE;
2132 /* This assumes power of two block sizes, but NPOT block sizes would be
2133 * silly anyway. */
2134 width_mask = surface->resource.format->block_width - 1;
2135 height_mask = surface->resource.format->block_height - 1;
2137 if (!(rect->left & width_mask) && !(rect->top & height_mask)
2138 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
2139 return TRUE;
2141 return FALSE;
2144 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
2145 struct wined3d_surface *src_surface, const RECT *src_rect)
2147 const struct wined3d_format *src_format;
2148 const struct wined3d_format *dst_format;
2149 const struct wined3d_gl_info *gl_info;
2150 enum wined3d_conversion_type convert;
2151 struct wined3d_context *context;
2152 struct wined3d_bo_address data;
2153 struct wined3d_format format;
2154 UINT update_w, update_h;
2155 UINT dst_w, dst_h;
2156 RECT r, dst_rect;
2157 UINT src_pitch;
2158 POINT p;
2160 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
2161 dst_surface, wine_dbgstr_point(dst_point),
2162 src_surface, wine_dbgstr_rect(src_rect));
2164 src_format = src_surface->resource.format;
2165 dst_format = dst_surface->resource.format;
2167 if (src_format->id != dst_format->id)
2169 WARN("Source and destination surfaces should have the same format.\n");
2170 return WINED3DERR_INVALIDCALL;
2173 if (!dst_point)
2175 p.x = 0;
2176 p.y = 0;
2177 dst_point = &p;
2179 else if (dst_point->x < 0 || dst_point->y < 0)
2181 WARN("Invalid destination point.\n");
2182 return WINED3DERR_INVALIDCALL;
2185 if (!src_rect)
2187 r.left = 0;
2188 r.top = 0;
2189 r.right = src_surface->resource.width;
2190 r.bottom = src_surface->resource.height;
2191 src_rect = &r;
2193 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
2194 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
2196 WARN("Invalid source rectangle.\n");
2197 return WINED3DERR_INVALIDCALL;
2200 dst_w = dst_surface->resource.width;
2201 dst_h = dst_surface->resource.height;
2203 update_w = src_rect->right - src_rect->left;
2204 update_h = src_rect->bottom - src_rect->top;
2206 if (update_w > dst_w || dst_point->x > dst_w - update_w
2207 || update_h > dst_h || dst_point->y > dst_h - update_h)
2209 WARN("Destination out of bounds.\n");
2210 return WINED3DERR_INVALIDCALL;
2213 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
2215 WARN("Source rectangle not block-aligned.\n");
2216 return WINED3DERR_INVALIDCALL;
2219 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
2220 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
2222 WARN("Destination rectangle not block-aligned.\n");
2223 return WINED3DERR_INVALIDCALL;
2226 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
2227 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
2228 if (convert != WINED3D_CT_NONE || format.convert)
2229 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
2231 context = context_acquire(dst_surface->resource.device, NULL);
2232 gl_info = context->gl_info;
2234 /* Only load the surface for partial updates. For newly allocated texture
2235 * the texture wouldn't be the current location, and we'd upload zeroes
2236 * just to overwrite them again. */
2237 if (update_w == dst_w && update_h == dst_h)
2238 surface_prepare_texture(dst_surface, context, FALSE);
2239 else
2240 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
2241 surface_bind(dst_surface, context, FALSE);
2243 data.buffer_object = src_surface->pbo;
2244 data.addr = src_surface->resource.allocatedMemory;
2245 src_pitch = wined3d_surface_get_pitch(src_surface);
2247 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
2249 context_invalidate_active_texture(context);
2251 context_release(context);
2253 surface_validate_location(dst_surface, SFLAG_INTEXTURE);
2254 surface_invalidate_location(dst_surface, ~SFLAG_INTEXTURE);
2256 return WINED3D_OK;
2259 /* This call just allocates the texture, the caller is responsible for binding
2260 * the correct texture. */
2261 /* Context activation is done by the caller. */
2262 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2263 const struct wined3d_format *format, BOOL srgb)
2265 BOOL disable_client_storage = FALSE;
2266 GLsizei width = surface->pow2Width;
2267 GLsizei height = surface->pow2Height;
2268 const BYTE *mem = NULL;
2269 GLenum internal;
2271 if (srgb)
2273 internal = format->glGammaInternal;
2275 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2277 internal = format->rtInternal;
2279 else
2281 internal = format->glInternal;
2284 if (!internal)
2285 FIXME("No GL internal format for format %s.\n", debug_d3dformat(format->id));
2287 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
2289 height *= format->height_scale.numerator;
2290 height /= format->height_scale.denominator;
2293 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",
2294 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2295 internal, width, height, format->glFormat, format->glType);
2297 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2299 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2300 || !surface->resource.allocatedMemory)
2302 /* In some cases we want to disable client storage.
2303 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2304 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2305 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2306 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2308 surface->flags &= ~SFLAG_CLIENT;
2310 else
2312 surface->flags |= SFLAG_CLIENT;
2314 /* Point OpenGL to our allocated texture memory. Do not use
2315 * resource.allocatedMemory here because it might point into a
2316 * PBO. Instead use heap_memory. */
2317 mem = surface->resource.heap_memory;
2319 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2320 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2321 disable_client_storage = TRUE;
2325 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2327 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2328 internal, width, height, 0, surface->resource.size, mem));
2329 checkGLcall("glCompressedTexImage2DARB");
2331 else
2333 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
2334 internal, width, height, 0, format->glFormat, format->glType, mem);
2335 checkGLcall("glTexImage2D");
2338 if (disable_client_storage)
2340 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2341 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2345 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2346 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2347 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2348 /* Context activation is done by the caller. */
2349 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2351 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2352 struct wined3d_renderbuffer_entry *entry;
2353 GLuint renderbuffer = 0;
2354 unsigned int src_width, src_height;
2355 unsigned int width, height;
2357 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2359 width = rt->pow2Width;
2360 height = rt->pow2Height;
2362 else
2364 width = surface->pow2Width;
2365 height = surface->pow2Height;
2368 src_width = surface->pow2Width;
2369 src_height = surface->pow2Height;
2371 /* A depth stencil smaller than the render target is not valid */
2372 if (width > src_width || height > src_height) return;
2374 /* Remove any renderbuffer set if the sizes match */
2375 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2376 || (width == src_width && height == src_height))
2378 surface->current_renderbuffer = NULL;
2379 return;
2382 /* Look if we've already got a renderbuffer of the correct dimensions */
2383 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2385 if (entry->width == width && entry->height == height)
2387 renderbuffer = entry->id;
2388 surface->current_renderbuffer = entry;
2389 break;
2393 if (!renderbuffer)
2395 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2396 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2397 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2398 surface->resource.format->glInternal, width, height);
2400 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2401 entry->width = width;
2402 entry->height = height;
2403 entry->id = renderbuffer;
2404 list_add_head(&surface->renderbuffers, &entry->entry);
2406 surface->current_renderbuffer = entry;
2409 checkGLcall("set_compatible_renderbuffer");
2412 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2414 const struct wined3d_swapchain *swapchain = surface->swapchain;
2416 TRACE("surface %p.\n", surface);
2418 if (!swapchain)
2420 ERR("Surface %p is not on a swapchain.\n", surface);
2421 return GL_NONE;
2424 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2426 if (swapchain->render_to_fbo)
2428 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2429 return GL_COLOR_ATTACHMENT0;
2431 TRACE("Returning GL_BACK\n");
2432 return GL_BACK;
2434 else if (surface == swapchain->front_buffer)
2436 TRACE("Returning GL_FRONT\n");
2437 return GL_FRONT;
2440 FIXME("Higher back buffer, returning GL_BACK\n");
2441 return GL_BACK;
2444 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2445 void surface_add_dirty_rect(struct wined3d_surface *surface, const struct wined3d_box *dirty_rect)
2447 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2449 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2450 /* No partial locking for textures yet. */
2451 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2453 surface_validate_location(surface, SFLAG_INSYSMEM);
2454 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
2456 if (dirty_rect)
2458 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->left);
2459 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->top);
2460 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->right);
2461 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->bottom);
2463 else
2465 surface->dirtyRect.left = 0;
2466 surface->dirtyRect.top = 0;
2467 surface->dirtyRect.right = surface->resource.width;
2468 surface->dirtyRect.bottom = surface->resource.height;
2471 /* if the container is a texture then mark it dirty. */
2472 if (surface->container)
2474 TRACE("Passing to container.\n");
2475 wined3d_texture_set_dirty(surface->container);
2479 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2481 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2482 BOOL ck_changed;
2484 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2486 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
2488 ERR("Not supported on scratch surfaces.\n");
2489 return WINED3DERR_INVALIDCALL;
2492 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2494 /* Reload if either the texture and sysmem have different ideas about the
2495 * color key, or the actual key values changed. */
2496 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2497 && (surface->gl_color_key.color_space_low_value != surface->src_blt_color_key.color_space_low_value
2498 || surface->gl_color_key.color_space_high_value != surface->src_blt_color_key.color_space_high_value)))
2500 TRACE("Reloading because of color keying\n");
2501 /* To perform the color key conversion we need a sysmem copy of
2502 * the surface. Make sure we have it. */
2504 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2505 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
2506 /* Switching color keying on / off may change the internal format. */
2507 if (ck_changed)
2508 surface_force_reload(surface);
2510 else if (!(surface->flags & flag))
2512 TRACE("Reloading because surface is dirty.\n");
2514 else
2516 TRACE("surface is already in texture\n");
2517 return WINED3D_OK;
2520 /* No partial locking for textures yet. */
2521 surface_load_location(surface, flag, NULL);
2522 surface_evict_sysmem(surface);
2524 return WINED3D_OK;
2527 /* See also float_16_to_32() in wined3d_private.h */
2528 static inline unsigned short float_32_to_16(const float *in)
2530 int exp = 0;
2531 float tmp = fabsf(*in);
2532 unsigned int mantissa;
2533 unsigned short ret;
2535 /* Deal with special numbers */
2536 if (*in == 0.0f)
2537 return 0x0000;
2538 if (isnan(*in))
2539 return 0x7c01;
2540 if (isinf(*in))
2541 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2543 if (tmp < powf(2, 10))
2547 tmp = tmp * 2.0f;
2548 exp--;
2549 } while (tmp < powf(2, 10));
2551 else if (tmp >= powf(2, 11))
2555 tmp /= 2.0f;
2556 exp++;
2557 } while (tmp >= powf(2, 11));
2560 mantissa = (unsigned int)tmp;
2561 if (tmp - mantissa >= 0.5f)
2562 ++mantissa; /* Round to nearest, away from zero. */
2564 exp += 10; /* Normalize the mantissa. */
2565 exp += 15; /* Exponent is encoded with excess 15. */
2567 if (exp > 30) /* too big */
2569 ret = 0x7c00; /* INF */
2571 else if (exp <= 0)
2573 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2574 while (exp <= 0)
2576 mantissa = mantissa >> 1;
2577 ++exp;
2579 ret = mantissa & 0x3ff;
2581 else
2583 ret = (exp << 10) | (mantissa & 0x3ff);
2586 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2587 return ret;
2590 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2592 ULONG refcount;
2594 TRACE("surface %p, swapchain %p, container %p.\n",
2595 surface, surface->swapchain, surface->container);
2597 if (surface->swapchain)
2598 return wined3d_swapchain_incref(surface->swapchain);
2600 if (surface->container)
2601 return wined3d_texture_incref(surface->container);
2603 refcount = InterlockedIncrement(&surface->resource.ref);
2604 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2606 return refcount;
2609 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2611 ULONG refcount;
2613 TRACE("surface %p, swapchain %p, container %p.\n",
2614 surface, surface->swapchain, surface->container);
2616 if (surface->swapchain)
2617 return wined3d_swapchain_decref(surface->swapchain);
2619 if (surface->container)
2620 return wined3d_texture_decref(surface->container);
2622 refcount = InterlockedDecrement(&surface->resource.ref);
2623 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2625 if (!refcount)
2627 surface_cleanup(surface);
2628 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2630 TRACE("Destroyed surface %p.\n", surface);
2631 HeapFree(GetProcessHeap(), 0, surface);
2634 return refcount;
2637 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2639 return resource_set_priority(&surface->resource, priority);
2642 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2644 return resource_get_priority(&surface->resource);
2647 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2649 struct wined3d_context *context;
2650 TRACE("surface %p.\n", surface);
2652 if (!surface->resource.device->d3d_initialized)
2654 ERR("D3D not initialized.\n");
2655 return;
2658 context = context_acquire(surface->resource.device, NULL);
2659 surface_internal_preload(surface, context, SRGB_ANY);
2660 context_release(context);
2663 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2665 TRACE("surface %p.\n", surface);
2667 return surface->resource.parent;
2670 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2672 TRACE("surface %p.\n", surface);
2674 return &surface->resource;
2677 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2679 TRACE("surface %p, flags %#x.\n", surface, flags);
2681 switch (flags)
2683 case WINEDDGBS_CANBLT:
2684 case WINEDDGBS_ISBLTDONE:
2685 return WINED3D_OK;
2687 default:
2688 return WINED3DERR_INVALIDCALL;
2692 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2694 TRACE("surface %p, flags %#x.\n", surface, flags);
2696 /* XXX: DDERR_INVALIDSURFACETYPE */
2698 switch (flags)
2700 case WINEDDGFS_CANFLIP:
2701 case WINEDDGFS_ISFLIPDONE:
2702 return WINED3D_OK;
2704 default:
2705 return WINED3DERR_INVALIDCALL;
2709 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2711 TRACE("surface %p.\n", surface);
2713 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2714 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2717 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2719 TRACE("surface %p.\n", surface);
2721 surface->flags &= ~SFLAG_LOST;
2722 return WINED3D_OK;
2725 void CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2727 TRACE("surface %p, palette %p.\n", surface, palette);
2729 if (surface->palette == palette)
2731 TRACE("Nop palette change.\n");
2732 return;
2735 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2736 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2738 surface->palette = palette;
2740 if (palette)
2742 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2743 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
2745 surface->surface_ops->surface_realize_palette(surface);
2749 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
2750 DWORD flags, const struct wined3d_color_key *color_key)
2752 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
2754 if (flags & WINEDDCKEY_COLORSPACE)
2756 FIXME(" colorkey value not supported (%08x) !\n", flags);
2757 return WINED3DERR_INVALIDCALL;
2760 /* Dirtify the surface, but only if a key was changed. */
2761 if (color_key)
2763 switch (flags & ~WINEDDCKEY_COLORSPACE)
2765 case WINEDDCKEY_DESTBLT:
2766 surface->dst_blt_color_key = *color_key;
2767 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
2768 break;
2770 case WINEDDCKEY_DESTOVERLAY:
2771 surface->dst_overlay_color_key = *color_key;
2772 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
2773 break;
2775 case WINEDDCKEY_SRCOVERLAY:
2776 surface->src_overlay_color_key = *color_key;
2777 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
2778 break;
2780 case WINEDDCKEY_SRCBLT:
2781 surface->src_blt_color_key = *color_key;
2782 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
2783 break;
2786 else
2788 switch (flags & ~WINEDDCKEY_COLORSPACE)
2790 case WINEDDCKEY_DESTBLT:
2791 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
2792 break;
2794 case WINEDDCKEY_DESTOVERLAY:
2795 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
2796 break;
2798 case WINEDDCKEY_SRCOVERLAY:
2799 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
2800 break;
2802 case WINEDDCKEY_SRCBLT:
2803 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
2804 break;
2808 return WINED3D_OK;
2811 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
2813 TRACE("surface %p.\n", surface);
2815 return surface->palette;
2818 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2820 const struct wined3d_format *format = surface->resource.format;
2821 DWORD pitch;
2823 TRACE("surface %p.\n", surface);
2825 if (surface->pitch)
2826 return surface->pitch;
2828 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
2830 /* Since compressed formats are block based, pitch means the amount of
2831 * bytes to the next row of block rather than the next row of pixels. */
2832 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
2833 pitch = row_block_count * format->block_byte_count;
2835 else
2837 unsigned char alignment = surface->resource.device->surface_alignment;
2838 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
2839 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2842 TRACE("Returning %u.\n", pitch);
2844 return pitch;
2847 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem, UINT pitch)
2849 TRACE("surface %p, mem %p.\n", surface, mem);
2851 if (surface->resource.map_count || (surface->flags & SFLAG_DCINUSE))
2853 WARN("Surface is mapped or the DC is in use.\n");
2854 return WINED3DERR_INVALIDCALL;
2857 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
2858 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2860 ERR("Not supported on render targets.\n");
2861 return WINED3DERR_INVALIDCALL;
2864 if (mem && mem != surface->resource.allocatedMemory)
2866 /* Do I have to copy the old surface content? */
2867 if (surface->flags & SFLAG_DIBSECTION)
2869 DeleteDC(surface->hDC);
2870 DeleteObject(surface->dib.DIBsection);
2871 surface->dib.bitmap_data = NULL;
2872 surface->resource.allocatedMemory = NULL;
2873 surface->hDC = NULL;
2874 surface->flags &= ~SFLAG_DIBSECTION;
2876 else if (!(surface->flags & SFLAG_USERPTR))
2878 wined3d_resource_free_sysmem(&surface->resource);
2880 surface->resource.allocatedMemory = mem;
2881 surface->flags |= SFLAG_USERPTR;
2883 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
2884 surface_validate_location(surface, SFLAG_INSYSMEM);
2885 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
2887 /* For client textures OpenGL has to be notified. */
2888 if (surface->flags & SFLAG_CLIENT)
2889 surface_release_client_storage(surface);
2891 else if (surface->flags & SFLAG_USERPTR)
2893 /* heap_memory should be NULL already. */
2894 if (surface->resource.heap_memory)
2895 ERR("User pointer surface has heap memory allocated.\n");
2897 if (!mem)
2899 surface->resource.allocatedMemory = NULL;
2900 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
2902 if (surface->flags & SFLAG_CLIENT)
2903 surface_release_client_storage(surface);
2905 surface_prepare_system_memory(surface);
2908 surface_validate_location(surface, SFLAG_INSYSMEM);
2909 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
2912 surface->pitch = pitch;
2914 return WINED3D_OK;
2917 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2919 LONG w, h;
2921 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2923 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2925 WARN("Not an overlay surface.\n");
2926 return WINEDDERR_NOTAOVERLAYSURFACE;
2929 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2930 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2931 surface->overlay_destrect.left = x;
2932 surface->overlay_destrect.top = y;
2933 surface->overlay_destrect.right = x + w;
2934 surface->overlay_destrect.bottom = y + h;
2936 surface_draw_overlay(surface);
2938 return WINED3D_OK;
2941 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2943 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2945 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2947 TRACE("Not an overlay surface.\n");
2948 return WINEDDERR_NOTAOVERLAYSURFACE;
2951 if (!surface->overlay_dest)
2953 TRACE("Overlay not visible.\n");
2954 *x = 0;
2955 *y = 0;
2956 return WINEDDERR_OVERLAYNOTVISIBLE;
2959 *x = surface->overlay_destrect.left;
2960 *y = surface->overlay_destrect.top;
2962 TRACE("Returning position %d, %d.\n", *x, *y);
2964 return WINED3D_OK;
2967 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2968 DWORD flags, struct wined3d_surface *ref)
2970 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2972 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2974 TRACE("Not an overlay surface.\n");
2975 return WINEDDERR_NOTAOVERLAYSURFACE;
2978 return WINED3D_OK;
2981 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2982 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2984 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2985 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2987 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2989 WARN("Not an overlay surface.\n");
2990 return WINEDDERR_NOTAOVERLAYSURFACE;
2992 else if (!dst_surface)
2994 WARN("Dest surface is NULL.\n");
2995 return WINED3DERR_INVALIDCALL;
2998 if (src_rect)
3000 surface->overlay_srcrect = *src_rect;
3002 else
3004 surface->overlay_srcrect.left = 0;
3005 surface->overlay_srcrect.top = 0;
3006 surface->overlay_srcrect.right = surface->resource.width;
3007 surface->overlay_srcrect.bottom = surface->resource.height;
3010 if (dst_rect)
3012 surface->overlay_destrect = *dst_rect;
3014 else
3016 surface->overlay_destrect.left = 0;
3017 surface->overlay_destrect.top = 0;
3018 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3019 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3022 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3024 surface->overlay_dest = NULL;
3025 list_remove(&surface->overlay_entry);
3028 if (flags & WINEDDOVER_SHOW)
3030 if (surface->overlay_dest != dst_surface)
3032 surface->overlay_dest = dst_surface;
3033 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3036 else if (flags & WINEDDOVER_HIDE)
3038 /* tests show that the rectangles are erased on hide */
3039 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3040 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3041 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3042 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3043 surface->overlay_dest = NULL;
3046 surface_draw_overlay(surface);
3048 return WINED3D_OK;
3051 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
3052 UINT width, UINT height, enum wined3d_format_id format_id,
3053 enum wined3d_multisample_type multisample_type, UINT multisample_quality)
3055 struct wined3d_device *device = surface->resource.device;
3056 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3057 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
3058 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height, 1);
3060 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u.\n",
3061 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type);
3063 if (!resource_size)
3064 return WINED3DERR_INVALIDCALL;
3066 if (device->d3d_initialized)
3067 surface->resource.resource_ops->resource_unload(&surface->resource);
3069 if (surface->flags & SFLAG_DIBSECTION)
3071 DeleteDC(surface->hDC);
3072 DeleteObject(surface->dib.DIBsection);
3073 surface->dib.bitmap_data = NULL;
3074 surface->flags &= ~SFLAG_DIBSECTION;
3077 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_USERPTR);
3078 surface->resource.allocatedMemory = NULL;
3079 wined3d_resource_free_sysmem(&surface->resource);
3081 surface->resource.width = width;
3082 surface->resource.height = height;
3083 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
3084 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
3086 surface->pow2Width = width;
3087 surface->pow2Height = height;
3089 else
3091 surface->pow2Width = surface->pow2Height = 1;
3092 while (surface->pow2Width < width)
3093 surface->pow2Width <<= 1;
3094 while (surface->pow2Height < height)
3095 surface->pow2Height <<= 1;
3098 if (surface->pow2Width != width || surface->pow2Height != height)
3099 surface->flags |= SFLAG_NONPOW2;
3100 else
3101 surface->flags &= ~SFLAG_NONPOW2;
3103 surface->resource.format = format;
3104 surface->resource.multisample_type = multisample_type;
3105 surface->resource.multisample_quality = multisample_quality;
3106 surface->resource.size = resource_size;
3108 if (!surface_init_sysmem(surface))
3109 return E_OUTOFMEMORY;
3111 return WINED3D_OK;
3114 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3115 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3117 unsigned short *dst_s;
3118 const float *src_f;
3119 unsigned int x, y;
3121 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3123 for (y = 0; y < h; ++y)
3125 src_f = (const float *)(src + y * pitch_in);
3126 dst_s = (unsigned short *) (dst + y * pitch_out);
3127 for (x = 0; x < w; ++x)
3129 dst_s[x] = float_32_to_16(src_f + x);
3134 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3135 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3137 static const unsigned char convert_5to8[] =
3139 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3140 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3141 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3142 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3144 static const unsigned char convert_6to8[] =
3146 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3147 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3148 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3149 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3150 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3151 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3152 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3153 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3155 unsigned int x, y;
3157 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3159 for (y = 0; y < h; ++y)
3161 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3162 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3163 for (x = 0; x < w; ++x)
3165 WORD pixel = src_line[x];
3166 dst_line[x] = 0xff000000
3167 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3168 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3169 | convert_5to8[(pixel & 0x001f)];
3174 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3175 * in both cases we're just setting the X / Alpha channel to 0xff. */
3176 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3177 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3179 unsigned int x, y;
3181 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3183 for (y = 0; y < h; ++y)
3185 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3186 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3188 for (x = 0; x < w; ++x)
3190 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3195 static inline BYTE cliptobyte(int x)
3197 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3200 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3201 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3203 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3204 unsigned int x, y;
3206 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3208 for (y = 0; y < h; ++y)
3210 const BYTE *src_line = src + y * pitch_in;
3211 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3212 for (x = 0; x < w; ++x)
3214 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3215 * C = Y - 16; D = U - 128; E = V - 128;
3216 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3217 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3218 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3219 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3220 * U and V are shared between the pixels. */
3221 if (!(x & 1)) /* For every even pixel, read new U and V. */
3223 d = (int) src_line[1] - 128;
3224 e = (int) src_line[3] - 128;
3225 r2 = 409 * e + 128;
3226 g2 = - 100 * d - 208 * e + 128;
3227 b2 = 516 * d + 128;
3229 c2 = 298 * ((int) src_line[0] - 16);
3230 dst_line[x] = 0xff000000
3231 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3232 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3233 | cliptobyte((c2 + b2) >> 8); /* blue */
3234 /* Scale RGB values to 0..255 range,
3235 * then clip them if still not in range (may be negative),
3236 * then shift them within DWORD if necessary. */
3237 src_line += 2;
3242 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3243 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3245 unsigned int x, y;
3246 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3248 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3250 for (y = 0; y < h; ++y)
3252 const BYTE *src_line = src + y * pitch_in;
3253 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3254 for (x = 0; x < w; ++x)
3256 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3257 * C = Y - 16; D = U - 128; E = V - 128;
3258 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3259 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3260 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3261 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3262 * U and V are shared between the pixels. */
3263 if (!(x & 1)) /* For every even pixel, read new U and V. */
3265 d = (int) src_line[1] - 128;
3266 e = (int) src_line[3] - 128;
3267 r2 = 409 * e + 128;
3268 g2 = - 100 * d - 208 * e + 128;
3269 b2 = 516 * d + 128;
3271 c2 = 298 * ((int) src_line[0] - 16);
3272 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3273 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3274 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3275 /* Scale RGB values to 0..255 range,
3276 * then clip them if still not in range (may be negative),
3277 * then shift them within DWORD if necessary. */
3278 src_line += 2;
3283 struct d3dfmt_converter_desc
3285 enum wined3d_format_id from, to;
3286 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3289 static const struct d3dfmt_converter_desc converters[] =
3291 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3292 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3293 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3294 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3295 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3296 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3299 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
3300 enum wined3d_format_id to)
3302 unsigned int i;
3304 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
3306 if (converters[i].from == from && converters[i].to == to)
3307 return &converters[i];
3310 return NULL;
3313 /*****************************************************************************
3314 * surface_convert_format
3316 * Creates a duplicate of a surface in a different format. Is used by Blt to
3317 * blit between surfaces with different formats.
3319 * Parameters
3320 * source: Source surface
3321 * fmt: Requested destination format
3323 *****************************************************************************/
3324 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3326 struct wined3d_map_desc src_map, dst_map;
3327 const struct d3dfmt_converter_desc *conv;
3328 struct wined3d_surface *ret = NULL;
3329 HRESULT hr;
3331 conv = find_converter(source->resource.format->id, to_fmt);
3332 if (!conv)
3334 FIXME("Cannot find a conversion function from format %s to %s.\n",
3335 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3336 return NULL;
3339 /* FIXME: Multisampled conversion? */
3340 if (FAILED(hr = wined3d_surface_create(source->resource.device, source->resource.width, source->resource.height,
3341 to_fmt, 0, WINED3D_POOL_SCRATCH, WINED3D_MULTISAMPLE_NONE, 0,
3342 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, &wined3d_null_parent_ops, &ret)))
3344 ERR("Failed to create a destination surface for conversion.\n");
3345 return NULL;
3348 memset(&src_map, 0, sizeof(src_map));
3349 memset(&dst_map, 0, sizeof(dst_map));
3351 if (FAILED(hr = wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
3353 ERR("Failed to lock the source surface.\n");
3354 wined3d_surface_decref(ret);
3355 return NULL;
3357 if (FAILED(hr = wined3d_surface_map(ret, &dst_map, NULL, WINED3D_MAP_READONLY)))
3359 ERR("Failed to lock the destination surface.\n");
3360 wined3d_surface_unmap(source);
3361 wined3d_surface_decref(ret);
3362 return NULL;
3365 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
3366 source->resource.width, source->resource.height);
3368 wined3d_surface_unmap(ret);
3369 wined3d_surface_unmap(source);
3371 return ret;
3374 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3375 unsigned int bpp, UINT pitch, DWORD color)
3377 BYTE *first;
3378 unsigned int x, y;
3380 /* Do first row */
3382 #define COLORFILL_ROW(type) \
3383 do { \
3384 type *d = (type *)buf; \
3385 for (x = 0; x < width; ++x) \
3386 d[x] = (type)color; \
3387 } while(0)
3389 switch (bpp)
3391 case 1:
3392 COLORFILL_ROW(BYTE);
3393 break;
3395 case 2:
3396 COLORFILL_ROW(WORD);
3397 break;
3399 case 3:
3401 BYTE *d = buf;
3402 for (x = 0; x < width; ++x, d += 3)
3404 d[0] = (color ) & 0xff;
3405 d[1] = (color >> 8) & 0xff;
3406 d[2] = (color >> 16) & 0xff;
3408 break;
3410 case 4:
3411 COLORFILL_ROW(DWORD);
3412 break;
3414 default:
3415 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3416 return WINED3DERR_NOTAVAILABLE;
3419 #undef COLORFILL_ROW
3421 /* Now copy first row. */
3422 first = buf;
3423 for (y = 1; y < height; ++y)
3425 buf += pitch;
3426 memcpy(buf, first, width * bpp);
3429 return WINED3D_OK;
3432 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
3434 return surface_from_resource(resource);
3437 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3439 TRACE("surface %p.\n", surface);
3441 if (!surface->resource.map_count)
3443 WARN("Trying to unmap unmapped surface.\n");
3444 return WINEDDERR_NOTLOCKED;
3446 --surface->resource.map_count;
3448 surface->surface_ops->surface_unmap(surface);
3450 return WINED3D_OK;
3453 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3454 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
3456 const struct wined3d_format *format = surface->resource.format;
3458 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
3459 surface, map_desc, wine_dbgstr_rect(rect), flags);
3461 if (surface->resource.map_count)
3463 WARN("Surface is already mapped.\n");
3464 return WINED3DERR_INVALIDCALL;
3467 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
3468 && !surface_check_block_align(surface, rect))
3470 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3471 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3473 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
3474 return WINED3DERR_INVALIDCALL;
3477 ++surface->resource.map_count;
3479 if (!(surface->flags & SFLAG_LOCKABLE))
3480 WARN("Trying to lock unlockable surface.\n");
3482 /* Performance optimization: Count how often a surface is mapped, if it is
3483 * mapped regularly do not throw away the system memory copy. This avoids
3484 * the need to download the surface from OpenGL all the time. The surface
3485 * is still downloaded if the OpenGL texture is changed. */
3486 if (!(surface->flags & SFLAG_DYNLOCK))
3488 if (++surface->lockCount > MAXLOCKCOUNT)
3490 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3491 surface->flags |= SFLAG_DYNLOCK;
3495 surface->surface_ops->surface_map(surface, rect, flags);
3497 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3498 map_desc->row_pitch = surface->resource.width * format->byte_count;
3499 else
3500 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
3501 map_desc->slice_pitch = 0;
3503 if (!rect)
3505 map_desc->data = surface->resource.allocatedMemory;
3506 surface->lockedRect.left = 0;
3507 surface->lockedRect.top = 0;
3508 surface->lockedRect.right = surface->resource.width;
3509 surface->lockedRect.bottom = surface->resource.height;
3511 else
3513 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3515 /* Compressed textures are block based, so calculate the offset of
3516 * the block that contains the top-left pixel of the locked rectangle. */
3517 map_desc->data = surface->resource.allocatedMemory
3518 + ((rect->top / format->block_height) * map_desc->row_pitch)
3519 + ((rect->left / format->block_width) * format->block_byte_count);
3521 else
3523 map_desc->data = surface->resource.allocatedMemory
3524 + (map_desc->row_pitch * rect->top)
3525 + (rect->left * format->byte_count);
3527 surface->lockedRect.left = rect->left;
3528 surface->lockedRect.top = rect->top;
3529 surface->lockedRect.right = rect->right;
3530 surface->lockedRect.bottom = rect->bottom;
3533 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3534 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
3536 return WINED3D_OK;
3539 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3541 struct wined3d_map_desc map;
3542 HRESULT hr;
3544 TRACE("surface %p, dc %p.\n", surface, dc);
3546 if (surface->flags & SFLAG_USERPTR)
3548 ERR("Not supported on surfaces with application-provided memory.\n");
3549 return WINEDDERR_NODC;
3552 /* Give more detailed info for ddraw. */
3553 if (surface->flags & SFLAG_DCINUSE)
3554 return WINEDDERR_DCALREADYCREATED;
3556 /* Can't GetDC if the surface is locked. */
3557 if (surface->resource.map_count)
3558 return WINED3DERR_INVALIDCALL;
3560 /* Create a DIB section if there isn't a dc yet. */
3561 if (!surface->hDC)
3563 if (surface->flags & SFLAG_CLIENT)
3565 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3566 surface_release_client_storage(surface);
3568 hr = surface_create_dib_section(surface);
3569 if (FAILED(hr))
3570 return WINED3DERR_INVALIDCALL;
3572 /* Use the DIB section from now on if we are not using a PBO. */
3573 if (!(surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)))
3575 wined3d_resource_free_sysmem(&surface->resource);
3576 surface->resource.allocatedMemory = surface->dib.bitmap_data;
3580 /* Map the surface. */
3581 hr = wined3d_surface_map(surface, &map, NULL, 0);
3582 if (FAILED(hr))
3584 ERR("Map failed, hr %#x.\n", hr);
3585 return hr;
3588 /* Sync the DIB with the PBO. This can't be done earlier because Map()
3589 * activates the allocatedMemory. */
3590 if (surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM))
3591 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
3593 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3594 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3596 /* GetDC on palettized formats is unsupported in D3D9, and the method
3597 * is missing in D3D8, so this should only be used for DX <=7
3598 * surfaces (with non-device palettes). */
3599 const PALETTEENTRY *pal = NULL;
3601 if (surface->palette)
3603 pal = surface->palette->palents;
3605 else
3607 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3608 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3610 if (dds_primary && dds_primary->palette)
3611 pal = dds_primary->palette->palents;
3614 if (pal)
3616 RGBQUAD col[256];
3617 unsigned int i;
3619 for (i = 0; i < 256; ++i)
3621 col[i].rgbRed = pal[i].peRed;
3622 col[i].rgbGreen = pal[i].peGreen;
3623 col[i].rgbBlue = pal[i].peBlue;
3624 col[i].rgbReserved = 0;
3626 SetDIBColorTable(surface->hDC, 0, 256, col);
3630 surface->flags |= SFLAG_DCINUSE;
3632 *dc = surface->hDC;
3633 TRACE("Returning dc %p.\n", *dc);
3635 return WINED3D_OK;
3638 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3640 TRACE("surface %p, dc %p.\n", surface, dc);
3642 if (!(surface->flags & SFLAG_DCINUSE))
3643 return WINEDDERR_NODC;
3645 if (surface->hDC != dc)
3647 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3648 dc, surface->hDC);
3649 return WINEDDERR_NODC;
3652 /* Copy the contents of the DIB over to the PBO. */
3653 if ((surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)) && surface->resource.allocatedMemory)
3654 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3656 /* We locked first, so unlock now. */
3657 wined3d_surface_unmap(surface);
3659 surface->flags &= ~SFLAG_DCINUSE;
3661 return WINED3D_OK;
3664 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3666 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3668 if (flags)
3670 static UINT once;
3671 if (!once++)
3672 FIXME("Ignoring flags %#x.\n", flags);
3673 else
3674 WARN("Ignoring flags %#x.\n", flags);
3677 if (surface->swapchain)
3679 ERR("Not supported on swapchain surfaces.\n");
3680 return WINEDDERR_NOTFLIPPABLE;
3683 flip_surface(surface, override);
3685 /* Update overlays if they're visible. */
3686 if ((surface->resource.usage & WINED3DUSAGE_OVERLAY) && surface->overlay_dest)
3687 return surface_draw_overlay(surface);
3689 return WINED3D_OK;
3692 /* Context activation is done by the caller */
3693 void surface_internal_preload(struct wined3d_surface *surface,
3694 struct wined3d_context *context, enum WINED3DSRGB srgb)
3696 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3698 if (surface->container)
3700 struct wined3d_texture *texture = surface->container;
3702 TRACE("Passing to container (%p).\n", texture);
3703 texture->texture_ops->texture_preload(texture, context, srgb);
3705 else
3707 TRACE("(%p) : About to load surface\n", surface);
3709 surface_load(surface, srgb == SRGB_SRGB);
3711 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
3713 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3714 GLclampf tmp;
3715 tmp = 0.9f;
3716 context->gl_info->gl_ops.gl.p_glPrioritizeTextures(1, &surface->texture_name, &tmp);
3721 /* Read the framebuffer back into the surface */
3722 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
3724 struct wined3d_device *device = surface->resource.device;
3725 const struct wined3d_gl_info *gl_info;
3726 struct wined3d_context *context;
3727 BYTE *mem;
3728 GLint fmt;
3729 GLint type;
3730 BYTE *row, *top, *bottom;
3731 int i;
3732 BOOL bpp;
3733 RECT local_rect;
3734 BOOL srcIsUpsideDown;
3735 GLint rowLen = 0;
3736 GLint skipPix = 0;
3737 GLint skipRow = 0;
3739 context = context_acquire(device, surface);
3740 context_apply_blit_state(context, device);
3741 gl_info = context->gl_info;
3743 /* Select the correct read buffer, and give some debug output.
3744 * There is no need to keep track of the current read buffer or reset it, every part of the code
3745 * that reads sets the read buffer as desired.
3747 if (surface_is_offscreen(surface))
3749 /* Mapping the primary render target which is not on a swapchain.
3750 * Read from the back buffer. */
3751 TRACE("Mapping offscreen render target.\n");
3752 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3753 srcIsUpsideDown = TRUE;
3755 else
3757 /* Onscreen surfaces are always part of a swapchain */
3758 GLenum buffer = surface_get_gl_buffer(surface);
3759 TRACE("Mapping %#x buffer.\n", buffer);
3760 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
3761 checkGLcall("glReadBuffer");
3762 srcIsUpsideDown = FALSE;
3765 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
3766 if (!rect)
3768 local_rect.left = 0;
3769 local_rect.top = 0;
3770 local_rect.right = surface->resource.width;
3771 local_rect.bottom = surface->resource.height;
3773 else
3775 local_rect = *rect;
3777 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
3779 switch (surface->resource.format->id)
3781 case WINED3DFMT_P8_UINT:
3783 if (swapchain_is_p8(context->swapchain))
3785 /* In case of P8 render targets the index is stored in the alpha component */
3786 fmt = GL_ALPHA;
3787 type = GL_UNSIGNED_BYTE;
3788 mem = dest;
3789 bpp = surface->resource.format->byte_count;
3791 else
3793 /* GL can't return palettized data, so read ARGB pixels into a
3794 * separate block of memory and convert them into palettized format
3795 * in software. Slow, but if the app means to use palettized render
3796 * targets and locks it...
3798 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
3799 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
3800 * for the color channels when palettizing the colors.
3802 fmt = GL_RGB;
3803 type = GL_UNSIGNED_BYTE;
3804 pitch *= 3;
3805 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
3806 if (!mem)
3808 ERR("Out of memory\n");
3809 return;
3811 bpp = surface->resource.format->byte_count * 3;
3814 break;
3816 default:
3817 mem = dest;
3818 fmt = surface->resource.format->glFormat;
3819 type = surface->resource.format->glType;
3820 bpp = surface->resource.format->byte_count;
3823 if (surface->flags & SFLAG_PBO)
3825 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
3826 checkGLcall("glBindBufferARB");
3827 if (mem)
3829 ERR("mem not null for pbo -- unexpected\n");
3830 mem = NULL;
3834 /* Save old pixel store pack state */
3835 gl_info->gl_ops.gl.p_glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
3836 checkGLcall("glGetIntegerv");
3837 gl_info->gl_ops.gl.p_glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
3838 checkGLcall("glGetIntegerv");
3839 gl_info->gl_ops.gl.p_glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
3840 checkGLcall("glGetIntegerv");
3842 /* Setup pixel store pack state -- to glReadPixels into the correct place */
3843 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
3844 checkGLcall("glPixelStorei");
3845 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
3846 checkGLcall("glPixelStorei");
3847 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
3848 checkGLcall("glPixelStorei");
3850 gl_info->gl_ops.gl.p_glReadPixels(local_rect.left,
3851 !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
3852 local_rect.right - local_rect.left,
3853 local_rect.bottom - local_rect.top,
3854 fmt, type, mem);
3855 checkGLcall("glReadPixels");
3857 /* Reset previous pixel store pack state */
3858 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
3859 checkGLcall("glPixelStorei");
3860 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
3861 checkGLcall("glPixelStorei");
3862 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
3863 checkGLcall("glPixelStorei");
3865 if (surface->flags & SFLAG_PBO)
3867 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
3868 checkGLcall("glBindBufferARB");
3870 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
3871 * to get a pointer to it and perform the flipping in software. This is a lot
3872 * faster than calling glReadPixels for each line. In case we want more speed
3873 * we should rerender it flipped in a FBO and read the data back from the FBO. */
3874 if (!srcIsUpsideDown)
3876 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
3877 checkGLcall("glBindBufferARB");
3879 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3880 checkGLcall("glMapBufferARB");
3884 /* TODO: Merge this with the palettization loop below for P8 targets */
3885 if(!srcIsUpsideDown) {
3886 UINT len, off;
3887 /* glReadPixels returns the image upside down, and there is no way to prevent this.
3888 Flip the lines in software */
3889 len = (local_rect.right - local_rect.left) * bpp;
3890 off = local_rect.left * bpp;
3892 row = HeapAlloc(GetProcessHeap(), 0, len);
3893 if(!row) {
3894 ERR("Out of memory\n");
3895 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
3896 HeapFree(GetProcessHeap(), 0, mem);
3897 return;
3900 top = mem + pitch * local_rect.top;
3901 bottom = mem + pitch * (local_rect.bottom - 1);
3902 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
3903 memcpy(row, top + off, len);
3904 memcpy(top + off, bottom + off, len);
3905 memcpy(bottom + off, row, len);
3906 top += pitch;
3907 bottom -= pitch;
3909 HeapFree(GetProcessHeap(), 0, row);
3911 /* Unmap the temp PBO buffer */
3912 if (surface->flags & SFLAG_PBO)
3914 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
3915 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
3919 /* For P8 textures we need to perform an inverse palette lookup. This is
3920 * done by searching for a palette index which matches the RGB value.
3921 * Note this isn't guaranteed to work when there are multiple entries for
3922 * the same color but we have no choice. In case of P8 render targets,
3923 * the index is stored in the alpha component so no conversion is needed. */
3924 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !swapchain_is_p8(context->swapchain))
3926 const PALETTEENTRY *pal = NULL;
3927 DWORD width = pitch / 3;
3928 int x, y, c;
3930 if (surface->palette)
3932 pal = surface->palette->palents;
3934 else
3936 ERR("Palette is missing, cannot perform inverse palette lookup\n");
3937 HeapFree(GetProcessHeap(), 0, mem);
3938 return;
3941 for(y = local_rect.top; y < local_rect.bottom; y++) {
3942 for(x = local_rect.left; x < local_rect.right; x++) {
3943 /* start lines pixels */
3944 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
3945 const BYTE *green = blue + 1;
3946 const BYTE *red = green + 1;
3948 for(c = 0; c < 256; c++) {
3949 if(*red == pal[c].peRed &&
3950 *green == pal[c].peGreen &&
3951 *blue == pal[c].peBlue)
3953 *((BYTE *) dest + y * width + x) = c;
3954 break;
3959 HeapFree(GetProcessHeap(), 0, mem);
3962 context_release(context);
3965 /* Read the framebuffer contents into a texture. Note that this function
3966 * doesn't do any kind of flipping. Using this on an onscreen surface will
3967 * result in a flipped D3D texture. */
3968 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
3970 struct wined3d_device *device = surface->resource.device;
3971 const struct wined3d_gl_info *gl_info;
3972 struct wined3d_context *context;
3974 context = context_acquire(device, surface);
3975 gl_info = context->gl_info;
3976 device_invalidate_state(device, STATE_FRAMEBUFFER);
3978 surface_prepare_texture(surface, context, srgb);
3979 surface_bind_and_dirtify(surface, context, srgb);
3981 TRACE("Reading back offscreen render target %p.\n", surface);
3983 if (surface_is_offscreen(surface))
3984 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3985 else
3986 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
3987 checkGLcall("glReadBuffer");
3989 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
3990 0, 0, 0, 0, surface->resource.width, surface->resource.height);
3991 checkGLcall("glCopyTexSubImage2D");
3993 context_release(context);
3996 /* Context activation is done by the caller. */
3997 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
3998 struct wined3d_context *context, BOOL srgb)
4000 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4001 enum wined3d_conversion_type convert;
4002 struct wined3d_format format;
4004 if (surface->flags & alloc_flag) return;
4006 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4007 if (convert != WINED3D_CT_NONE || format.convert)
4008 surface->flags |= SFLAG_CONVERTED;
4009 else surface->flags &= ~SFLAG_CONVERTED;
4011 surface_bind_and_dirtify(surface, context, srgb);
4012 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4013 surface->flags |= alloc_flag;
4016 /* Context activation is done by the caller. */
4017 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4019 if (surface->container)
4021 struct wined3d_texture *texture = surface->container;
4022 UINT sub_count = texture->level_count * texture->layer_count;
4023 UINT i;
4025 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4027 for (i = 0; i < sub_count; ++i)
4029 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4030 surface_prepare_texture_internal(s, context, srgb);
4033 return;
4036 surface_prepare_texture_internal(surface, context, srgb);
4039 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4041 if (multisample)
4043 if (surface->rb_multisample)
4044 return;
4046 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4047 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4048 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4049 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4050 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4052 else
4054 if (surface->rb_resolved)
4055 return;
4057 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4058 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4059 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4060 surface->pow2Width, surface->pow2Height);
4061 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4065 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
4067 /* FIXME: Is this really how color keys are supposed to work? I think it
4068 * makes more sense to compare the individual channels. */
4069 return color >= color_key->color_space_low_value
4070 && color <= color_key->color_space_high_value;
4073 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4075 const struct wined3d_device *device = surface->resource.device;
4076 const struct wined3d_palette *pal = surface->palette;
4077 BOOL index_in_alpha = FALSE;
4078 unsigned int i;
4080 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4081 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4082 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4083 * duplicate entries. Store the color key in the unused alpha component to speed the
4084 * download up and to make conversion unneeded. */
4085 index_in_alpha = swapchain_is_p8(device->swapchains[0]);
4087 if (!pal)
4089 FIXME("No palette set.\n");
4090 if (index_in_alpha)
4092 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4093 * there's no palette at this time. */
4094 for (i = 0; i < 256; i++) table[i][3] = i;
4097 else
4099 TRACE("Using surface palette %p\n", pal);
4100 /* Get the surface's palette */
4101 for (i = 0; i < 256; ++i)
4103 table[i][0] = pal->palents[i].peRed;
4104 table[i][1] = pal->palents[i].peGreen;
4105 table[i][2] = pal->palents[i].peBlue;
4107 /* When index_in_alpha is set the palette index is stored in the
4108 * alpha component. In case of a readback we can then read
4109 * GL_ALPHA. Color keying is handled in surface_blt_special() using a
4110 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4111 * color key itself is passed to glAlphaFunc in other cases the
4112 * alpha component of pixels that should be masked away is set to 0. */
4113 if (index_in_alpha)
4114 table[i][3] = i;
4115 else if (colorkey && color_in_range(&surface->src_blt_color_key, i))
4116 table[i][3] = 0x00;
4117 else if (pal->flags & WINEDDPCAPS_ALPHA)
4118 table[i][3] = pal->palents[i].peFlags;
4119 else
4120 table[i][3] = 0xff;
4125 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
4126 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
4128 const BYTE *source;
4129 BYTE *dest;
4131 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
4132 src, dst, pitch, width, height, outpitch, conversion_type, surface);
4134 switch (conversion_type)
4136 case WINED3D_CT_NONE:
4138 memcpy(dst, src, pitch * height);
4139 break;
4142 case WINED3D_CT_PALETTED:
4143 case WINED3D_CT_PALETTED_CK:
4145 BYTE table[256][4];
4146 unsigned int x, y;
4148 d3dfmt_p8_init_palette(surface, table, (conversion_type == WINED3D_CT_PALETTED_CK));
4150 for (y = 0; y < height; y++)
4152 source = src + pitch * y;
4153 dest = dst + outpitch * y;
4154 /* This is an 1 bpp format, using the width here is fine */
4155 for (x = 0; x < width; x++) {
4156 BYTE color = *source++;
4157 *dest++ = table[color][0];
4158 *dest++ = table[color][1];
4159 *dest++ = table[color][2];
4160 *dest++ = table[color][3];
4164 break;
4166 case WINED3D_CT_CK_565:
4168 /* Converting the 565 format in 5551 packed to emulate color-keying.
4170 Note : in all these conversion, it would be best to average the averaging
4171 pixels to get the color of the pixel that will be color-keyed to
4172 prevent 'color bleeding'. This will be done later on if ever it is
4173 too visible.
4175 Note2: Nvidia documents say that their driver does not support alpha + color keying
4176 on the same surface and disables color keying in such a case
4178 unsigned int x, y;
4179 const WORD *Source;
4180 WORD *Dest;
4182 TRACE("Color keyed 565\n");
4184 for (y = 0; y < height; y++) {
4185 Source = (const WORD *)(src + y * pitch);
4186 Dest = (WORD *) (dst + y * outpitch);
4187 for (x = 0; x < width; x++ ) {
4188 WORD color = *Source++;
4189 *Dest = ((color & 0xffc0) | ((color & 0x1f) << 1));
4190 if (!color_in_range(&surface->src_blt_color_key, color))
4191 *Dest |= 0x0001;
4192 Dest++;
4196 break;
4198 case WINED3D_CT_CK_5551:
4200 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4201 unsigned int x, y;
4202 const WORD *Source;
4203 WORD *Dest;
4204 TRACE("Color keyed 5551\n");
4205 for (y = 0; y < height; y++) {
4206 Source = (const WORD *)(src + y * pitch);
4207 Dest = (WORD *) (dst + y * outpitch);
4208 for (x = 0; x < width; x++ ) {
4209 WORD color = *Source++;
4210 *Dest = color;
4211 if (!color_in_range(&surface->src_blt_color_key, color))
4212 *Dest |= (1 << 15);
4213 else
4214 *Dest &= ~(1 << 15);
4215 Dest++;
4219 break;
4221 case WINED3D_CT_CK_RGB24:
4223 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4224 unsigned int x, y;
4225 for (y = 0; y < height; y++)
4227 source = src + pitch * y;
4228 dest = dst + outpitch * y;
4229 for (x = 0; x < width; x++) {
4230 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4231 DWORD dstcolor = color << 8;
4232 if (!color_in_range(&surface->src_blt_color_key, color))
4233 dstcolor |= 0xff;
4234 *(DWORD*)dest = dstcolor;
4235 source += 3;
4236 dest += 4;
4240 break;
4242 case WINED3D_CT_RGB32_888:
4244 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4245 unsigned int x, y;
4246 for (y = 0; y < height; y++)
4248 source = src + pitch * y;
4249 dest = dst + outpitch * y;
4250 for (x = 0; x < width; x++) {
4251 DWORD color = 0xffffff & *(const DWORD*)source;
4252 DWORD dstcolor = color << 8;
4253 if (!color_in_range(&surface->src_blt_color_key, color))
4254 dstcolor |= 0xff;
4255 *(DWORD*)dest = dstcolor;
4256 source += 4;
4257 dest += 4;
4261 break;
4263 case WINED3D_CT_CK_ARGB32:
4265 unsigned int x, y;
4266 for (y = 0; y < height; ++y)
4268 source = src + pitch * y;
4269 dest = dst + outpitch * y;
4270 for (x = 0; x < width; ++x)
4272 DWORD color = *(const DWORD *)source;
4273 if (color_in_range(&surface->src_blt_color_key, color))
4274 color &= ~0xff000000;
4275 *(DWORD*)dest = color;
4276 source += 4;
4277 dest += 4;
4281 break;
4283 default:
4284 ERR("Unsupported conversion type %#x.\n", conversion_type);
4286 return WINED3D_OK;
4289 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4291 /* Flip the surface contents */
4292 /* Flip the DC */
4294 HDC tmp;
4295 tmp = front->hDC;
4296 front->hDC = back->hDC;
4297 back->hDC = tmp;
4300 /* Flip the DIBsection */
4302 HBITMAP tmp = front->dib.DIBsection;
4303 front->dib.DIBsection = back->dib.DIBsection;
4304 back->dib.DIBsection = tmp;
4307 /* Flip the surface data */
4309 void* tmp;
4311 tmp = front->dib.bitmap_data;
4312 front->dib.bitmap_data = back->dib.bitmap_data;
4313 back->dib.bitmap_data = tmp;
4315 tmp = front->resource.allocatedMemory;
4316 front->resource.allocatedMemory = back->resource.allocatedMemory;
4317 back->resource.allocatedMemory = tmp;
4319 tmp = front->resource.heap_memory;
4320 front->resource.heap_memory = back->resource.heap_memory;
4321 back->resource.heap_memory = tmp;
4324 /* Flip the PBO */
4326 GLuint tmp_pbo = front->pbo;
4327 front->pbo = back->pbo;
4328 back->pbo = tmp_pbo;
4331 /* Flip the opengl texture */
4333 GLuint tmp;
4335 tmp = back->texture_name;
4336 back->texture_name = front->texture_name;
4337 front->texture_name = tmp;
4339 tmp = back->texture_name_srgb;
4340 back->texture_name_srgb = front->texture_name_srgb;
4341 front->texture_name_srgb = tmp;
4343 tmp = back->rb_multisample;
4344 back->rb_multisample = front->rb_multisample;
4345 front->rb_multisample = tmp;
4347 tmp = back->rb_resolved;
4348 back->rb_resolved = front->rb_resolved;
4349 front->rb_resolved = tmp;
4351 resource_unload(&back->resource);
4352 resource_unload(&front->resource);
4356 DWORD tmp_flags = back->flags;
4357 back->flags = front->flags;
4358 front->flags = tmp_flags;
4362 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4363 * pixel copy calls. */
4364 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4365 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4367 struct wined3d_device *device = dst_surface->resource.device;
4368 const struct wined3d_gl_info *gl_info;
4369 float xrel, yrel;
4370 struct wined3d_context *context;
4371 BOOL upsidedown = FALSE;
4372 RECT dst_rect = *dst_rect_in;
4373 GLenum dst_target;
4375 if (dst_surface->container)
4376 dst_target = dst_surface->container->target;
4377 else
4378 dst_target = dst_surface->texture_target;
4380 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4381 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4383 if(dst_rect.top > dst_rect.bottom) {
4384 UINT tmp = dst_rect.bottom;
4385 dst_rect.bottom = dst_rect.top;
4386 dst_rect.top = tmp;
4387 upsidedown = TRUE;
4390 context = context_acquire(device, src_surface);
4391 gl_info = context->gl_info;
4392 context_apply_blit_state(context, device);
4393 surface_internal_preload(dst_surface, context, SRGB_RGB);
4395 /* Bind the target texture */
4396 context_bind_texture(context, dst_target, dst_surface->texture_name);
4397 if (surface_is_offscreen(src_surface))
4399 TRACE("Reading from an offscreen target\n");
4400 upsidedown = !upsidedown;
4401 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
4403 else
4405 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
4407 checkGLcall("glReadBuffer");
4409 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4410 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4412 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4414 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4416 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4417 ERR("Texture filtering not supported in direct blit.\n");
4419 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4420 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4422 ERR("Texture filtering not supported in direct blit\n");
4425 if (upsidedown
4426 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4427 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4429 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
4430 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4431 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4432 src_rect->left, src_surface->resource.height - src_rect->bottom,
4433 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4435 else
4437 LONG row;
4438 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4439 /* I have to process this row by row to swap the image,
4440 * otherwise it would be upside down, so stretching in y direction
4441 * doesn't cost extra time
4443 * However, stretching in x direction can be avoided if not necessary
4445 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4446 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4448 /* Well, that stuff works, but it's very slow.
4449 * find a better way instead
4451 LONG col;
4453 for (col = dst_rect.left; col < dst_rect.right; ++col)
4455 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4456 dst_rect.left + col /* x offset */, row /* y offset */,
4457 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4460 else
4462 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4463 dst_rect.left /* x offset */, row /* y offset */,
4464 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4468 checkGLcall("glCopyTexSubImage2D");
4470 context_release(context);
4472 /* The texture is now most up to date - If the surface is a render target
4473 * and has a drawable, this path is never entered. */
4474 surface_validate_location(dst_surface, SFLAG_INTEXTURE);
4475 surface_invalidate_location(dst_surface, ~SFLAG_INTEXTURE);
4478 /* Uses the hardware to stretch and flip the image */
4479 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4480 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4482 struct wined3d_device *device = dst_surface->resource.device;
4483 GLuint src, backup = 0;
4484 float left, right, top, bottom; /* Texture coordinates */
4485 UINT fbwidth = src_surface->resource.width;
4486 UINT fbheight = src_surface->resource.height;
4487 const struct wined3d_gl_info *gl_info;
4488 struct wined3d_context *context;
4489 GLenum drawBuffer = GL_BACK;
4490 GLenum texture_target;
4491 BOOL noBackBufferBackup;
4492 BOOL src_offscreen;
4493 BOOL upsidedown = FALSE;
4494 RECT dst_rect = *dst_rect_in;
4496 TRACE("Using hwstretch blit\n");
4497 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4498 context = context_acquire(device, src_surface);
4499 gl_info = context->gl_info;
4500 context_apply_blit_state(context, device);
4501 surface_internal_preload(dst_surface, context, SRGB_RGB);
4503 src_offscreen = surface_is_offscreen(src_surface);
4504 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4505 if (!noBackBufferBackup && !src_surface->texture_name)
4507 /* Get it a description */
4508 surface_internal_preload(src_surface, context, SRGB_RGB);
4511 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4512 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4514 if (context->aux_buffers >= 2)
4516 /* Got more than one aux buffer? Use the 2nd aux buffer */
4517 drawBuffer = GL_AUX1;
4519 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4521 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4522 drawBuffer = GL_AUX0;
4525 if (noBackBufferBackup)
4527 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
4528 checkGLcall("glGenTextures");
4529 context_bind_texture(context, GL_TEXTURE_2D, backup);
4530 texture_target = GL_TEXTURE_2D;
4532 else
4534 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4535 * we are reading from the back buffer, the backup can be used as source texture
4537 texture_target = src_surface->texture_target;
4538 context_bind_texture(context, texture_target, src_surface->texture_name);
4539 gl_info->gl_ops.gl.p_glEnable(texture_target);
4540 checkGLcall("glEnable(texture_target)");
4542 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4543 src_surface->flags &= ~SFLAG_INTEXTURE;
4546 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4547 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4549 if(dst_rect.top > dst_rect.bottom) {
4550 UINT tmp = dst_rect.bottom;
4551 dst_rect.bottom = dst_rect.top;
4552 dst_rect.top = tmp;
4553 upsidedown = TRUE;
4556 if (src_offscreen)
4558 TRACE("Reading from an offscreen target\n");
4559 upsidedown = !upsidedown;
4560 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
4562 else
4564 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
4567 /* TODO: Only back up the part that will be overwritten */
4568 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
4570 checkGLcall("glCopyTexSubImage2D");
4572 /* No issue with overriding these - the sampler is dirty due to blit usage */
4573 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
4574 wined3d_gl_mag_filter(magLookup, filter));
4575 checkGLcall("glTexParameteri");
4576 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
4577 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
4578 checkGLcall("glTexParameteri");
4580 if (!src_surface->swapchain || src_surface == src_surface->swapchain->back_buffers[0])
4582 src = backup ? backup : src_surface->texture_name;
4584 else
4586 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
4587 checkGLcall("glReadBuffer(GL_FRONT)");
4589 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
4590 checkGLcall("glGenTextures(1, &src)");
4591 context_bind_texture(context, GL_TEXTURE_2D, src);
4593 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
4594 * out for power of 2 sizes
4596 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
4597 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
4598 checkGLcall("glTexImage2D");
4599 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
4601 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4602 checkGLcall("glTexParameteri");
4603 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4604 checkGLcall("glTexParameteri");
4606 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
4607 checkGLcall("glReadBuffer(GL_BACK)");
4609 if (texture_target != GL_TEXTURE_2D)
4611 gl_info->gl_ops.gl.p_glDisable(texture_target);
4612 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
4613 texture_target = GL_TEXTURE_2D;
4616 checkGLcall("glEnd and previous");
4618 left = src_rect->left;
4619 right = src_rect->right;
4621 if (!upsidedown)
4623 top = src_surface->resource.height - src_rect->top;
4624 bottom = src_surface->resource.height - src_rect->bottom;
4626 else
4628 top = src_surface->resource.height - src_rect->bottom;
4629 bottom = src_surface->resource.height - src_rect->top;
4632 if (src_surface->flags & SFLAG_NORMCOORD)
4634 left /= src_surface->pow2Width;
4635 right /= src_surface->pow2Width;
4636 top /= src_surface->pow2Height;
4637 bottom /= src_surface->pow2Height;
4640 /* draw the source texture stretched and upside down. The correct surface is bound already */
4641 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
4642 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
4644 context_set_draw_buffer(context, drawBuffer);
4645 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
4647 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
4648 /* bottom left */
4649 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
4650 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
4652 /* top left */
4653 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
4654 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
4656 /* top right */
4657 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
4658 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4660 /* bottom right */
4661 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
4662 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
4663 gl_info->gl_ops.gl.p_glEnd();
4664 checkGLcall("glEnd and previous");
4666 if (texture_target != dst_surface->texture_target)
4668 gl_info->gl_ops.gl.p_glDisable(texture_target);
4669 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
4670 texture_target = dst_surface->texture_target;
4673 /* Now read the stretched and upside down image into the destination texture */
4674 context_bind_texture(context, texture_target, dst_surface->texture_name);
4675 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
4677 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
4678 0, 0, /* We blitted the image to the origin */
4679 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4680 checkGLcall("glCopyTexSubImage2D");
4682 if (drawBuffer == GL_BACK)
4684 /* Write the back buffer backup back. */
4685 if (backup)
4687 if (texture_target != GL_TEXTURE_2D)
4689 gl_info->gl_ops.gl.p_glDisable(texture_target);
4690 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
4691 texture_target = GL_TEXTURE_2D;
4693 context_bind_texture(context, GL_TEXTURE_2D, backup);
4695 else
4697 if (texture_target != src_surface->texture_target)
4699 gl_info->gl_ops.gl.p_glDisable(texture_target);
4700 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
4701 texture_target = src_surface->texture_target;
4703 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
4706 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
4707 /* top left */
4708 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
4709 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
4711 /* bottom left */
4712 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
4713 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
4715 /* bottom right */
4716 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
4717 (float)fbheight / (float)src_surface->pow2Height);
4718 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
4720 /* top right */
4721 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
4722 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
4723 gl_info->gl_ops.gl.p_glEnd();
4725 gl_info->gl_ops.gl.p_glDisable(texture_target);
4726 checkGLcall("glDisable(texture_target)");
4728 /* Cleanup */
4729 if (src != src_surface->texture_name && src != backup)
4731 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
4732 checkGLcall("glDeleteTextures(1, &src)");
4734 if (backup)
4736 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
4737 checkGLcall("glDeleteTextures(1, &backup)");
4740 if (wined3d_settings.strict_draw_ordering)
4741 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4743 context_release(context);
4745 /* The texture is now most up to date - If the surface is a render target
4746 * and has a drawable, this path is never entered. */
4747 surface_validate_location(dst_surface, SFLAG_INTEXTURE);
4748 surface_invalidate_location(dst_surface, ~SFLAG_INTEXTURE);
4751 /* Front buffer coordinates are always full screen coordinates, but our GL
4752 * drawable is limited to the window's client area. The sysmem and texture
4753 * copies do have the full screen size. Note that GL has a bottom-left
4754 * origin, while D3D has a top-left origin. */
4755 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
4757 UINT drawable_height;
4759 if (surface->swapchain && surface == surface->swapchain->front_buffer)
4761 POINT offset = {0, 0};
4762 RECT windowsize;
4764 ScreenToClient(window, &offset);
4765 OffsetRect(rect, offset.x, offset.y);
4767 GetClientRect(window, &windowsize);
4768 drawable_height = windowsize.bottom - windowsize.top;
4770 else
4772 drawable_height = surface->resource.height;
4775 rect->top = drawable_height - rect->top;
4776 rect->bottom = drawable_height - rect->bottom;
4779 static void surface_blt_to_drawable(const struct wined3d_device *device,
4780 enum wined3d_texture_filter_type filter, BOOL color_key,
4781 struct wined3d_surface *src_surface, const RECT *src_rect_in,
4782 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
4784 const struct wined3d_gl_info *gl_info;
4785 struct wined3d_context *context;
4786 RECT src_rect, dst_rect;
4788 src_rect = *src_rect_in;
4789 dst_rect = *dst_rect_in;
4791 context = context_acquire(device, dst_surface);
4792 gl_info = context->gl_info;
4794 /* Make sure the surface is up-to-date. This should probably use
4795 * surface_load_location() and worry about the destination surface too,
4796 * unless we're overwriting it completely. */
4797 surface_internal_preload(src_surface, context, SRGB_RGB);
4799 /* Activate the destination context, set it up for blitting */
4800 context_apply_blit_state(context, device);
4802 if (!surface_is_offscreen(dst_surface))
4803 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
4805 device->blitter->set_shader(device->blit_priv, context, src_surface);
4807 if (color_key)
4809 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
4810 checkGLcall("glEnable(GL_ALPHA_TEST)");
4812 /* When the primary render target uses P8, the alpha component
4813 * contains the palette index. Which means that the colorkey is one of
4814 * the palette entries. In other cases pixels that should be masked
4815 * away have alpha set to 0. */
4816 if (swapchain_is_p8(context->swapchain))
4817 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
4818 (float)src_surface->src_blt_color_key.color_space_low_value / 256.0f);
4819 else
4820 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
4821 checkGLcall("glAlphaFunc");
4823 else
4825 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4826 checkGLcall("glDisable(GL_ALPHA_TEST)");
4829 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
4831 if (color_key)
4833 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4834 checkGLcall("glDisable(GL_ALPHA_TEST)");
4837 /* Leave the opengl state valid for blitting */
4838 device->blitter->unset_shader(context->gl_info);
4840 if (wined3d_settings.strict_draw_ordering
4841 || (dst_surface->swapchain && dst_surface->swapchain->front_buffer == dst_surface))
4842 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4844 context_release(context);
4847 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
4849 struct wined3d_device *device = s->resource.device;
4850 const struct blit_shader *blitter;
4852 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
4853 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
4854 if (!blitter)
4856 FIXME("No blitter is capable of performing the requested color fill operation.\n");
4857 return WINED3DERR_INVALIDCALL;
4860 return blitter->color_fill(device, s, rect, color);
4863 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4864 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
4865 enum wined3d_texture_filter_type filter)
4867 struct wined3d_device *device = dst_surface->resource.device;
4868 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4869 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
4871 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
4872 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4873 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
4875 /* Get the swapchain. One of the surfaces has to be a primary surface */
4876 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4878 WARN("Destination is in sysmem, rejecting gl blt\n");
4879 return WINED3DERR_INVALIDCALL;
4882 dst_swapchain = dst_surface->swapchain;
4884 if (src_surface)
4886 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4888 WARN("Src is in sysmem, rejecting gl blt\n");
4889 return WINED3DERR_INVALIDCALL;
4892 src_swapchain = src_surface->swapchain;
4894 else
4896 src_swapchain = NULL;
4899 /* Early sort out of cases where no render target is used */
4900 if (!dst_swapchain && !src_swapchain
4901 && src_surface != device->fb.render_targets[0]
4902 && dst_surface != device->fb.render_targets[0])
4904 TRACE("No surface is render target, not using hardware blit.\n");
4905 return WINED3DERR_INVALIDCALL;
4908 /* No destination color keying supported */
4909 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
4911 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
4912 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
4913 return WINED3DERR_INVALIDCALL;
4916 if (dst_swapchain && dst_swapchain == src_swapchain)
4918 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
4919 return WINED3DERR_INVALIDCALL;
4922 if (dst_swapchain && src_swapchain)
4924 FIXME("Implement hardware blit between two different swapchains\n");
4925 return WINED3DERR_INVALIDCALL;
4928 if (dst_swapchain)
4930 /* Handled with regular texture -> swapchain blit */
4931 if (src_surface == device->fb.render_targets[0])
4932 TRACE("Blit from active render target to a swapchain\n");
4934 else if (src_swapchain && dst_surface == device->fb.render_targets[0])
4936 FIXME("Implement blit from a swapchain to the active render target\n");
4937 return WINED3DERR_INVALIDCALL;
4940 if ((src_swapchain || src_surface == device->fb.render_targets[0]) && !dst_swapchain)
4942 /* Blit from render target to texture */
4943 BOOL stretchx;
4945 /* P8 read back is not implemented */
4946 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
4947 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
4949 TRACE("P8 read back not supported by frame buffer to texture blit\n");
4950 return WINED3DERR_INVALIDCALL;
4953 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
4955 TRACE("Color keying not supported by frame buffer to texture blit\n");
4956 return WINED3DERR_INVALIDCALL;
4957 /* Destination color key is checked above */
4960 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
4961 stretchx = TRUE;
4962 else
4963 stretchx = FALSE;
4965 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
4966 * flip the image nor scale it.
4968 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
4969 * -> If the app wants a image width an unscaled width, copy it line per line
4970 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
4971 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
4972 * back buffer. This is slower than reading line per line, thus not used for flipping
4973 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
4974 * pixel by pixel. */
4975 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
4976 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
4978 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
4979 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
4981 else
4983 TRACE("Using hardware stretching to flip / stretch the texture.\n");
4984 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
4987 if (!dst_surface->resource.map_count && !(dst_surface->flags & SFLAG_DONOTFREE))
4989 wined3d_resource_free_sysmem(&dst_surface->resource);
4990 dst_surface->resource.allocatedMemory = NULL;
4992 else
4994 dst_surface->flags &= ~SFLAG_INSYSMEM;
4997 return WINED3D_OK;
4999 else if (src_surface)
5001 /* Blit from offscreen surface to render target */
5002 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
5003 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5005 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5007 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5008 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5009 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5011 FIXME("Unsupported blit operation falling back to software\n");
5012 return WINED3DERR_INVALIDCALL;
5015 /* Color keying: Check if we have to do a color keyed blt,
5016 * and if not check if a color key is activated.
5018 * Just modify the color keying parameters in the surface and restore them afterwards
5019 * The surface keeps track of the color key last used to load the opengl surface.
5020 * PreLoad will catch the change to the flags and color key and reload if necessary.
5022 if (flags & WINEDDBLT_KEYSRC)
5024 /* Use color key from surface */
5026 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5028 /* Use color key from DDBltFx */
5029 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5030 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
5032 else
5034 /* Do not use color key */
5035 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5038 surface_blt_to_drawable(device, filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5039 src_surface, src_rect, dst_surface, dst_rect);
5041 /* Restore the color key parameters */
5042 src_surface->CKeyFlags = oldCKeyFlags;
5043 src_surface->src_blt_color_key = old_blt_key;
5045 surface_validate_location(dst_surface, dst_surface->draw_binding);
5046 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
5048 return WINED3D_OK;
5051 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5052 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5053 return WINED3DERR_INVALIDCALL;
5056 /* Context activation is done by the caller. */
5057 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5058 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5060 struct wined3d_device *device = surface->resource.device;
5061 const struct wined3d_gl_info *gl_info = context->gl_info;
5062 GLint compare_mode = GL_NONE;
5063 struct blt_info info;
5064 GLint old_binding = 0;
5065 RECT rect;
5067 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5069 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
5070 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
5071 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
5072 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
5073 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
5074 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
5075 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
5076 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
5077 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5078 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
5079 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
5081 SetRect(&rect, 0, h, w, 0);
5082 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5083 context_active_texture(context, context->gl_info, 0);
5084 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
5085 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
5086 if (gl_info->supported[ARB_SHADOW])
5088 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5089 if (compare_mode != GL_NONE)
5090 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5093 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5094 gl_info, info.tex_type, &surface->ds_current_size);
5096 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
5097 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
5098 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
5099 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
5100 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
5101 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
5102 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
5103 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
5104 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
5105 gl_info->gl_ops.gl.p_glEnd();
5107 if (compare_mode != GL_NONE)
5108 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5109 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
5111 gl_info->gl_ops.gl.p_glPopAttrib();
5113 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5116 void surface_modify_ds_location(struct wined3d_surface *surface,
5117 DWORD location, UINT w, UINT h)
5119 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5121 if (location & ~(SFLAG_LOCATIONS | SFLAG_DISCARDED))
5122 FIXME("Invalid location (%#x) specified.\n", location);
5124 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5125 || (!(surface->flags & SFLAG_INTEXTURE) && (location & SFLAG_INTEXTURE)))
5127 if (surface->container)
5129 TRACE("Passing to container.\n");
5130 wined3d_texture_set_dirty(surface->container);
5134 surface->ds_current_size.cx = w;
5135 surface->ds_current_size.cy = h;
5136 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_DISCARDED);
5137 surface->flags |= location;
5140 /* Context activation is done by the caller. */
5141 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5143 const struct wined3d_gl_info *gl_info = context->gl_info;
5144 struct wined3d_device *device = surface->resource.device;
5145 GLsizei w, h;
5147 TRACE("surface %p, new location %#x.\n", surface, location);
5149 /* TODO: Make this work for modes other than FBO */
5150 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5152 if (!(surface->flags & location))
5154 w = surface->ds_current_size.cx;
5155 h = surface->ds_current_size.cy;
5156 surface->ds_current_size.cx = 0;
5157 surface->ds_current_size.cy = 0;
5159 else
5161 w = surface->resource.width;
5162 h = surface->resource.height;
5165 if (surface->ds_current_size.cx == surface->resource.width
5166 && surface->ds_current_size.cy == surface->resource.height)
5168 TRACE("Location (%#x) is already up to date.\n", location);
5169 return;
5172 if (surface->current_renderbuffer)
5174 FIXME("Not supported with fixed up depth stencil.\n");
5175 return;
5178 if (surface->flags & SFLAG_DISCARDED)
5180 TRACE("Surface was discarded, no need copy data.\n");
5181 switch (location)
5183 case SFLAG_INTEXTURE:
5184 surface_prepare_texture(surface, context, FALSE);
5185 break;
5186 case SFLAG_INRB_MULTISAMPLE:
5187 surface_prepare_rb(surface, gl_info, TRUE);
5188 break;
5189 case SFLAG_INDRAWABLE:
5190 /* Nothing to do */
5191 break;
5192 default:
5193 FIXME("Unhandled location %#x\n", location);
5195 surface->flags &= ~SFLAG_DISCARDED;
5196 surface->flags |= location;
5197 surface->ds_current_size.cx = surface->resource.width;
5198 surface->ds_current_size.cy = surface->resource.height;
5199 return;
5202 if (!(surface->flags & SFLAG_LOCATIONS))
5204 FIXME("No up to date depth stencil location.\n");
5205 surface->flags |= location;
5206 surface->ds_current_size.cx = surface->resource.width;
5207 surface->ds_current_size.cy = surface->resource.height;
5208 return;
5211 if (location == SFLAG_INTEXTURE)
5213 GLint old_binding = 0;
5214 GLenum bind_target;
5216 /* The render target is allowed to be smaller than the depth/stencil
5217 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5218 * than the offscreen surface. Don't overwrite the offscreen surface
5219 * with undefined data. */
5220 w = min(w, context->swapchain->desc.backbuffer_width);
5221 h = min(h, context->swapchain->desc.backbuffer_height);
5223 TRACE("Copying onscreen depth buffer to depth texture.\n");
5225 if (!device->depth_blt_texture)
5226 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
5228 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5229 * directly on the FBO texture. That's because we need to flip. */
5230 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5231 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5232 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5234 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5235 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5237 else
5239 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5240 bind_target = GL_TEXTURE_2D;
5242 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
5243 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5244 * internal format, because the internal format might include stencil
5245 * data. In principle we should copy stencil data as well, but unless
5246 * the driver supports stencil export it's hard to do, and doesn't
5247 * seem to be needed in practice. If the hardware doesn't support
5248 * writing stencil data, the glCopyTexImage2D() call might trigger
5249 * software fallbacks. */
5250 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5251 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5252 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5253 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5254 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5255 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5256 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5257 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
5259 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5260 NULL, surface, SFLAG_INTEXTURE);
5261 context_set_draw_buffer(context, GL_NONE);
5263 /* Do the actual blit */
5264 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5265 checkGLcall("depth_blt");
5267 context_invalidate_state(context, STATE_FRAMEBUFFER);
5269 if (wined3d_settings.strict_draw_ordering)
5270 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
5272 else if (location == SFLAG_INDRAWABLE)
5274 TRACE("Copying depth texture to onscreen depth buffer.\n");
5276 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5277 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5278 surface_depth_blt(surface, context, surface->texture_name,
5279 0, surface->pow2Height - h, w, h, surface->texture_target);
5280 checkGLcall("depth_blt");
5282 context_invalidate_state(context, STATE_FRAMEBUFFER);
5284 if (wined3d_settings.strict_draw_ordering)
5285 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
5287 else
5289 ERR("Invalid location (%#x) specified.\n", location);
5292 surface->flags |= location;
5293 surface->ds_current_size.cx = surface->resource.width;
5294 surface->ds_current_size.cy = surface->resource.height;
5297 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
5299 struct wined3d_surface *overlay;
5301 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location & SFLAG_LOCATIONS));
5303 surface->flags |= (location & SFLAG_LOCATIONS);
5305 /* Redraw emulated overlays, if any. */
5306 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5308 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5310 surface_draw_overlay(overlay);
5315 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
5317 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location & SFLAG_LOCATIONS));
5319 if ((location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && surface->container)
5320 wined3d_texture_set_dirty(surface->container);
5321 surface->flags &= ~(location & SFLAG_LOCATIONS);
5323 if (!(surface->flags & SFLAG_LOCATIONS))
5324 ERR("Surface %p does not have any up to date location.\n", surface);
5327 static DWORD resource_access_from_location(DWORD location)
5329 switch (location)
5331 case SFLAG_INSYSMEM:
5332 return WINED3D_RESOURCE_ACCESS_CPU;
5334 case SFLAG_INDRAWABLE:
5335 case SFLAG_INSRGBTEX:
5336 case SFLAG_INTEXTURE:
5337 case SFLAG_INRB_MULTISAMPLE:
5338 case SFLAG_INRB_RESOLVED:
5339 return WINED3D_RESOURCE_ACCESS_GPU;
5341 default:
5342 FIXME("Unhandled location %#x.\n", location);
5343 return 0;
5347 static void surface_load_sysmem(struct wined3d_surface *surface,
5348 const struct wined3d_gl_info *gl_info, const RECT *rect)
5350 surface_prepare_system_memory(surface);
5352 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5353 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5355 /* Download the surface to system memory. */
5356 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5358 struct wined3d_device *device = surface->resource.device;
5359 struct wined3d_context *context;
5361 /* TODO: Use already acquired context when possible. */
5362 context = context_acquire(device, NULL);
5364 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5365 surface_download_data(surface, gl_info);
5367 context_release(context);
5369 return;
5372 if (surface->flags & SFLAG_INDRAWABLE)
5374 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5375 wined3d_surface_get_pitch(surface));
5376 return;
5379 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5380 surface, surface->flags & SFLAG_LOCATIONS);
5383 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5384 const struct wined3d_gl_info *gl_info, const RECT *rect)
5386 RECT r;
5388 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5390 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5391 return WINED3DERR_INVALIDCALL;
5394 surface_get_rect(surface, rect, &r);
5395 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5396 surface_blt_to_drawable(surface->resource.device,
5397 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
5399 return WINED3D_OK;
5402 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5403 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5405 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5406 struct wined3d_device *device = surface->resource.device;
5407 enum wined3d_conversion_type convert;
5408 struct wined3d_context *context;
5409 UINT width, src_pitch, dst_pitch;
5410 struct wined3d_bo_address data;
5411 struct wined3d_format format;
5412 POINT dst_point = {0, 0};
5413 BYTE *mem;
5415 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
5416 && surface_is_offscreen(surface)
5417 && (surface->flags & SFLAG_INDRAWABLE))
5419 surface_load_fb_texture(surface, srgb);
5421 return WINED3D_OK;
5424 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
5425 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
5426 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5427 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5428 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5430 if (srgb)
5431 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INTEXTURE,
5432 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
5433 else
5434 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INSRGBTEX,
5435 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
5437 return WINED3D_OK;
5440 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
5441 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
5442 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5443 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5444 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5446 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
5447 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
5448 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5450 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
5451 &rect, surface, dst_location, &rect);
5453 return WINED3D_OK;
5456 /* Upload from system memory */
5458 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
5459 TRUE /* We will use textures */, &format, &convert);
5461 if (srgb)
5463 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
5465 /* Performance warning... */
5466 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
5467 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5470 else
5472 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
5474 /* Performance warning... */
5475 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
5476 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5480 if (!(surface->flags & SFLAG_INSYSMEM))
5482 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
5483 /* Lets hope we get it from somewhere... */
5484 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5487 /* TODO: Use already acquired context when possible. */
5488 context = context_acquire(device, NULL);
5490 surface_prepare_texture(surface, context, srgb);
5491 surface_bind_and_dirtify(surface, context, srgb);
5493 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
5495 surface->flags |= SFLAG_GLCKEY;
5496 surface->gl_color_key = surface->src_blt_color_key;
5498 else surface->flags &= ~SFLAG_GLCKEY;
5500 width = surface->resource.width;
5501 src_pitch = wined3d_surface_get_pitch(surface);
5503 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5504 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
5505 * called. */
5506 if ((convert != WINED3D_CT_NONE || format.convert) && (surface->flags & SFLAG_PBO))
5508 TRACE("Removing the pbo attached to surface %p.\n", surface);
5509 surface_remove_pbo(surface, gl_info);
5512 if (format.convert)
5514 /* This code is entered for texture formats which need a fixup. */
5515 UINT height = surface->resource.height;
5517 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
5518 dst_pitch = width * format.conv_byte_count;
5519 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5521 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5523 ERR("Out of memory (%u).\n", dst_pitch * height);
5524 context_release(context);
5525 return E_OUTOFMEMORY;
5527 format.convert(surface->resource.allocatedMemory, mem, src_pitch, dst_pitch, width, height);
5528 format.byte_count = format.conv_byte_count;
5529 src_pitch = dst_pitch;
5531 else if (convert != WINED3D_CT_NONE && surface->resource.allocatedMemory)
5533 /* This code is only entered for color keying fixups */
5534 UINT height = surface->resource.height;
5536 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
5537 dst_pitch = width * format.conv_byte_count;
5538 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5540 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5542 ERR("Out of memory (%u).\n", dst_pitch * height);
5543 context_release(context);
5544 return E_OUTOFMEMORY;
5546 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
5547 width, height, dst_pitch, convert, surface);
5548 format.byte_count = format.conv_byte_count;
5549 src_pitch = dst_pitch;
5551 else
5553 mem = surface->resource.allocatedMemory;
5556 data.buffer_object = surface->pbo;
5557 data.addr = mem;
5558 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
5560 context_release(context);
5562 /* Don't delete PBO memory. */
5563 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5564 HeapFree(GetProcessHeap(), 0, mem);
5566 return WINED3D_OK;
5569 static void surface_multisample_resolve(struct wined3d_surface *surface)
5571 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5573 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
5574 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
5576 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
5577 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
5580 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
5582 struct wined3d_device *device = surface->resource.device;
5583 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5584 HRESULT hr;
5586 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
5588 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5590 if (location == SFLAG_INTEXTURE && surface->flags & SFLAG_INDRAWABLE)
5592 struct wined3d_context *context = context_acquire(device, NULL);
5593 surface_load_ds_location(surface, context, location);
5594 context_release(context);
5595 return WINED3D_OK;
5597 else if (location & surface->flags && surface->draw_binding != SFLAG_INDRAWABLE)
5599 /* Already up to date, nothing to do. */
5600 return WINED3D_OK;
5602 else
5604 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
5605 debug_surflocation(surface->flags & SFLAG_LOCATIONS), debug_surflocation(location));
5606 return WINED3DERR_INVALIDCALL;
5610 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5611 location = SFLAG_INTEXTURE;
5613 if (surface->flags & location)
5615 TRACE("Location already up to date.\n");
5617 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
5618 && surface_need_pbo(surface, gl_info))
5619 surface_load_pbo(surface, gl_info);
5621 return WINED3D_OK;
5624 if (WARN_ON(d3d_surface))
5626 DWORD required_access = resource_access_from_location(location);
5627 if ((surface->resource.access_flags & required_access) != required_access)
5628 WARN("Operation requires %#x access, but surface only has %#x.\n",
5629 required_access, surface->resource.access_flags);
5632 if (!(surface->flags & SFLAG_LOCATIONS))
5634 ERR("Surface %p does not have any up to date location.\n", surface);
5635 surface->flags |= SFLAG_LOST;
5636 return WINED3DERR_DEVICELOST;
5639 switch (location)
5641 case SFLAG_INSYSMEM:
5642 surface_load_sysmem(surface, gl_info, rect);
5643 break;
5645 case SFLAG_INDRAWABLE:
5646 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
5647 return hr;
5648 break;
5650 case SFLAG_INRB_RESOLVED:
5651 surface_multisample_resolve(surface);
5652 break;
5654 case SFLAG_INTEXTURE:
5655 case SFLAG_INSRGBTEX:
5656 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
5657 return hr;
5658 break;
5660 default:
5661 ERR("Don't know how to handle location %#x.\n", location);
5662 break;
5665 if (!rect)
5667 surface->flags |= location;
5669 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
5670 surface_evict_sysmem(surface);
5673 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5674 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5676 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5679 return WINED3D_OK;
5682 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
5684 struct wined3d_swapchain *swapchain;
5686 /* Not on a swapchain - must be offscreen */
5687 if (!(swapchain = surface->swapchain))
5688 return TRUE;
5690 /* The front buffer is always onscreen */
5691 if (surface == swapchain->front_buffer) return FALSE;
5693 /* If the swapchain is rendered to an FBO, the backbuffer is
5694 * offscreen, otherwise onscreen */
5695 return swapchain->render_to_fbo;
5698 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
5699 /* Context activation is done by the caller. */
5700 static void ffp_blit_free(struct wined3d_device *device) { }
5702 /* Context activation is done by the caller. */
5703 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5705 const struct wined3d_gl_info *gl_info = context->gl_info;
5706 GLenum target;
5708 if (surface->container)
5709 target = surface->container->target;
5710 else
5711 target = surface->texture_target;
5713 gl_info->gl_ops.gl.p_glEnable(target);
5714 checkGLcall("glEnable(target)");
5716 return WINED3D_OK;
5719 /* Context activation is done by the caller. */
5720 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
5722 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
5723 checkGLcall("glDisable(GL_TEXTURE_2D)");
5724 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
5726 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5727 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5729 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
5731 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
5732 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5736 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5737 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5738 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5740 switch (blit_op)
5742 case WINED3D_BLIT_OP_COLOR_BLIT:
5743 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
5744 return FALSE;
5746 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5748 TRACE("Checking support for fixup:\n");
5749 dump_color_fixup_desc(src_format->color_fixup);
5752 /* We only support identity conversions. */
5753 if (!is_identity_fixup(src_format->color_fixup)
5754 || !is_identity_fixup(dst_format->color_fixup))
5756 TRACE("Fixups are not supported.\n");
5757 return FALSE;
5760 return TRUE;
5762 case WINED3D_BLIT_OP_COLOR_FILL:
5763 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
5764 return FALSE;
5766 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5768 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
5769 return FALSE;
5771 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
5773 TRACE("Color fill not supported\n");
5774 return FALSE;
5777 /* FIXME: We should reject color fills on formats with fixups,
5778 * but this would break P8 color fills for example. */
5780 return TRUE;
5782 case WINED3D_BLIT_OP_DEPTH_FILL:
5783 return TRUE;
5785 default:
5786 TRACE("Unsupported blit_op=%d\n", blit_op);
5787 return FALSE;
5791 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5792 const RECT *dst_rect, const struct wined3d_color *color)
5794 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
5795 struct wined3d_fb_state fb = {&dst_surface, NULL};
5797 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
5799 return WINED3D_OK;
5802 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
5803 struct wined3d_surface *surface, const RECT *rect, float depth)
5805 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
5806 struct wined3d_fb_state fb = {NULL, surface};
5808 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
5810 return WINED3D_OK;
5813 const struct blit_shader ffp_blit = {
5814 ffp_blit_alloc,
5815 ffp_blit_free,
5816 ffp_blit_set,
5817 ffp_blit_unset,
5818 ffp_blit_supported,
5819 ffp_blit_color_fill,
5820 ffp_blit_depth_fill,
5823 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
5825 return WINED3D_OK;
5828 /* Context activation is done by the caller. */
5829 static void cpu_blit_free(struct wined3d_device *device)
5833 /* Context activation is done by the caller. */
5834 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5836 return WINED3D_OK;
5839 /* Context activation is done by the caller. */
5840 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
5844 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5845 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5846 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5848 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
5850 return TRUE;
5853 return FALSE;
5856 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
5857 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
5858 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
5860 UINT row_block_count;
5861 const BYTE *src_row;
5862 BYTE *dst_row;
5863 UINT x, y;
5865 src_row = src_data;
5866 dst_row = dst_data;
5868 row_block_count = (update_w + format->block_width - 1) / format->block_width;
5870 if (!flags)
5872 for (y = 0; y < update_h; y += format->block_height)
5874 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
5875 src_row += src_pitch;
5876 dst_row += dst_pitch;
5879 return WINED3D_OK;
5882 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
5884 src_row += (((update_h / format->block_height) - 1) * src_pitch);
5886 switch (format->id)
5888 case WINED3DFMT_DXT1:
5889 for (y = 0; y < update_h; y += format->block_height)
5891 struct block
5893 WORD color[2];
5894 BYTE control_row[4];
5897 const struct block *s = (const struct block *)src_row;
5898 struct block *d = (struct block *)dst_row;
5900 for (x = 0; x < row_block_count; ++x)
5902 d[x].color[0] = s[x].color[0];
5903 d[x].color[1] = s[x].color[1];
5904 d[x].control_row[0] = s[x].control_row[3];
5905 d[x].control_row[1] = s[x].control_row[2];
5906 d[x].control_row[2] = s[x].control_row[1];
5907 d[x].control_row[3] = s[x].control_row[0];
5909 src_row -= src_pitch;
5910 dst_row += dst_pitch;
5912 return WINED3D_OK;
5914 case WINED3DFMT_DXT3:
5915 for (y = 0; y < update_h; y += format->block_height)
5917 struct block
5919 WORD alpha_row[4];
5920 WORD color[2];
5921 BYTE control_row[4];
5924 const struct block *s = (const struct block *)src_row;
5925 struct block *d = (struct block *)dst_row;
5927 for (x = 0; x < row_block_count; ++x)
5929 d[x].alpha_row[0] = s[x].alpha_row[3];
5930 d[x].alpha_row[1] = s[x].alpha_row[2];
5931 d[x].alpha_row[2] = s[x].alpha_row[1];
5932 d[x].alpha_row[3] = s[x].alpha_row[0];
5933 d[x].color[0] = s[x].color[0];
5934 d[x].color[1] = s[x].color[1];
5935 d[x].control_row[0] = s[x].control_row[3];
5936 d[x].control_row[1] = s[x].control_row[2];
5937 d[x].control_row[2] = s[x].control_row[1];
5938 d[x].control_row[3] = s[x].control_row[0];
5940 src_row -= src_pitch;
5941 dst_row += dst_pitch;
5943 return WINED3D_OK;
5945 default:
5946 FIXME("Compressed flip not implemented for format %s.\n",
5947 debug_d3dformat(format->id));
5948 return E_NOTIMPL;
5952 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
5953 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
5955 return E_NOTIMPL;
5958 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5959 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
5960 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5962 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
5963 const struct wined3d_format *src_format, *dst_format;
5964 struct wined3d_surface *orig_src = src_surface;
5965 struct wined3d_map_desc dst_map, src_map;
5966 const BYTE *sbase = NULL;
5967 HRESULT hr = WINED3D_OK;
5968 const BYTE *sbuf;
5969 BYTE *dbuf;
5970 int x, y;
5972 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5973 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5974 flags, fx, debug_d3dtexturefiltertype(filter));
5976 if (src_surface == dst_surface)
5978 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
5979 src_map = dst_map;
5980 src_format = dst_surface->resource.format;
5981 dst_format = src_format;
5983 else
5985 dst_format = dst_surface->resource.format;
5986 if (src_surface)
5988 if (dst_surface->resource.format->id != src_surface->resource.format->id)
5990 src_surface = surface_convert_format(src_surface, dst_format->id);
5991 if (!src_surface)
5993 /* The conv function writes a FIXME */
5994 WARN("Cannot convert source surface format to dest format.\n");
5995 goto release;
5998 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
5999 src_format = src_surface->resource.format;
6001 else
6003 src_format = dst_format;
6006 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
6009 bpp = dst_surface->resource.format->byte_count;
6010 srcheight = src_rect->bottom - src_rect->top;
6011 srcwidth = src_rect->right - src_rect->left;
6012 dstheight = dst_rect->bottom - dst_rect->top;
6013 dstwidth = dst_rect->right - dst_rect->left;
6014 width = (dst_rect->right - dst_rect->left) * bpp;
6016 if (src_surface)
6017 sbase = (BYTE *)src_map.data
6018 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
6019 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
6020 if (src_surface != dst_surface)
6021 dbuf = dst_map.data;
6022 else
6023 dbuf = (BYTE *)dst_map.data
6024 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
6025 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
6027 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
6029 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6031 if (src_surface == dst_surface)
6033 FIXME("Only plain blits supported on compressed surfaces.\n");
6034 hr = E_NOTIMPL;
6035 goto release;
6038 if (srcheight != dstheight || srcwidth != dstwidth)
6040 WARN("Stretching not supported on compressed surfaces.\n");
6041 hr = WINED3DERR_INVALIDCALL;
6042 goto release;
6045 if (!surface_check_block_align(src_surface, src_rect))
6047 WARN("Source rectangle not block-aligned.\n");
6048 hr = WINED3DERR_INVALIDCALL;
6049 goto release;
6052 if (!surface_check_block_align(dst_surface, dst_rect))
6054 WARN("Destination rectangle not block-aligned.\n");
6055 hr = WINED3DERR_INVALIDCALL;
6056 goto release;
6059 hr = surface_cpu_blt_compressed(sbase, dbuf,
6060 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
6061 src_format, flags, fx);
6062 goto release;
6065 /* First, all the 'source-less' blits */
6066 if (flags & WINEDDBLT_COLORFILL)
6068 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
6069 flags &= ~WINEDDBLT_COLORFILL;
6072 if (flags & WINEDDBLT_DEPTHFILL)
6074 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6076 if (flags & WINEDDBLT_ROP)
6078 /* Catch some degenerate cases here. */
6079 switch (fx->dwROP)
6081 case BLACKNESS:
6082 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
6083 break;
6084 case 0xaa0029: /* No-op */
6085 break;
6086 case WHITENESS:
6087 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
6088 break;
6089 case SRCCOPY: /* Well, we do that below? */
6090 break;
6091 default:
6092 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6093 goto error;
6095 flags &= ~WINEDDBLT_ROP;
6097 if (flags & WINEDDBLT_DDROPS)
6099 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6101 /* Now the 'with source' blits. */
6102 if (src_surface)
6104 int sx, xinc, sy, yinc;
6106 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6107 goto release;
6109 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
6110 && (srcwidth != dstwidth || srcheight != dstheight))
6112 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6113 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6116 xinc = (srcwidth << 16) / dstwidth;
6117 yinc = (srcheight << 16) / dstheight;
6119 if (!flags)
6121 /* No effects, we can cheat here. */
6122 if (dstwidth == srcwidth)
6124 if (dstheight == srcheight)
6126 /* No stretching in either direction. This needs to be as
6127 * fast as possible. */
6128 sbuf = sbase;
6130 /* Check for overlapping surfaces. */
6131 if (src_surface != dst_surface || dst_rect->top < src_rect->top
6132 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
6134 /* No overlap, or dst above src, so copy from top downwards. */
6135 for (y = 0; y < dstheight; ++y)
6137 memcpy(dbuf, sbuf, width);
6138 sbuf += src_map.row_pitch;
6139 dbuf += dst_map.row_pitch;
6142 else if (dst_rect->top > src_rect->top)
6144 /* Copy from bottom upwards. */
6145 sbuf += src_map.row_pitch * dstheight;
6146 dbuf += dst_map.row_pitch * dstheight;
6147 for (y = 0; y < dstheight; ++y)
6149 sbuf -= src_map.row_pitch;
6150 dbuf -= dst_map.row_pitch;
6151 memcpy(dbuf, sbuf, width);
6154 else
6156 /* Src and dst overlapping on the same line, use memmove. */
6157 for (y = 0; y < dstheight; ++y)
6159 memmove(dbuf, sbuf, width);
6160 sbuf += src_map.row_pitch;
6161 dbuf += dst_map.row_pitch;
6165 else
6167 /* Stretching in y direction only. */
6168 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6170 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6171 memcpy(dbuf, sbuf, width);
6172 dbuf += dst_map.row_pitch;
6176 else
6178 /* Stretching in X direction. */
6179 int last_sy = -1;
6180 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6182 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6184 if ((sy >> 16) == (last_sy >> 16))
6186 /* This source row is the same as last source row -
6187 * Copy the already stretched row. */
6188 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
6190 else
6192 #define STRETCH_ROW(type) \
6193 do { \
6194 const type *s = (const type *)sbuf; \
6195 type *d = (type *)dbuf; \
6196 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6197 d[x] = s[sx >> 16]; \
6198 } while(0)
6200 switch(bpp)
6202 case 1:
6203 STRETCH_ROW(BYTE);
6204 break;
6205 case 2:
6206 STRETCH_ROW(WORD);
6207 break;
6208 case 4:
6209 STRETCH_ROW(DWORD);
6210 break;
6211 case 3:
6213 const BYTE *s;
6214 BYTE *d = dbuf;
6215 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6217 DWORD pixel;
6219 s = sbuf + 3 * (sx >> 16);
6220 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6221 d[0] = (pixel ) & 0xff;
6222 d[1] = (pixel >> 8) & 0xff;
6223 d[2] = (pixel >> 16) & 0xff;
6224 d += 3;
6226 break;
6228 default:
6229 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6230 hr = WINED3DERR_NOTAVAILABLE;
6231 goto error;
6233 #undef STRETCH_ROW
6235 dbuf += dst_map.row_pitch;
6236 last_sy = sy;
6240 else
6242 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
6243 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
6244 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
6245 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6247 /* The color keying flags are checked for correctness in ddraw */
6248 if (flags & WINEDDBLT_KEYSRC)
6250 keylow = src_surface->src_blt_color_key.color_space_low_value;
6251 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
6253 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6255 keylow = fx->ddckSrcColorkey.color_space_low_value;
6256 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
6259 if (flags & WINEDDBLT_KEYDEST)
6261 /* Destination color keys are taken from the source surface! */
6262 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
6263 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
6265 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6267 destkeylow = fx->ddckDestColorkey.color_space_low_value;
6268 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
6271 if (bpp == 1)
6273 keymask = 0xff;
6275 else
6277 DWORD masks[3];
6278 get_color_masks(src_format, masks);
6279 keymask = masks[0]
6280 | masks[1]
6281 | masks[2];
6283 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6286 if (flags & WINEDDBLT_DDFX)
6288 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6289 LONG tmpxy;
6290 dTopLeft = dbuf;
6291 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6292 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
6293 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6295 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6297 /* I don't think we need to do anything about this flag */
6298 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6300 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6302 tmp = dTopRight;
6303 dTopRight = dTopLeft;
6304 dTopLeft = tmp;
6305 tmp = dBottomRight;
6306 dBottomRight = dBottomLeft;
6307 dBottomLeft = tmp;
6308 dstxinc = dstxinc * -1;
6310 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6312 tmp = dTopLeft;
6313 dTopLeft = dBottomLeft;
6314 dBottomLeft = tmp;
6315 tmp = dTopRight;
6316 dTopRight = dBottomRight;
6317 dBottomRight = tmp;
6318 dstyinc = dstyinc * -1;
6320 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6322 /* I don't think we need to do anything about this flag */
6323 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6325 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6327 tmp = dBottomRight;
6328 dBottomRight = dTopLeft;
6329 dTopLeft = tmp;
6330 tmp = dBottomLeft;
6331 dBottomLeft = dTopRight;
6332 dTopRight = tmp;
6333 dstxinc = dstxinc * -1;
6334 dstyinc = dstyinc * -1;
6336 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6338 tmp = dTopLeft;
6339 dTopLeft = dBottomLeft;
6340 dBottomLeft = dBottomRight;
6341 dBottomRight = dTopRight;
6342 dTopRight = tmp;
6343 tmpxy = dstxinc;
6344 dstxinc = dstyinc;
6345 dstyinc = tmpxy;
6346 dstxinc = dstxinc * -1;
6348 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6350 tmp = dTopLeft;
6351 dTopLeft = dTopRight;
6352 dTopRight = dBottomRight;
6353 dBottomRight = dBottomLeft;
6354 dBottomLeft = tmp;
6355 tmpxy = dstxinc;
6356 dstxinc = dstyinc;
6357 dstyinc = tmpxy;
6358 dstyinc = dstyinc * -1;
6360 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6362 /* I don't think we need to do anything about this flag */
6363 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6365 dbuf = dTopLeft;
6366 flags &= ~(WINEDDBLT_DDFX);
6369 #define COPY_COLORKEY_FX(type) \
6370 do { \
6371 const type *s; \
6372 type *d = (type *)dbuf, *dx, tmp; \
6373 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6375 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
6376 dx = d; \
6377 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6379 tmp = s[sx >> 16]; \
6380 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6381 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6383 dx[0] = tmp; \
6385 dx = (type *)(((BYTE *)dx) + dstxinc); \
6387 d = (type *)(((BYTE *)d) + dstyinc); \
6389 } while(0)
6391 switch (bpp)
6393 case 1:
6394 COPY_COLORKEY_FX(BYTE);
6395 break;
6396 case 2:
6397 COPY_COLORKEY_FX(WORD);
6398 break;
6399 case 4:
6400 COPY_COLORKEY_FX(DWORD);
6401 break;
6402 case 3:
6404 const BYTE *s;
6405 BYTE *d = dbuf, *dx;
6406 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6408 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6409 dx = d;
6410 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
6412 DWORD pixel, dpixel = 0;
6413 s = sbuf + 3 * (sx>>16);
6414 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6415 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
6416 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
6417 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
6419 dx[0] = (pixel ) & 0xff;
6420 dx[1] = (pixel >> 8) & 0xff;
6421 dx[2] = (pixel >> 16) & 0xff;
6423 dx += dstxinc;
6425 d += dstyinc;
6427 break;
6429 default:
6430 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
6431 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
6432 hr = WINED3DERR_NOTAVAILABLE;
6433 goto error;
6434 #undef COPY_COLORKEY_FX
6439 error:
6440 if (flags && FIXME_ON(d3d_surface))
6442 FIXME("\tUnsupported flags: %#x.\n", flags);
6445 release:
6446 wined3d_surface_unmap(dst_surface);
6447 if (src_surface && src_surface != dst_surface)
6448 wined3d_surface_unmap(src_surface);
6449 /* Release the converted surface, if any. */
6450 if (src_surface && src_surface != orig_src)
6451 wined3d_surface_decref(src_surface);
6453 return hr;
6456 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6457 const RECT *dst_rect, const struct wined3d_color *color)
6459 static const RECT src_rect;
6460 WINEDDBLTFX BltFx;
6462 memset(&BltFx, 0, sizeof(BltFx));
6463 BltFx.dwSize = sizeof(BltFx);
6464 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
6465 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
6466 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
6469 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
6470 struct wined3d_surface *surface, const RECT *rect, float depth)
6472 FIXME("Depth filling not implemented by cpu_blit.\n");
6473 return WINED3DERR_INVALIDCALL;
6476 const struct blit_shader cpu_blit = {
6477 cpu_blit_alloc,
6478 cpu_blit_free,
6479 cpu_blit_set,
6480 cpu_blit_unset,
6481 cpu_blit_supported,
6482 cpu_blit_color_fill,
6483 cpu_blit_depth_fill,
6486 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
6487 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
6488 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
6490 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
6491 struct wined3d_device *device = dst_surface->resource.device;
6492 DWORD src_ds_flags, dst_ds_flags;
6493 RECT src_rect, dst_rect;
6494 BOOL scale, convert;
6496 static const DWORD simple_blit = WINEDDBLT_ASYNC
6497 | WINEDDBLT_COLORFILL
6498 | WINEDDBLT_WAIT
6499 | WINEDDBLT_DEPTHFILL
6500 | WINEDDBLT_DONOTWAIT;
6502 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6503 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
6504 flags, fx, debug_d3dtexturefiltertype(filter));
6505 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
6507 if (fx)
6509 TRACE("dwSize %#x.\n", fx->dwSize);
6510 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
6511 TRACE("dwROP %#x.\n", fx->dwROP);
6512 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
6513 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
6514 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
6515 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
6516 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
6517 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
6518 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
6519 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
6520 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
6521 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
6522 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
6523 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
6524 TRACE("dwReserved %#x.\n", fx->dwReserved);
6525 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
6526 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
6527 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
6528 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
6529 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
6530 TRACE("ddckDestColorkey {%#x, %#x}.\n",
6531 fx->ddckDestColorkey.color_space_low_value,
6532 fx->ddckDestColorkey.color_space_high_value);
6533 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
6534 fx->ddckSrcColorkey.color_space_low_value,
6535 fx->ddckSrcColorkey.color_space_high_value);
6538 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
6540 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
6541 return WINEDDERR_SURFACEBUSY;
6544 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
6546 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
6547 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
6548 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
6549 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
6550 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
6552 WARN("The application gave us a bad destination rectangle.\n");
6553 return WINEDDERR_INVALIDRECT;
6556 if (src_surface)
6558 surface_get_rect(src_surface, src_rect_in, &src_rect);
6560 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
6561 || src_rect.left > src_surface->resource.width || src_rect.left < 0
6562 || src_rect.top > src_surface->resource.height || src_rect.top < 0
6563 || src_rect.right > src_surface->resource.width || src_rect.right < 0
6564 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
6566 WARN("Application gave us bad source rectangle for Blt.\n");
6567 return WINEDDERR_INVALIDRECT;
6570 else
6572 memset(&src_rect, 0, sizeof(src_rect));
6575 if (!fx || !(fx->dwDDFX))
6576 flags &= ~WINEDDBLT_DDFX;
6578 if (flags & WINEDDBLT_WAIT)
6579 flags &= ~WINEDDBLT_WAIT;
6581 if (flags & WINEDDBLT_ASYNC)
6583 static unsigned int once;
6585 if (!once++)
6586 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
6587 flags &= ~WINEDDBLT_ASYNC;
6590 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
6591 if (flags & WINEDDBLT_DONOTWAIT)
6593 static unsigned int once;
6595 if (!once++)
6596 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
6597 flags &= ~WINEDDBLT_DONOTWAIT;
6600 if (!device->d3d_initialized)
6602 WARN("D3D not initialized, using fallback.\n");
6603 goto cpu;
6606 /* We want to avoid invalidating the sysmem location for converted
6607 * surfaces, since otherwise we'd have to convert the data back when
6608 * locking them. */
6609 if (dst_surface->flags & SFLAG_CONVERTED)
6611 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
6612 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
6615 if (flags & ~simple_blit)
6617 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
6618 goto fallback;
6621 if (src_surface)
6622 src_swapchain = src_surface->swapchain;
6623 else
6624 src_swapchain = NULL;
6626 dst_swapchain = dst_surface->swapchain;
6628 /* This isn't strictly needed. FBO blits for example could deal with
6629 * cross-swapchain blits by first downloading the source to a texture
6630 * before switching to the destination context. We just have this here to
6631 * not have to deal with the issue, since cross-swapchain blits should be
6632 * rare. */
6633 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
6635 FIXME("Using fallback for cross-swapchain blit.\n");
6636 goto fallback;
6639 scale = src_surface
6640 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
6641 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
6642 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
6644 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
6645 if (src_surface)
6646 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
6647 else
6648 src_ds_flags = 0;
6650 if (src_ds_flags || dst_ds_flags)
6652 if (flags & WINEDDBLT_DEPTHFILL)
6654 float depth;
6656 TRACE("Depth fill.\n");
6658 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
6659 return WINED3DERR_INVALIDCALL;
6661 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
6662 return WINED3D_OK;
6664 else
6666 if (src_ds_flags != dst_ds_flags)
6668 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
6669 return WINED3DERR_INVALIDCALL;
6672 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->draw_binding, &src_rect,
6673 dst_surface, dst_surface->draw_binding, &dst_rect)))
6674 return WINED3D_OK;
6677 else
6679 /* In principle this would apply to depth blits as well, but we don't
6680 * implement those in the CPU blitter at the moment. */
6681 if ((dst_surface->flags & SFLAG_INSYSMEM)
6682 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
6684 if (scale)
6685 TRACE("Not doing sysmem blit because of scaling.\n");
6686 else if (convert)
6687 TRACE("Not doing sysmem blit because of format conversion.\n");
6688 else
6689 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
6692 if (flags & WINEDDBLT_COLORFILL)
6694 struct wined3d_color color;
6696 TRACE("Color fill.\n");
6698 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
6699 goto fallback;
6701 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
6702 return WINED3D_OK;
6704 else
6706 TRACE("Color blit.\n");
6708 /* Upload */
6709 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
6711 if (scale)
6712 TRACE("Not doing upload because of scaling.\n");
6713 else if (convert)
6714 TRACE("Not doing upload because of format conversion.\n");
6715 else
6717 POINT dst_point = {dst_rect.left, dst_rect.top};
6719 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
6721 if (!surface_is_offscreen(dst_surface))
6722 surface_load_location(dst_surface, dst_surface->draw_binding, NULL);
6723 return WINED3D_OK;
6728 /* Use present for back -> front blits. The idea behind this is
6729 * that present is potentially faster than a blit, in particular
6730 * when FBO blits aren't available. Some ddraw applications like
6731 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
6732 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
6733 * applications can't blit directly to the frontbuffer. */
6734 if (dst_swapchain && dst_swapchain->back_buffers
6735 && dst_surface == dst_swapchain->front_buffer
6736 && src_surface == dst_swapchain->back_buffers[0])
6738 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
6740 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
6742 /* Set the swap effect to COPY, we don't want the backbuffer
6743 * to become undefined. */
6744 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
6745 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
6746 dst_swapchain->desc.swap_effect = swap_effect;
6748 return WINED3D_OK;
6751 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6752 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6753 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6755 TRACE("Using FBO blit.\n");
6757 surface_blt_fbo(device, filter,
6758 src_surface, src_surface->draw_binding, &src_rect,
6759 dst_surface, dst_surface->draw_binding, &dst_rect);
6760 surface_validate_location(dst_surface, dst_surface->draw_binding);
6761 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
6763 return WINED3D_OK;
6766 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6767 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6768 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6770 TRACE("Using arbfp blit.\n");
6772 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
6773 return WINED3D_OK;
6778 fallback:
6780 /* Special cases for render targets. */
6781 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
6782 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
6784 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect,
6785 src_surface, &src_rect, flags, fx, filter)))
6786 return WINED3D_OK;
6789 cpu:
6791 /* For the rest call the X11 surface implementation. For render targets
6792 * this should be implemented OpenGL accelerated in surface_blt_special(),
6793 * other blits are rather rare. */
6794 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
6797 static HRESULT surface_init(struct wined3d_surface *surface, UINT alignment, UINT width, UINT height,
6798 enum wined3d_multisample_type multisample_type, UINT multisample_quality,
6799 struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
6800 enum wined3d_pool pool, DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops)
6802 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6803 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
6804 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
6805 unsigned int resource_size;
6806 HRESULT hr;
6808 if (multisample_quality > 0)
6810 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
6811 multisample_quality = 0;
6814 /* Quick lockable sanity check.
6815 * TODO: remove this after surfaces, usage and lockability have been debugged properly
6816 * this function is too deep to need to care about things like this.
6817 * Levels need to be checked too, since they all affect what can be done. */
6818 switch (pool)
6820 case WINED3D_POOL_SCRATCH:
6821 if (!lockable)
6823 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
6824 "which are mutually exclusive, setting lockable to TRUE.\n");
6825 lockable = TRUE;
6827 break;
6829 case WINED3D_POOL_SYSTEM_MEM:
6830 if (!lockable)
6831 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
6832 break;
6834 case WINED3D_POOL_MANAGED:
6835 if (usage & WINED3DUSAGE_DYNAMIC)
6836 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
6837 break;
6839 case WINED3D_POOL_DEFAULT:
6840 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
6841 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
6842 break;
6844 default:
6845 FIXME("Unknown pool %#x.\n", pool);
6846 break;
6849 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3D_POOL_DEFAULT)
6850 FIXME("Trying to create a render target that isn't in the default pool.\n");
6852 /* FIXME: Check that the format is supported by the device. */
6854 resource_size = wined3d_format_calculate_size(format, alignment, width, height, 1);
6855 if (!resource_size)
6856 return WINED3DERR_INVALIDCALL;
6858 if (device->wined3d->flags & WINED3D_NO3D)
6859 surface->surface_ops = &gdi_surface_ops;
6860 else
6861 surface->surface_ops = &surface_ops;
6863 hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
6864 multisample_type, multisample_quality, usage, pool, width, height, 1,
6865 resource_size, parent, parent_ops, &surface_resource_ops);
6866 if (FAILED(hr))
6868 WARN("Failed to initialize resource, returning %#x.\n", hr);
6869 return hr;
6872 /* "Standalone" surface. */
6873 surface_set_container(surface, NULL);
6875 list_init(&surface->overlays);
6877 /* Flags */
6878 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
6879 if (flags & WINED3D_SURFACE_DISCARD)
6880 surface->flags |= SFLAG_DISCARD;
6881 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
6882 surface->flags |= SFLAG_PIN_SYSMEM;
6883 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
6884 surface->flags |= SFLAG_LOCKABLE;
6885 /* I'm not sure if this qualifies as a hack or as an optimization. It
6886 * seems reasonable to assume that lockable render targets will get
6887 * locked, so we might as well set SFLAG_DYNLOCK right at surface
6888 * creation. However, the other reason we want to do this is that several
6889 * ddraw applications access surface memory while the surface isn't
6890 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
6891 * future locks prevents these from crashing. */
6892 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
6893 surface->flags |= SFLAG_DYNLOCK;
6895 /* Mark the texture as dirty so that it gets loaded first time around. */
6896 surface_add_dirty_rect(surface, NULL);
6897 list_init(&surface->renderbuffers);
6899 TRACE("surface %p, memory %p, size %u\n",
6900 surface, surface->resource.allocatedMemory, surface->resource.size);
6902 /* Call the private setup routine */
6903 hr = surface->surface_ops->surface_private_setup(surface);
6904 if (FAILED(hr))
6906 ERR("Private setup failed, returning %#x\n", hr);
6907 surface_cleanup(surface);
6908 return hr;
6911 /* Similar to lockable rendertargets above, creating the DIB section
6912 * during surface initialization prevents the sysmem pointer from changing
6913 * after a wined3d_surface_getdc() call. */
6914 if ((usage & WINED3DUSAGE_OWNDC) && !surface->hDC
6915 && SUCCEEDED(surface_create_dib_section(surface)))
6917 wined3d_resource_free_sysmem(&surface->resource);
6918 surface->resource.allocatedMemory = surface->dib.bitmap_data;
6921 return hr;
6924 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
6925 enum wined3d_format_id format_id, DWORD usage, enum wined3d_pool pool,
6926 enum wined3d_multisample_type multisample_type, DWORD multisample_quality, DWORD flags,
6927 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
6929 struct wined3d_surface *object;
6930 HRESULT hr;
6932 TRACE("device %p, width %u, height %u, format %s\n",
6933 device, width, height, debug_d3dformat(format_id));
6934 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
6935 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
6936 TRACE("flags %#x, parent %p, parent_ops %p.\n", flags, parent, parent_ops);
6938 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
6939 if (!object)
6940 return WINED3DERR_OUTOFVIDEOMEMORY;
6942 if (FAILED(hr = surface_init(object, device->surface_alignment, width, height, multisample_type,
6943 multisample_quality, device, usage, format_id, pool, flags, parent, parent_ops)))
6945 WARN("Failed to initialize surface, returning %#x.\n", hr);
6946 HeapFree(GetProcessHeap(), 0, object);
6947 return hr;
6950 TRACE("Created surface %p.\n", object);
6951 *surface = object;
6953 return hr;