kernel32/tests: Fix timer tests compilation with __WINESRC__ defined.
[wine.git] / dlls / wined3d / surface.c
blobdaa26e2adbd58bede5fa3f64b008a01c3b6e3537
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->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
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 surface_evict_sysmem(dst_surface);
4989 return WINED3D_OK;
4991 else if (src_surface)
4993 /* Blit from offscreen surface to render target */
4994 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
4995 DWORD oldCKeyFlags = src_surface->CKeyFlags;
4997 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4999 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5000 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5001 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5003 FIXME("Unsupported blit operation falling back to software\n");
5004 return WINED3DERR_INVALIDCALL;
5007 /* Color keying: Check if we have to do a color keyed blt,
5008 * and if not check if a color key is activated.
5010 * Just modify the color keying parameters in the surface and restore them afterwards
5011 * The surface keeps track of the color key last used to load the opengl surface.
5012 * PreLoad will catch the change to the flags and color key and reload if necessary.
5014 if (flags & WINEDDBLT_KEYSRC)
5016 /* Use color key from surface */
5018 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5020 /* Use color key from DDBltFx */
5021 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5022 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
5024 else
5026 /* Do not use color key */
5027 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5030 surface_blt_to_drawable(device, filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5031 src_surface, src_rect, dst_surface, dst_rect);
5033 /* Restore the color key parameters */
5034 src_surface->CKeyFlags = oldCKeyFlags;
5035 src_surface->src_blt_color_key = old_blt_key;
5037 surface_validate_location(dst_surface, dst_surface->draw_binding);
5038 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
5040 return WINED3D_OK;
5043 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5044 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5045 return WINED3DERR_INVALIDCALL;
5048 /* Context activation is done by the caller. */
5049 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5050 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5052 struct wined3d_device *device = surface->resource.device;
5053 const struct wined3d_gl_info *gl_info = context->gl_info;
5054 GLint compare_mode = GL_NONE;
5055 struct blt_info info;
5056 GLint old_binding = 0;
5057 RECT rect;
5059 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5061 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
5062 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
5063 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
5064 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
5065 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
5066 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
5067 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
5068 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
5069 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5070 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
5071 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
5073 SetRect(&rect, 0, h, w, 0);
5074 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5075 context_active_texture(context, context->gl_info, 0);
5076 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
5077 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
5078 if (gl_info->supported[ARB_SHADOW])
5080 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5081 if (compare_mode != GL_NONE)
5082 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5085 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5086 gl_info, info.tex_type, &surface->ds_current_size);
5088 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
5089 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
5090 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
5091 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
5092 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
5093 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
5094 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
5095 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
5096 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
5097 gl_info->gl_ops.gl.p_glEnd();
5099 if (compare_mode != GL_NONE)
5100 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5101 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
5103 gl_info->gl_ops.gl.p_glPopAttrib();
5105 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5108 void surface_modify_ds_location(struct wined3d_surface *surface,
5109 DWORD location, UINT w, UINT h)
5111 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5113 if (location & ~(SFLAG_LOCATIONS | SFLAG_DISCARDED))
5114 FIXME("Invalid location (%#x) specified.\n", location);
5116 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5117 || (!(surface->flags & SFLAG_INTEXTURE) && (location & SFLAG_INTEXTURE)))
5119 if (surface->container)
5121 TRACE("Passing to container.\n");
5122 wined3d_texture_set_dirty(surface->container);
5126 surface->ds_current_size.cx = w;
5127 surface->ds_current_size.cy = h;
5128 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_DISCARDED);
5129 surface->flags |= location;
5132 /* Context activation is done by the caller. */
5133 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5135 const struct wined3d_gl_info *gl_info = context->gl_info;
5136 struct wined3d_device *device = surface->resource.device;
5137 GLsizei w, h;
5139 TRACE("surface %p, new location %#x.\n", surface, location);
5141 /* TODO: Make this work for modes other than FBO */
5142 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5144 if (!(surface->flags & location))
5146 w = surface->ds_current_size.cx;
5147 h = surface->ds_current_size.cy;
5148 surface->ds_current_size.cx = 0;
5149 surface->ds_current_size.cy = 0;
5151 else
5153 w = surface->resource.width;
5154 h = surface->resource.height;
5157 if (surface->ds_current_size.cx == surface->resource.width
5158 && surface->ds_current_size.cy == surface->resource.height)
5160 TRACE("Location (%#x) is already up to date.\n", location);
5161 return;
5164 if (surface->current_renderbuffer)
5166 FIXME("Not supported with fixed up depth stencil.\n");
5167 return;
5170 if (surface->flags & SFLAG_DISCARDED)
5172 TRACE("Surface was discarded, no need copy data.\n");
5173 switch (location)
5175 case SFLAG_INTEXTURE:
5176 surface_prepare_texture(surface, context, FALSE);
5177 break;
5178 case SFLAG_INRB_MULTISAMPLE:
5179 surface_prepare_rb(surface, gl_info, TRUE);
5180 break;
5181 case SFLAG_INDRAWABLE:
5182 /* Nothing to do */
5183 break;
5184 default:
5185 FIXME("Unhandled location %#x\n", location);
5187 surface->flags &= ~SFLAG_DISCARDED;
5188 surface->flags |= location;
5189 surface->ds_current_size.cx = surface->resource.width;
5190 surface->ds_current_size.cy = surface->resource.height;
5191 return;
5194 if (!(surface->flags & SFLAG_LOCATIONS))
5196 FIXME("No up to date depth stencil location.\n");
5197 surface->flags |= location;
5198 surface->ds_current_size.cx = surface->resource.width;
5199 surface->ds_current_size.cy = surface->resource.height;
5200 return;
5203 if (location == SFLAG_INTEXTURE)
5205 GLint old_binding = 0;
5206 GLenum bind_target;
5208 /* The render target is allowed to be smaller than the depth/stencil
5209 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5210 * than the offscreen surface. Don't overwrite the offscreen surface
5211 * with undefined data. */
5212 w = min(w, context->swapchain->desc.backbuffer_width);
5213 h = min(h, context->swapchain->desc.backbuffer_height);
5215 TRACE("Copying onscreen depth buffer to depth texture.\n");
5217 if (!device->depth_blt_texture)
5218 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
5220 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5221 * directly on the FBO texture. That's because we need to flip. */
5222 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5223 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5224 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5226 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5227 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5229 else
5231 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5232 bind_target = GL_TEXTURE_2D;
5234 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
5235 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5236 * internal format, because the internal format might include stencil
5237 * data. In principle we should copy stencil data as well, but unless
5238 * the driver supports stencil export it's hard to do, and doesn't
5239 * seem to be needed in practice. If the hardware doesn't support
5240 * writing stencil data, the glCopyTexImage2D() call might trigger
5241 * software fallbacks. */
5242 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5243 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5244 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5245 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5246 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5247 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5248 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5249 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
5251 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5252 NULL, surface, SFLAG_INTEXTURE);
5253 context_set_draw_buffer(context, GL_NONE);
5255 /* Do the actual blit */
5256 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5257 checkGLcall("depth_blt");
5259 context_invalidate_state(context, STATE_FRAMEBUFFER);
5261 if (wined3d_settings.strict_draw_ordering)
5262 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
5264 else if (location == SFLAG_INDRAWABLE)
5266 TRACE("Copying depth texture to onscreen depth buffer.\n");
5268 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5269 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5270 surface_depth_blt(surface, context, surface->texture_name,
5271 0, surface->pow2Height - h, w, h, surface->texture_target);
5272 checkGLcall("depth_blt");
5274 context_invalidate_state(context, STATE_FRAMEBUFFER);
5276 if (wined3d_settings.strict_draw_ordering)
5277 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
5279 else
5281 ERR("Invalid location (%#x) specified.\n", location);
5284 surface->flags |= location;
5285 surface->ds_current_size.cx = surface->resource.width;
5286 surface->ds_current_size.cy = surface->resource.height;
5289 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
5291 struct wined3d_surface *overlay;
5293 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location & SFLAG_LOCATIONS));
5295 surface->flags |= (location & SFLAG_LOCATIONS);
5297 /* Redraw emulated overlays, if any. */
5298 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5300 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5302 surface_draw_overlay(overlay);
5307 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
5309 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location & SFLAG_LOCATIONS));
5311 if ((location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && surface->container)
5312 wined3d_texture_set_dirty(surface->container);
5313 surface->flags &= ~(location & SFLAG_LOCATIONS);
5315 if (!(surface->flags & SFLAG_LOCATIONS))
5316 ERR("Surface %p does not have any up to date location.\n", surface);
5319 static DWORD resource_access_from_location(DWORD location)
5321 switch (location)
5323 case SFLAG_INSYSMEM:
5324 return WINED3D_RESOURCE_ACCESS_CPU;
5326 case SFLAG_INDRAWABLE:
5327 case SFLAG_INSRGBTEX:
5328 case SFLAG_INTEXTURE:
5329 case SFLAG_INRB_MULTISAMPLE:
5330 case SFLAG_INRB_RESOLVED:
5331 return WINED3D_RESOURCE_ACCESS_GPU;
5333 default:
5334 FIXME("Unhandled location %#x.\n", location);
5335 return 0;
5339 static void surface_load_sysmem(struct wined3d_surface *surface,
5340 const struct wined3d_gl_info *gl_info, const RECT *rect)
5342 surface_prepare_system_memory(surface);
5344 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5345 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5347 /* Download the surface to system memory. */
5348 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5350 struct wined3d_device *device = surface->resource.device;
5351 struct wined3d_context *context;
5353 /* TODO: Use already acquired context when possible. */
5354 context = context_acquire(device, NULL);
5356 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5357 surface_download_data(surface, gl_info);
5359 context_release(context);
5361 return;
5364 if (surface->flags & SFLAG_INDRAWABLE)
5366 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5367 wined3d_surface_get_pitch(surface));
5368 return;
5371 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5372 surface, surface->flags & SFLAG_LOCATIONS);
5375 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5376 const struct wined3d_gl_info *gl_info, const RECT *rect)
5378 RECT r;
5380 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5382 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5383 return WINED3DERR_INVALIDCALL;
5386 surface_get_rect(surface, rect, &r);
5387 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5388 surface_blt_to_drawable(surface->resource.device,
5389 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
5391 return WINED3D_OK;
5394 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5395 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5397 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5398 struct wined3d_device *device = surface->resource.device;
5399 enum wined3d_conversion_type convert;
5400 struct wined3d_context *context;
5401 UINT width, src_pitch, dst_pitch;
5402 struct wined3d_bo_address data;
5403 struct wined3d_format format;
5404 POINT dst_point = {0, 0};
5405 BYTE *mem;
5407 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
5408 && surface_is_offscreen(surface)
5409 && (surface->flags & SFLAG_INDRAWABLE))
5411 surface_load_fb_texture(surface, srgb);
5413 return WINED3D_OK;
5416 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
5417 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
5418 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5419 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5420 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5422 if (srgb)
5423 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INTEXTURE,
5424 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
5425 else
5426 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INSRGBTEX,
5427 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
5429 return WINED3D_OK;
5432 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
5433 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
5434 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5435 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5436 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5438 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
5439 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
5440 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5442 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
5443 &rect, surface, dst_location, &rect);
5445 return WINED3D_OK;
5448 /* Upload from system memory */
5450 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
5451 TRUE /* We will use textures */, &format, &convert);
5453 if (srgb)
5455 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
5457 /* Performance warning... */
5458 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
5459 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5462 else
5464 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
5466 /* Performance warning... */
5467 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
5468 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5472 if (!(surface->flags & SFLAG_INSYSMEM))
5474 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
5475 /* Lets hope we get it from somewhere... */
5476 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5479 /* TODO: Use already acquired context when possible. */
5480 context = context_acquire(device, NULL);
5482 surface_prepare_texture(surface, context, srgb);
5483 surface_bind_and_dirtify(surface, context, srgb);
5485 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
5487 surface->flags |= SFLAG_GLCKEY;
5488 surface->gl_color_key = surface->src_blt_color_key;
5490 else surface->flags &= ~SFLAG_GLCKEY;
5492 width = surface->resource.width;
5493 src_pitch = wined3d_surface_get_pitch(surface);
5495 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5496 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
5497 * called. */
5498 if ((convert != WINED3D_CT_NONE || format.convert) && (surface->flags & SFLAG_PBO))
5500 TRACE("Removing the pbo attached to surface %p.\n", surface);
5501 surface_remove_pbo(surface, gl_info);
5504 if (format.convert)
5506 /* This code is entered for texture formats which need a fixup. */
5507 UINT height = surface->resource.height;
5509 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
5510 dst_pitch = width * format.conv_byte_count;
5511 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5513 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5515 ERR("Out of memory (%u).\n", dst_pitch * height);
5516 context_release(context);
5517 return E_OUTOFMEMORY;
5519 format.convert(surface->resource.allocatedMemory, mem, src_pitch, src_pitch * height,
5520 dst_pitch, dst_pitch * height, width, height, 1);
5521 format.byte_count = format.conv_byte_count;
5522 src_pitch = dst_pitch;
5524 else if (convert != WINED3D_CT_NONE && surface->resource.allocatedMemory)
5526 /* This code is only entered for color keying fixups */
5527 UINT height = surface->resource.height;
5529 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
5530 dst_pitch = width * format.conv_byte_count;
5531 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5533 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5535 ERR("Out of memory (%u).\n", dst_pitch * height);
5536 context_release(context);
5537 return E_OUTOFMEMORY;
5539 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
5540 width, height, dst_pitch, convert, surface);
5541 format.byte_count = format.conv_byte_count;
5542 src_pitch = dst_pitch;
5544 else
5546 mem = surface->resource.allocatedMemory;
5549 data.buffer_object = surface->pbo;
5550 data.addr = mem;
5551 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
5553 context_release(context);
5555 /* Don't delete PBO memory. */
5556 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5557 HeapFree(GetProcessHeap(), 0, mem);
5559 return WINED3D_OK;
5562 static void surface_multisample_resolve(struct wined3d_surface *surface)
5564 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5566 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
5567 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
5569 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
5570 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
5573 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
5575 struct wined3d_device *device = surface->resource.device;
5576 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5577 HRESULT hr;
5579 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
5581 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5583 if (location == SFLAG_INTEXTURE && surface->flags & SFLAG_INDRAWABLE)
5585 struct wined3d_context *context = context_acquire(device, NULL);
5586 surface_load_ds_location(surface, context, location);
5587 context_release(context);
5588 return WINED3D_OK;
5590 else if (location & surface->flags && surface->draw_binding != SFLAG_INDRAWABLE)
5592 /* Already up to date, nothing to do. */
5593 return WINED3D_OK;
5595 else
5597 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
5598 debug_surflocation(surface->flags & SFLAG_LOCATIONS), debug_surflocation(location));
5599 return WINED3DERR_INVALIDCALL;
5603 if (surface->flags & location)
5605 TRACE("Location already up to date.\n");
5607 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
5608 && surface_need_pbo(surface, gl_info))
5609 surface_load_pbo(surface, gl_info);
5611 return WINED3D_OK;
5614 if (WARN_ON(d3d_surface))
5616 DWORD required_access = resource_access_from_location(location);
5617 if ((surface->resource.access_flags & required_access) != required_access)
5618 WARN("Operation requires %#x access, but surface only has %#x.\n",
5619 required_access, surface->resource.access_flags);
5622 if (!(surface->flags & SFLAG_LOCATIONS))
5624 ERR("Surface %p does not have any up to date location.\n", surface);
5625 surface->flags |= SFLAG_LOST;
5626 return WINED3DERR_DEVICELOST;
5629 switch (location)
5631 case SFLAG_INSYSMEM:
5632 surface_load_sysmem(surface, gl_info, rect);
5633 break;
5635 case SFLAG_INDRAWABLE:
5636 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
5637 return hr;
5638 break;
5640 case SFLAG_INRB_RESOLVED:
5641 surface_multisample_resolve(surface);
5642 break;
5644 case SFLAG_INTEXTURE:
5645 case SFLAG_INSRGBTEX:
5646 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
5647 return hr;
5648 break;
5650 default:
5651 ERR("Don't know how to handle location %#x.\n", location);
5652 break;
5655 if (!rect)
5657 surface->flags |= location;
5659 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
5660 surface_evict_sysmem(surface);
5663 return WINED3D_OK;
5666 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
5668 struct wined3d_swapchain *swapchain;
5670 /* Not on a swapchain - must be offscreen */
5671 if (!(swapchain = surface->swapchain))
5672 return TRUE;
5674 /* The front buffer is always onscreen */
5675 if (surface == swapchain->front_buffer) return FALSE;
5677 /* If the swapchain is rendered to an FBO, the backbuffer is
5678 * offscreen, otherwise onscreen */
5679 return swapchain->render_to_fbo;
5682 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
5683 /* Context activation is done by the caller. */
5684 static void ffp_blit_free(struct wined3d_device *device) { }
5686 /* Context activation is done by the caller. */
5687 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5689 const struct wined3d_gl_info *gl_info = context->gl_info;
5690 GLenum target;
5692 if (surface->container)
5693 target = surface->container->target;
5694 else
5695 target = surface->texture_target;
5697 gl_info->gl_ops.gl.p_glEnable(target);
5698 checkGLcall("glEnable(target)");
5700 return WINED3D_OK;
5703 /* Context activation is done by the caller. */
5704 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
5706 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
5707 checkGLcall("glDisable(GL_TEXTURE_2D)");
5708 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
5710 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5711 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5713 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
5715 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
5716 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5720 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5721 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5722 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5724 switch (blit_op)
5726 case WINED3D_BLIT_OP_COLOR_BLIT:
5727 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
5728 return FALSE;
5730 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5732 TRACE("Checking support for fixup:\n");
5733 dump_color_fixup_desc(src_format->color_fixup);
5736 /* We only support identity conversions. */
5737 if (!is_identity_fixup(src_format->color_fixup)
5738 || !is_identity_fixup(dst_format->color_fixup))
5740 TRACE("Fixups are not supported.\n");
5741 return FALSE;
5744 return TRUE;
5746 case WINED3D_BLIT_OP_COLOR_FILL:
5747 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
5748 return FALSE;
5750 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5752 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
5753 return FALSE;
5755 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
5757 TRACE("Color fill not supported\n");
5758 return FALSE;
5761 /* FIXME: We should reject color fills on formats with fixups,
5762 * but this would break P8 color fills for example. */
5764 return TRUE;
5766 case WINED3D_BLIT_OP_DEPTH_FILL:
5767 return TRUE;
5769 default:
5770 TRACE("Unsupported blit_op=%d\n", blit_op);
5771 return FALSE;
5775 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5776 const RECT *dst_rect, const struct wined3d_color *color)
5778 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
5779 struct wined3d_fb_state fb = {&dst_surface, NULL};
5781 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
5783 return WINED3D_OK;
5786 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
5787 struct wined3d_surface *surface, const RECT *rect, float depth)
5789 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
5790 struct wined3d_fb_state fb = {NULL, surface};
5792 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
5794 return WINED3D_OK;
5797 const struct blit_shader ffp_blit = {
5798 ffp_blit_alloc,
5799 ffp_blit_free,
5800 ffp_blit_set,
5801 ffp_blit_unset,
5802 ffp_blit_supported,
5803 ffp_blit_color_fill,
5804 ffp_blit_depth_fill,
5807 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
5809 return WINED3D_OK;
5812 /* Context activation is done by the caller. */
5813 static void cpu_blit_free(struct wined3d_device *device)
5817 /* Context activation is done by the caller. */
5818 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5820 return WINED3D_OK;
5823 /* Context activation is done by the caller. */
5824 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
5828 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5829 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5830 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5832 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
5834 return TRUE;
5837 return FALSE;
5840 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
5841 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
5842 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
5844 UINT row_block_count;
5845 const BYTE *src_row;
5846 BYTE *dst_row;
5847 UINT x, y;
5849 src_row = src_data;
5850 dst_row = dst_data;
5852 row_block_count = (update_w + format->block_width - 1) / format->block_width;
5854 if (!flags)
5856 for (y = 0; y < update_h; y += format->block_height)
5858 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
5859 src_row += src_pitch;
5860 dst_row += dst_pitch;
5863 return WINED3D_OK;
5866 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
5868 src_row += (((update_h / format->block_height) - 1) * src_pitch);
5870 switch (format->id)
5872 case WINED3DFMT_DXT1:
5873 for (y = 0; y < update_h; y += format->block_height)
5875 struct block
5877 WORD color[2];
5878 BYTE control_row[4];
5881 const struct block *s = (const struct block *)src_row;
5882 struct block *d = (struct block *)dst_row;
5884 for (x = 0; x < row_block_count; ++x)
5886 d[x].color[0] = s[x].color[0];
5887 d[x].color[1] = s[x].color[1];
5888 d[x].control_row[0] = s[x].control_row[3];
5889 d[x].control_row[1] = s[x].control_row[2];
5890 d[x].control_row[2] = s[x].control_row[1];
5891 d[x].control_row[3] = s[x].control_row[0];
5893 src_row -= src_pitch;
5894 dst_row += dst_pitch;
5896 return WINED3D_OK;
5898 case WINED3DFMT_DXT3:
5899 for (y = 0; y < update_h; y += format->block_height)
5901 struct block
5903 WORD alpha_row[4];
5904 WORD color[2];
5905 BYTE control_row[4];
5908 const struct block *s = (const struct block *)src_row;
5909 struct block *d = (struct block *)dst_row;
5911 for (x = 0; x < row_block_count; ++x)
5913 d[x].alpha_row[0] = s[x].alpha_row[3];
5914 d[x].alpha_row[1] = s[x].alpha_row[2];
5915 d[x].alpha_row[2] = s[x].alpha_row[1];
5916 d[x].alpha_row[3] = s[x].alpha_row[0];
5917 d[x].color[0] = s[x].color[0];
5918 d[x].color[1] = s[x].color[1];
5919 d[x].control_row[0] = s[x].control_row[3];
5920 d[x].control_row[1] = s[x].control_row[2];
5921 d[x].control_row[2] = s[x].control_row[1];
5922 d[x].control_row[3] = s[x].control_row[0];
5924 src_row -= src_pitch;
5925 dst_row += dst_pitch;
5927 return WINED3D_OK;
5929 default:
5930 FIXME("Compressed flip not implemented for format %s.\n",
5931 debug_d3dformat(format->id));
5932 return E_NOTIMPL;
5936 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
5937 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
5939 return E_NOTIMPL;
5942 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5943 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
5944 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5946 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
5947 const struct wined3d_format *src_format, *dst_format;
5948 struct wined3d_surface *orig_src = src_surface;
5949 struct wined3d_map_desc dst_map, src_map;
5950 const BYTE *sbase = NULL;
5951 HRESULT hr = WINED3D_OK;
5952 const BYTE *sbuf;
5953 BYTE *dbuf;
5954 int x, y;
5956 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5957 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5958 flags, fx, debug_d3dtexturefiltertype(filter));
5960 if (src_surface == dst_surface)
5962 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
5963 src_map = dst_map;
5964 src_format = dst_surface->resource.format;
5965 dst_format = src_format;
5967 else
5969 dst_format = dst_surface->resource.format;
5970 if (src_surface)
5972 if (dst_surface->resource.format->id != src_surface->resource.format->id)
5974 src_surface = surface_convert_format(src_surface, dst_format->id);
5975 if (!src_surface)
5977 /* The conv function writes a FIXME */
5978 WARN("Cannot convert source surface format to dest format.\n");
5979 goto release;
5982 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
5983 src_format = src_surface->resource.format;
5985 else
5987 src_format = dst_format;
5990 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
5993 bpp = dst_surface->resource.format->byte_count;
5994 srcheight = src_rect->bottom - src_rect->top;
5995 srcwidth = src_rect->right - src_rect->left;
5996 dstheight = dst_rect->bottom - dst_rect->top;
5997 dstwidth = dst_rect->right - dst_rect->left;
5998 width = (dst_rect->right - dst_rect->left) * bpp;
6000 if (src_surface)
6001 sbase = (BYTE *)src_map.data
6002 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
6003 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
6004 if (src_surface != dst_surface)
6005 dbuf = dst_map.data;
6006 else
6007 dbuf = (BYTE *)dst_map.data
6008 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
6009 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
6011 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
6013 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6015 if (src_surface == dst_surface)
6017 FIXME("Only plain blits supported on compressed surfaces.\n");
6018 hr = E_NOTIMPL;
6019 goto release;
6022 if (srcheight != dstheight || srcwidth != dstwidth)
6024 WARN("Stretching not supported on compressed surfaces.\n");
6025 hr = WINED3DERR_INVALIDCALL;
6026 goto release;
6029 if (!surface_check_block_align(src_surface, src_rect))
6031 WARN("Source rectangle not block-aligned.\n");
6032 hr = WINED3DERR_INVALIDCALL;
6033 goto release;
6036 if (!surface_check_block_align(dst_surface, dst_rect))
6038 WARN("Destination rectangle not block-aligned.\n");
6039 hr = WINED3DERR_INVALIDCALL;
6040 goto release;
6043 hr = surface_cpu_blt_compressed(sbase, dbuf,
6044 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
6045 src_format, flags, fx);
6046 goto release;
6049 /* First, all the 'source-less' blits */
6050 if (flags & WINEDDBLT_COLORFILL)
6052 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
6053 flags &= ~WINEDDBLT_COLORFILL;
6056 if (flags & WINEDDBLT_DEPTHFILL)
6058 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6060 if (flags & WINEDDBLT_ROP)
6062 /* Catch some degenerate cases here. */
6063 switch (fx->dwROP)
6065 case BLACKNESS:
6066 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
6067 break;
6068 case 0xaa0029: /* No-op */
6069 break;
6070 case WHITENESS:
6071 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
6072 break;
6073 case SRCCOPY: /* Well, we do that below? */
6074 break;
6075 default:
6076 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6077 goto error;
6079 flags &= ~WINEDDBLT_ROP;
6081 if (flags & WINEDDBLT_DDROPS)
6083 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6085 /* Now the 'with source' blits. */
6086 if (src_surface)
6088 int sx, xinc, sy, yinc;
6090 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6091 goto release;
6093 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
6094 && (srcwidth != dstwidth || srcheight != dstheight))
6096 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6097 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6100 xinc = (srcwidth << 16) / dstwidth;
6101 yinc = (srcheight << 16) / dstheight;
6103 if (!flags)
6105 /* No effects, we can cheat here. */
6106 if (dstwidth == srcwidth)
6108 if (dstheight == srcheight)
6110 /* No stretching in either direction. This needs to be as
6111 * fast as possible. */
6112 sbuf = sbase;
6114 /* Check for overlapping surfaces. */
6115 if (src_surface != dst_surface || dst_rect->top < src_rect->top
6116 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
6118 /* No overlap, or dst above src, so copy from top downwards. */
6119 for (y = 0; y < dstheight; ++y)
6121 memcpy(dbuf, sbuf, width);
6122 sbuf += src_map.row_pitch;
6123 dbuf += dst_map.row_pitch;
6126 else if (dst_rect->top > src_rect->top)
6128 /* Copy from bottom upwards. */
6129 sbuf += src_map.row_pitch * dstheight;
6130 dbuf += dst_map.row_pitch * dstheight;
6131 for (y = 0; y < dstheight; ++y)
6133 sbuf -= src_map.row_pitch;
6134 dbuf -= dst_map.row_pitch;
6135 memcpy(dbuf, sbuf, width);
6138 else
6140 /* Src and dst overlapping on the same line, use memmove. */
6141 for (y = 0; y < dstheight; ++y)
6143 memmove(dbuf, sbuf, width);
6144 sbuf += src_map.row_pitch;
6145 dbuf += dst_map.row_pitch;
6149 else
6151 /* Stretching in y direction only. */
6152 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6154 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6155 memcpy(dbuf, sbuf, width);
6156 dbuf += dst_map.row_pitch;
6160 else
6162 /* Stretching in X direction. */
6163 int last_sy = -1;
6164 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6166 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6168 if ((sy >> 16) == (last_sy >> 16))
6170 /* This source row is the same as last source row -
6171 * Copy the already stretched row. */
6172 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
6174 else
6176 #define STRETCH_ROW(type) \
6177 do { \
6178 const type *s = (const type *)sbuf; \
6179 type *d = (type *)dbuf; \
6180 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6181 d[x] = s[sx >> 16]; \
6182 } while(0)
6184 switch(bpp)
6186 case 1:
6187 STRETCH_ROW(BYTE);
6188 break;
6189 case 2:
6190 STRETCH_ROW(WORD);
6191 break;
6192 case 4:
6193 STRETCH_ROW(DWORD);
6194 break;
6195 case 3:
6197 const BYTE *s;
6198 BYTE *d = dbuf;
6199 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6201 DWORD pixel;
6203 s = sbuf + 3 * (sx >> 16);
6204 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6205 d[0] = (pixel ) & 0xff;
6206 d[1] = (pixel >> 8) & 0xff;
6207 d[2] = (pixel >> 16) & 0xff;
6208 d += 3;
6210 break;
6212 default:
6213 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6214 hr = WINED3DERR_NOTAVAILABLE;
6215 goto error;
6217 #undef STRETCH_ROW
6219 dbuf += dst_map.row_pitch;
6220 last_sy = sy;
6224 else
6226 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
6227 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
6228 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
6229 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6231 /* The color keying flags are checked for correctness in ddraw */
6232 if (flags & WINEDDBLT_KEYSRC)
6234 keylow = src_surface->src_blt_color_key.color_space_low_value;
6235 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
6237 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6239 keylow = fx->ddckSrcColorkey.color_space_low_value;
6240 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
6243 if (flags & WINEDDBLT_KEYDEST)
6245 /* Destination color keys are taken from the source surface! */
6246 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
6247 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
6249 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6251 destkeylow = fx->ddckDestColorkey.color_space_low_value;
6252 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
6255 if (bpp == 1)
6257 keymask = 0xff;
6259 else
6261 DWORD masks[3];
6262 get_color_masks(src_format, masks);
6263 keymask = masks[0]
6264 | masks[1]
6265 | masks[2];
6267 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6270 if (flags & WINEDDBLT_DDFX)
6272 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6273 LONG tmpxy;
6274 dTopLeft = dbuf;
6275 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6276 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
6277 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6279 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6281 /* I don't think we need to do anything about this flag */
6282 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6284 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6286 tmp = dTopRight;
6287 dTopRight = dTopLeft;
6288 dTopLeft = tmp;
6289 tmp = dBottomRight;
6290 dBottomRight = dBottomLeft;
6291 dBottomLeft = tmp;
6292 dstxinc = dstxinc * -1;
6294 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6296 tmp = dTopLeft;
6297 dTopLeft = dBottomLeft;
6298 dBottomLeft = tmp;
6299 tmp = dTopRight;
6300 dTopRight = dBottomRight;
6301 dBottomRight = tmp;
6302 dstyinc = dstyinc * -1;
6304 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6306 /* I don't think we need to do anything about this flag */
6307 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6309 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6311 tmp = dBottomRight;
6312 dBottomRight = dTopLeft;
6313 dTopLeft = tmp;
6314 tmp = dBottomLeft;
6315 dBottomLeft = dTopRight;
6316 dTopRight = tmp;
6317 dstxinc = dstxinc * -1;
6318 dstyinc = dstyinc * -1;
6320 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6322 tmp = dTopLeft;
6323 dTopLeft = dBottomLeft;
6324 dBottomLeft = dBottomRight;
6325 dBottomRight = dTopRight;
6326 dTopRight = tmp;
6327 tmpxy = dstxinc;
6328 dstxinc = dstyinc;
6329 dstyinc = tmpxy;
6330 dstxinc = dstxinc * -1;
6332 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6334 tmp = dTopLeft;
6335 dTopLeft = dTopRight;
6336 dTopRight = dBottomRight;
6337 dBottomRight = dBottomLeft;
6338 dBottomLeft = tmp;
6339 tmpxy = dstxinc;
6340 dstxinc = dstyinc;
6341 dstyinc = tmpxy;
6342 dstyinc = dstyinc * -1;
6344 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6346 /* I don't think we need to do anything about this flag */
6347 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6349 dbuf = dTopLeft;
6350 flags &= ~(WINEDDBLT_DDFX);
6353 #define COPY_COLORKEY_FX(type) \
6354 do { \
6355 const type *s; \
6356 type *d = (type *)dbuf, *dx, tmp; \
6357 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6359 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
6360 dx = d; \
6361 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6363 tmp = s[sx >> 16]; \
6364 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6365 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6367 dx[0] = tmp; \
6369 dx = (type *)(((BYTE *)dx) + dstxinc); \
6371 d = (type *)(((BYTE *)d) + dstyinc); \
6373 } while(0)
6375 switch (bpp)
6377 case 1:
6378 COPY_COLORKEY_FX(BYTE);
6379 break;
6380 case 2:
6381 COPY_COLORKEY_FX(WORD);
6382 break;
6383 case 4:
6384 COPY_COLORKEY_FX(DWORD);
6385 break;
6386 case 3:
6388 const BYTE *s;
6389 BYTE *d = dbuf, *dx;
6390 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6392 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6393 dx = d;
6394 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
6396 DWORD pixel, dpixel = 0;
6397 s = sbuf + 3 * (sx>>16);
6398 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6399 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
6400 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
6401 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
6403 dx[0] = (pixel ) & 0xff;
6404 dx[1] = (pixel >> 8) & 0xff;
6405 dx[2] = (pixel >> 16) & 0xff;
6407 dx += dstxinc;
6409 d += dstyinc;
6411 break;
6413 default:
6414 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
6415 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
6416 hr = WINED3DERR_NOTAVAILABLE;
6417 goto error;
6418 #undef COPY_COLORKEY_FX
6423 error:
6424 if (flags && FIXME_ON(d3d_surface))
6426 FIXME("\tUnsupported flags: %#x.\n", flags);
6429 release:
6430 wined3d_surface_unmap(dst_surface);
6431 if (src_surface && src_surface != dst_surface)
6432 wined3d_surface_unmap(src_surface);
6433 /* Release the converted surface, if any. */
6434 if (src_surface && src_surface != orig_src)
6435 wined3d_surface_decref(src_surface);
6437 return hr;
6440 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6441 const RECT *dst_rect, const struct wined3d_color *color)
6443 static const RECT src_rect;
6444 WINEDDBLTFX BltFx;
6446 memset(&BltFx, 0, sizeof(BltFx));
6447 BltFx.dwSize = sizeof(BltFx);
6448 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
6449 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
6450 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
6453 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
6454 struct wined3d_surface *surface, const RECT *rect, float depth)
6456 FIXME("Depth filling not implemented by cpu_blit.\n");
6457 return WINED3DERR_INVALIDCALL;
6460 const struct blit_shader cpu_blit = {
6461 cpu_blit_alloc,
6462 cpu_blit_free,
6463 cpu_blit_set,
6464 cpu_blit_unset,
6465 cpu_blit_supported,
6466 cpu_blit_color_fill,
6467 cpu_blit_depth_fill,
6470 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
6471 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
6472 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
6474 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
6475 struct wined3d_device *device = dst_surface->resource.device;
6476 DWORD src_ds_flags, dst_ds_flags;
6477 RECT src_rect, dst_rect;
6478 BOOL scale, convert;
6480 static const DWORD simple_blit = WINEDDBLT_ASYNC
6481 | WINEDDBLT_COLORFILL
6482 | WINEDDBLT_WAIT
6483 | WINEDDBLT_DEPTHFILL
6484 | WINEDDBLT_DONOTWAIT;
6486 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6487 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
6488 flags, fx, debug_d3dtexturefiltertype(filter));
6489 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
6491 if (fx)
6493 TRACE("dwSize %#x.\n", fx->dwSize);
6494 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
6495 TRACE("dwROP %#x.\n", fx->dwROP);
6496 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
6497 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
6498 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
6499 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
6500 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
6501 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
6502 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
6503 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
6504 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
6505 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
6506 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
6507 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
6508 TRACE("dwReserved %#x.\n", fx->dwReserved);
6509 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
6510 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
6511 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
6512 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
6513 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
6514 TRACE("ddckDestColorkey {%#x, %#x}.\n",
6515 fx->ddckDestColorkey.color_space_low_value,
6516 fx->ddckDestColorkey.color_space_high_value);
6517 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
6518 fx->ddckSrcColorkey.color_space_low_value,
6519 fx->ddckSrcColorkey.color_space_high_value);
6522 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
6524 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
6525 return WINEDDERR_SURFACEBUSY;
6528 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
6530 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
6531 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
6532 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
6533 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
6534 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
6536 WARN("The application gave us a bad destination rectangle.\n");
6537 return WINEDDERR_INVALIDRECT;
6540 if (src_surface)
6542 surface_get_rect(src_surface, src_rect_in, &src_rect);
6544 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
6545 || src_rect.left > src_surface->resource.width || src_rect.left < 0
6546 || src_rect.top > src_surface->resource.height || src_rect.top < 0
6547 || src_rect.right > src_surface->resource.width || src_rect.right < 0
6548 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
6550 WARN("Application gave us bad source rectangle for Blt.\n");
6551 return WINEDDERR_INVALIDRECT;
6554 else
6556 memset(&src_rect, 0, sizeof(src_rect));
6559 if (!fx || !(fx->dwDDFX))
6560 flags &= ~WINEDDBLT_DDFX;
6562 if (flags & WINEDDBLT_WAIT)
6563 flags &= ~WINEDDBLT_WAIT;
6565 if (flags & WINEDDBLT_ASYNC)
6567 static unsigned int once;
6569 if (!once++)
6570 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
6571 flags &= ~WINEDDBLT_ASYNC;
6574 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
6575 if (flags & WINEDDBLT_DONOTWAIT)
6577 static unsigned int once;
6579 if (!once++)
6580 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
6581 flags &= ~WINEDDBLT_DONOTWAIT;
6584 if (!device->d3d_initialized)
6586 WARN("D3D not initialized, using fallback.\n");
6587 goto cpu;
6590 /* We want to avoid invalidating the sysmem location for converted
6591 * surfaces, since otherwise we'd have to convert the data back when
6592 * locking them. */
6593 if (dst_surface->flags & SFLAG_CONVERTED)
6595 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
6596 goto cpu;
6599 if (flags & ~simple_blit)
6601 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
6602 goto fallback;
6605 if (src_surface)
6606 src_swapchain = src_surface->swapchain;
6607 else
6608 src_swapchain = NULL;
6610 dst_swapchain = dst_surface->swapchain;
6612 /* This isn't strictly needed. FBO blits for example could deal with
6613 * cross-swapchain blits by first downloading the source to a texture
6614 * before switching to the destination context. We just have this here to
6615 * not have to deal with the issue, since cross-swapchain blits should be
6616 * rare. */
6617 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
6619 FIXME("Using fallback for cross-swapchain blit.\n");
6620 goto fallback;
6623 scale = src_surface
6624 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
6625 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
6626 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
6628 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
6629 if (src_surface)
6630 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
6631 else
6632 src_ds_flags = 0;
6634 if (src_ds_flags || dst_ds_flags)
6636 if (flags & WINEDDBLT_DEPTHFILL)
6638 float depth;
6640 TRACE("Depth fill.\n");
6642 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
6643 return WINED3DERR_INVALIDCALL;
6645 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
6646 return WINED3D_OK;
6648 else
6650 if (src_ds_flags != dst_ds_flags)
6652 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
6653 return WINED3DERR_INVALIDCALL;
6656 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->draw_binding, &src_rect,
6657 dst_surface, dst_surface->draw_binding, &dst_rect)))
6658 return WINED3D_OK;
6661 else
6663 /* In principle this would apply to depth blits as well, but we don't
6664 * implement those in the CPU blitter at the moment. */
6665 if ((dst_surface->flags & SFLAG_INSYSMEM)
6666 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
6668 if (scale)
6669 TRACE("Not doing sysmem blit because of scaling.\n");
6670 else if (convert)
6671 TRACE("Not doing sysmem blit because of format conversion.\n");
6672 else
6673 goto cpu;
6676 if (flags & WINEDDBLT_COLORFILL)
6678 struct wined3d_color color;
6680 TRACE("Color fill.\n");
6682 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
6683 goto fallback;
6685 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
6686 return WINED3D_OK;
6688 else
6690 TRACE("Color blit.\n");
6692 /* Upload */
6693 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
6695 if (scale)
6696 TRACE("Not doing upload because of scaling.\n");
6697 else if (convert)
6698 TRACE("Not doing upload because of format conversion.\n");
6699 else
6701 POINT dst_point = {dst_rect.left, dst_rect.top};
6703 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
6705 if (!surface_is_offscreen(dst_surface))
6706 surface_load_location(dst_surface, dst_surface->draw_binding, NULL);
6707 return WINED3D_OK;
6712 /* Use present for back -> front blits. The idea behind this is
6713 * that present is potentially faster than a blit, in particular
6714 * when FBO blits aren't available. Some ddraw applications like
6715 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
6716 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
6717 * applications can't blit directly to the frontbuffer. */
6718 if (dst_swapchain && dst_swapchain->back_buffers
6719 && dst_surface == dst_swapchain->front_buffer
6720 && src_surface == dst_swapchain->back_buffers[0])
6722 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
6724 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
6726 /* Set the swap effect to COPY, we don't want the backbuffer
6727 * to become undefined. */
6728 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
6729 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
6730 dst_swapchain->desc.swap_effect = swap_effect;
6732 return WINED3D_OK;
6735 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6736 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6737 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6739 TRACE("Using FBO blit.\n");
6741 surface_blt_fbo(device, filter,
6742 src_surface, src_surface->draw_binding, &src_rect,
6743 dst_surface, dst_surface->draw_binding, &dst_rect);
6744 surface_validate_location(dst_surface, dst_surface->draw_binding);
6745 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
6747 return WINED3D_OK;
6750 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6751 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6752 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6754 TRACE("Using arbfp blit.\n");
6756 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
6757 return WINED3D_OK;
6762 fallback:
6763 /* Special cases for render targets. */
6764 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
6765 return WINED3D_OK;
6767 cpu:
6769 /* For the rest call the X11 surface implementation. For render targets
6770 * this should be implemented OpenGL accelerated in surface_blt_special(),
6771 * other blits are rather rare. */
6772 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
6775 static HRESULT surface_init(struct wined3d_surface *surface, UINT alignment, UINT width, UINT height,
6776 enum wined3d_multisample_type multisample_type, UINT multisample_quality,
6777 struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
6778 enum wined3d_pool pool, DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops)
6780 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6781 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
6782 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
6783 unsigned int resource_size;
6784 HRESULT hr;
6786 if (multisample_quality > 0)
6788 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
6789 multisample_quality = 0;
6792 /* Quick lockable sanity check.
6793 * TODO: remove this after surfaces, usage and lockability have been debugged properly
6794 * this function is too deep to need to care about things like this.
6795 * Levels need to be checked too, since they all affect what can be done. */
6796 switch (pool)
6798 case WINED3D_POOL_SCRATCH:
6799 if (!lockable)
6801 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
6802 "which are mutually exclusive, setting lockable to TRUE.\n");
6803 lockable = TRUE;
6805 break;
6807 case WINED3D_POOL_SYSTEM_MEM:
6808 if (!lockable)
6809 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
6810 break;
6812 case WINED3D_POOL_MANAGED:
6813 if (usage & WINED3DUSAGE_DYNAMIC)
6814 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
6815 break;
6817 case WINED3D_POOL_DEFAULT:
6818 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
6819 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
6820 break;
6822 default:
6823 FIXME("Unknown pool %#x.\n", pool);
6824 break;
6827 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3D_POOL_DEFAULT)
6828 FIXME("Trying to create a render target that isn't in the default pool.\n");
6830 /* FIXME: Check that the format is supported by the device. */
6832 resource_size = wined3d_format_calculate_size(format, alignment, width, height, 1);
6833 if (!resource_size)
6834 return WINED3DERR_INVALIDCALL;
6836 if (device->wined3d->flags & WINED3D_NO3D)
6837 surface->surface_ops = &gdi_surface_ops;
6838 else
6839 surface->surface_ops = &surface_ops;
6841 hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
6842 multisample_type, multisample_quality, usage, pool, width, height, 1,
6843 resource_size, parent, parent_ops, &surface_resource_ops);
6844 if (FAILED(hr))
6846 WARN("Failed to initialize resource, returning %#x.\n", hr);
6847 return hr;
6850 /* "Standalone" surface. */
6851 surface_set_container(surface, NULL);
6853 list_init(&surface->overlays);
6855 /* Flags */
6856 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
6857 if (flags & WINED3D_SURFACE_DISCARD)
6858 surface->flags |= SFLAG_DISCARD;
6859 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
6860 surface->flags |= SFLAG_PIN_SYSMEM;
6861 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
6862 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
6864 /* I'm not sure if this qualifies as a hack or as an optimization. It
6865 * seems reasonable to assume that lockable render targets will get
6866 * locked, so we might as well set SFLAG_DYNLOCK right at surface
6867 * creation. However, the other reason we want to do this is that several
6868 * ddraw applications access surface memory while the surface isn't
6869 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
6870 * future locks prevents these from crashing. */
6871 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
6872 surface->flags |= SFLAG_DYNLOCK;
6874 /* Mark the texture as dirty so that it gets loaded first time around. */
6875 surface_add_dirty_rect(surface, NULL);
6876 list_init(&surface->renderbuffers);
6878 TRACE("surface %p, memory %p, size %u\n",
6879 surface, surface->resource.allocatedMemory, surface->resource.size);
6881 /* Call the private setup routine */
6882 hr = surface->surface_ops->surface_private_setup(surface);
6883 if (FAILED(hr))
6885 ERR("Private setup failed, returning %#x\n", hr);
6886 surface_cleanup(surface);
6887 return hr;
6890 /* Similar to lockable rendertargets above, creating the DIB section
6891 * during surface initialization prevents the sysmem pointer from changing
6892 * after a wined3d_surface_getdc() call. */
6893 if ((usage & WINED3DUSAGE_OWNDC) && !surface->hDC
6894 && SUCCEEDED(surface_create_dib_section(surface)))
6896 wined3d_resource_free_sysmem(&surface->resource);
6897 surface->resource.allocatedMemory = surface->dib.bitmap_data;
6900 return hr;
6903 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
6904 enum wined3d_format_id format_id, DWORD usage, enum wined3d_pool pool,
6905 enum wined3d_multisample_type multisample_type, DWORD multisample_quality, DWORD flags,
6906 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
6908 struct wined3d_surface *object;
6909 HRESULT hr;
6911 TRACE("device %p, width %u, height %u, format %s\n",
6912 device, width, height, debug_d3dformat(format_id));
6913 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
6914 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
6915 TRACE("flags %#x, parent %p, parent_ops %p.\n", flags, parent, parent_ops);
6917 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
6918 if (!object)
6919 return WINED3DERR_OUTOFVIDEOMEMORY;
6921 if (FAILED(hr = surface_init(object, device->surface_alignment, width, height, multisample_type,
6922 multisample_quality, device, usage, format_id, pool, flags, parent, parent_ops)))
6924 WARN("Failed to initialize surface, returning %#x.\n", hr);
6925 HeapFree(GetProcessHeap(), 0, object);
6926 return hr;
6929 TRACE("Created surface %p.\n", object);
6930 *surface = object;
6932 return hr;