wined3d: Add a function for allocating aligned resource memory.
[wine.git] / dlls / wined3d / surface.c
blobefcd4924b6c0201e9d4e498dacebab3141df0390
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 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
38 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
39 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter);
40 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
41 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *fx,
42 enum wined3d_texture_filter_type filter);
44 static void surface_cleanup(struct wined3d_surface *surface)
46 struct wined3d_surface *overlay, *cur;
48 TRACE("surface %p.\n", surface);
50 if (surface->texture_name || (surface->flags & SFLAG_PBO)
51 || surface->rb_multisample || surface->rb_resolved
52 || !list_empty(&surface->renderbuffers))
54 struct wined3d_renderbuffer_entry *entry, *entry2;
55 const struct wined3d_gl_info *gl_info;
56 struct wined3d_context *context;
58 context = context_acquire(surface->resource.device, NULL);
59 gl_info = context->gl_info;
61 if (surface->texture_name)
63 TRACE("Deleting texture %u.\n", surface->texture_name);
64 gl_info->gl_ops.gl.p_glDeleteTextures(1, &surface->texture_name);
67 if (surface->flags & SFLAG_PBO)
69 TRACE("Deleting PBO %u.\n", surface->pbo);
70 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
73 if (surface->rb_multisample)
75 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
76 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
79 if (surface->rb_resolved)
81 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
82 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
85 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
87 TRACE("Deleting renderbuffer %u.\n", entry->id);
88 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
89 HeapFree(GetProcessHeap(), 0, entry);
92 context_release(context);
95 if (surface->flags & SFLAG_DIBSECTION)
97 DeleteDC(surface->hDC);
98 DeleteObject(surface->dib.DIBsection);
99 surface->dib.bitmap_data = NULL;
100 surface->resource.allocatedMemory = NULL;
103 if (surface->flags & SFLAG_USERPTR)
104 wined3d_surface_set_mem(surface, NULL, 0);
105 if (surface->overlay_dest)
106 list_remove(&surface->overlay_entry);
108 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
110 list_remove(&overlay->overlay_entry);
111 overlay->overlay_dest = NULL;
114 resource_cleanup(&surface->resource);
117 void surface_update_draw_binding(struct wined3d_surface *surface)
119 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
120 surface->draw_binding = SFLAG_INDRAWABLE;
121 else if (surface->resource.multisample_type)
122 surface->draw_binding = SFLAG_INRB_MULTISAMPLE;
123 else
124 surface->draw_binding = SFLAG_INTEXTURE;
127 void surface_set_swapchain(struct wined3d_surface *surface, struct wined3d_swapchain *swapchain)
129 TRACE("surface %p, swapchain %p.\n", surface, swapchain);
131 if (swapchain)
133 surface->get_drawable_size = get_drawable_size_swapchain;
135 else
137 switch (wined3d_settings.offscreen_rendering_mode)
139 case ORM_FBO:
140 surface->get_drawable_size = get_drawable_size_fbo;
141 break;
143 case ORM_BACKBUFFER:
144 surface->get_drawable_size = get_drawable_size_backbuffer;
145 break;
147 default:
148 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
149 return;
153 surface->swapchain = swapchain;
154 surface_update_draw_binding(surface);
157 void surface_set_container(struct wined3d_surface *surface, struct wined3d_texture *container)
159 TRACE("surface %p, container %p.\n", surface, container);
161 if (!surface->swapchain)
163 switch (wined3d_settings.offscreen_rendering_mode)
165 case ORM_FBO:
166 surface->get_drawable_size = get_drawable_size_fbo;
167 break;
169 case ORM_BACKBUFFER:
170 surface->get_drawable_size = get_drawable_size_backbuffer;
171 break;
173 default:
174 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
175 return;
179 surface->container = container;
180 surface_update_draw_binding(surface);
183 struct blt_info
185 GLenum binding;
186 GLenum bind_target;
187 enum tex_types tex_type;
188 GLfloat coords[4][3];
191 struct float_rect
193 float l;
194 float t;
195 float r;
196 float b;
199 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
201 f->l = ((r->left * 2.0f) / w) - 1.0f;
202 f->t = ((r->top * 2.0f) / h) - 1.0f;
203 f->r = ((r->right * 2.0f) / w) - 1.0f;
204 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
207 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
209 GLfloat (*coords)[3] = info->coords;
210 struct float_rect f;
212 switch (target)
214 default:
215 FIXME("Unsupported texture target %#x\n", target);
216 /* Fall back to GL_TEXTURE_2D */
217 case GL_TEXTURE_2D:
218 info->binding = GL_TEXTURE_BINDING_2D;
219 info->bind_target = GL_TEXTURE_2D;
220 info->tex_type = tex_2d;
221 coords[0][0] = (float)rect->left / w;
222 coords[0][1] = (float)rect->top / h;
223 coords[0][2] = 0.0f;
225 coords[1][0] = (float)rect->right / w;
226 coords[1][1] = (float)rect->top / h;
227 coords[1][2] = 0.0f;
229 coords[2][0] = (float)rect->left / w;
230 coords[2][1] = (float)rect->bottom / h;
231 coords[2][2] = 0.0f;
233 coords[3][0] = (float)rect->right / w;
234 coords[3][1] = (float)rect->bottom / h;
235 coords[3][2] = 0.0f;
236 break;
238 case GL_TEXTURE_RECTANGLE_ARB:
239 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
240 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
241 info->tex_type = tex_rect;
242 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
243 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
244 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
245 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
246 break;
248 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
249 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
250 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
251 info->tex_type = tex_cube;
252 cube_coords_float(rect, w, h, &f);
254 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
255 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
256 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
257 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
258 break;
260 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
261 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
262 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
263 info->tex_type = tex_cube;
264 cube_coords_float(rect, w, h, &f);
266 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
267 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
268 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
269 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
270 break;
272 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
273 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
274 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
275 info->tex_type = tex_cube;
276 cube_coords_float(rect, w, h, &f);
278 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
279 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
280 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
281 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
282 break;
284 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
285 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
286 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
287 info->tex_type = tex_cube;
288 cube_coords_float(rect, w, h, &f);
290 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
291 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
292 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
293 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
294 break;
296 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
297 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
298 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
299 info->tex_type = tex_cube;
300 cube_coords_float(rect, w, h, &f);
302 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
303 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
304 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
305 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
306 break;
308 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
309 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
310 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
311 info->tex_type = tex_cube;
312 cube_coords_float(rect, w, h, &f);
314 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
315 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
316 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
317 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
318 break;
322 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
324 if (rect_in)
325 *rect_out = *rect_in;
326 else
328 rect_out->left = 0;
329 rect_out->top = 0;
330 rect_out->right = surface->resource.width;
331 rect_out->bottom = surface->resource.height;
335 /* Context activation is done by the caller. */
336 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
337 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
339 const struct wined3d_gl_info *gl_info = context->gl_info;
340 struct blt_info info;
342 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
344 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
345 checkGLcall("glEnable(bind_target)");
347 context_bind_texture(context, info.bind_target, src_surface->texture_name);
349 /* Filtering for StretchRect */
350 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
351 wined3d_gl_mag_filter(magLookup, filter));
352 checkGLcall("glTexParameteri");
353 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
354 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
355 checkGLcall("glTexParameteri");
356 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
357 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
358 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
359 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
360 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
361 checkGLcall("glTexEnvi");
363 /* Draw a quad */
364 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
365 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
366 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
368 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
369 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
371 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
372 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
374 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
375 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
376 gl_info->gl_ops.gl.p_glEnd();
378 /* Unbind the texture */
379 context_bind_texture(context, info.bind_target, 0);
381 /* We changed the filtering settings on the texture. Inform the
382 * container about this to get the filters reset properly next draw. */
383 if (src_surface->container)
385 struct wined3d_texture *texture = src_surface->container;
386 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_POINT;
387 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
388 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_NONE;
389 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
393 /* Works correctly only for <= 4 bpp formats. */
394 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
396 masks[0] = ((1 << format->red_size) - 1) << format->red_offset;
397 masks[1] = ((1 << format->green_size) - 1) << format->green_offset;
398 masks[2] = ((1 << format->blue_size) - 1) << format->blue_offset;
401 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
403 const struct wined3d_format *format = surface->resource.format;
404 SYSTEM_INFO sysInfo;
405 BITMAPINFO *b_info;
406 int extraline = 0;
407 DWORD *masks;
409 TRACE("surface %p.\n", surface);
411 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
413 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
414 return WINED3DERR_INVALIDCALL;
417 switch (format->byte_count)
419 case 2:
420 case 4:
421 /* Allocate extra space to store the RGB bit masks. */
422 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
423 break;
425 case 3:
426 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
427 break;
429 default:
430 /* Allocate extra space for a palette. */
431 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
432 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
433 break;
436 if (!b_info)
437 return E_OUTOFMEMORY;
439 /* Some applications access the surface in via DWORDs, and do not take
440 * the necessary care at the end of the surface. So we need at least
441 * 4 extra bytes at the end of the surface. Check against the page size,
442 * if the last page used for the surface has at least 4 spare bytes we're
443 * safe, otherwise add an extra line to the DIB section. */
444 GetSystemInfo(&sysInfo);
445 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
447 extraline = 1;
448 TRACE("Adding an extra line to the DIB section.\n");
451 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
452 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
453 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
454 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
455 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
456 * wined3d_surface_get_pitch(surface);
457 b_info->bmiHeader.biPlanes = 1;
458 b_info->bmiHeader.biBitCount = format->byte_count * 8;
460 b_info->bmiHeader.biXPelsPerMeter = 0;
461 b_info->bmiHeader.biYPelsPerMeter = 0;
462 b_info->bmiHeader.biClrUsed = 0;
463 b_info->bmiHeader.biClrImportant = 0;
465 /* Get the bit masks */
466 masks = (DWORD *)b_info->bmiColors;
467 switch (surface->resource.format->id)
469 case WINED3DFMT_B8G8R8_UNORM:
470 b_info->bmiHeader.biCompression = BI_RGB;
471 break;
473 case WINED3DFMT_B5G5R5X1_UNORM:
474 case WINED3DFMT_B5G5R5A1_UNORM:
475 case WINED3DFMT_B4G4R4A4_UNORM:
476 case WINED3DFMT_B4G4R4X4_UNORM:
477 case WINED3DFMT_B2G3R3_UNORM:
478 case WINED3DFMT_B2G3R3A8_UNORM:
479 case WINED3DFMT_R10G10B10A2_UNORM:
480 case WINED3DFMT_R8G8B8A8_UNORM:
481 case WINED3DFMT_R8G8B8X8_UNORM:
482 case WINED3DFMT_B10G10R10A2_UNORM:
483 case WINED3DFMT_B5G6R5_UNORM:
484 case WINED3DFMT_R16G16B16A16_UNORM:
485 b_info->bmiHeader.biCompression = BI_BITFIELDS;
486 get_color_masks(format, masks);
487 break;
489 default:
490 /* Don't know palette */
491 b_info->bmiHeader.biCompression = BI_RGB;
492 break;
495 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
496 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
497 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
498 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
500 if (!surface->dib.DIBsection)
502 ERR("Failed to create DIB section.\n");
503 HeapFree(GetProcessHeap(), 0, b_info);
504 return HRESULT_FROM_WIN32(GetLastError());
507 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
508 /* Copy the existing surface to the dib section. */
509 if (surface->resource.allocatedMemory)
511 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
512 surface->resource.height * wined3d_surface_get_pitch(surface));
514 else
516 /* This is to make maps read the GL texture although memory is allocated. */
517 surface->flags &= ~SFLAG_INSYSMEM;
519 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
521 HeapFree(GetProcessHeap(), 0, b_info);
523 /* Now allocate a DC. */
524 surface->hDC = CreateCompatibleDC(0);
525 SelectObject(surface->hDC, surface->dib.DIBsection);
526 TRACE("Using wined3d palette %p.\n", surface->palette);
527 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
529 surface->flags |= SFLAG_DIBSECTION;
531 return WINED3D_OK;
534 static BOOL surface_need_pbo(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
536 if (surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
537 return FALSE;
538 if (!(surface->flags & SFLAG_DYNLOCK))
539 return FALSE;
540 if (surface->flags & (SFLAG_CONVERTED | SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM))
541 return FALSE;
542 if (!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT])
543 return FALSE;
545 return TRUE;
548 static void surface_load_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
550 struct wined3d_context *context;
551 GLenum error;
553 context = context_acquire(surface->resource.device, NULL);
555 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
556 error = gl_info->gl_ops.gl.p_glGetError();
557 if (!surface->pbo || error != GL_NO_ERROR)
558 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
560 TRACE("Binding PBO %u.\n", surface->pbo);
562 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
563 checkGLcall("glBindBufferARB");
565 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
566 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
567 checkGLcall("glBufferDataARB");
569 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
570 checkGLcall("glBindBufferARB");
572 /* We don't need the system memory anymore and we can't even use it for PBOs. */
573 if (!(surface->flags & SFLAG_CLIENT))
575 wined3d_resource_free_sysmem(surface->resource.heap_memory);
576 surface->resource.heap_memory = NULL;
578 surface->resource.allocatedMemory = NULL;
579 surface->flags |= SFLAG_PBO;
580 context_release(context);
583 static void surface_prepare_system_memory(struct wined3d_surface *surface)
585 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
587 TRACE("surface %p.\n", surface);
589 if (!(surface->flags & SFLAG_PBO) && surface_need_pbo(surface, gl_info))
590 surface_load_pbo(surface, gl_info);
591 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
593 /* Whatever surface we have, make sure that there is memory allocated
594 * for the downloaded copy, or a PBO to map. */
595 if (!surface->resource.heap_memory)
596 surface->resource.heap_memory = wined3d_resource_allocate_sysmem(surface->resource.size);
598 surface->resource.allocatedMemory = surface->resource.heap_memory;
600 if (surface->flags & SFLAG_INSYSMEM)
601 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
605 static void surface_evict_sysmem(struct wined3d_surface *surface)
607 if (surface->resource.map_count || (surface->flags & SFLAG_DONOTFREE))
608 return;
610 wined3d_resource_free_sysmem(surface->resource.heap_memory);
611 surface->resource.allocatedMemory = NULL;
612 surface->resource.heap_memory = NULL;
613 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
616 /* Context activation is done by the caller. */
617 static void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
619 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
621 if (surface->container)
623 struct wined3d_texture *texture = surface->container;
625 TRACE("Passing to container (%p).\n", texture);
626 texture->texture_ops->texture_bind(texture, context, srgb);
628 else
630 const struct wined3d_gl_info *gl_info = context->gl_info;
632 if (surface->texture_level)
634 ERR("Standalone surface %p is non-zero texture level %u.\n",
635 surface, surface->texture_level);
638 if (srgb)
639 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
641 if (!surface->texture_name)
643 gl_info->gl_ops.gl.p_glGenTextures(1, &surface->texture_name);
644 checkGLcall("glGenTextures");
646 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
648 context_bind_texture(context, surface->texture_target, surface->texture_name);
649 gl_info->gl_ops.gl.p_glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
650 gl_info->gl_ops.gl.p_glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
651 gl_info->gl_ops.gl.p_glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
652 gl_info->gl_ops.gl.p_glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
653 gl_info->gl_ops.gl.p_glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
654 checkGLcall("glTexParameteri");
656 else
658 context_bind_texture(context, surface->texture_target, surface->texture_name);
663 /* Context activation is done by the caller. */
664 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
665 struct wined3d_context *context, BOOL srgb)
667 struct wined3d_device *device = surface->resource.device;
668 DWORD active_sampler;
670 /* We don't need a specific texture unit, but after binding the texture
671 * the current unit is dirty. Read the unit back instead of switching to
672 * 0, this avoids messing around with the state manager's GL states. The
673 * current texture unit should always be a valid one.
675 * To be more specific, this is tricky because we can implicitly be
676 * called from sampler() in state.c. This means we can't touch anything
677 * other than whatever happens to be the currently active texture, or we
678 * would risk marking already applied sampler states dirty again. */
679 active_sampler = device->rev_tex_unit_map[context->active_texture];
681 if (active_sampler != WINED3D_UNMAPPED_STAGE)
682 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
683 surface_bind(surface, context, srgb);
686 static void surface_force_reload(struct wined3d_surface *surface)
688 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
691 static void surface_release_client_storage(struct wined3d_surface *surface)
693 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
694 const struct wined3d_gl_info *gl_info = context->gl_info;
696 if (surface->texture_name)
698 surface_bind_and_dirtify(surface, context, FALSE);
699 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
700 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
702 if (surface->texture_name_srgb)
704 surface_bind_and_dirtify(surface, context, TRUE);
705 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
706 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
709 context_release(context);
711 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
712 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
713 surface_force_reload(surface);
716 static HRESULT surface_private_setup(struct wined3d_surface *surface)
718 /* TODO: Check against the maximum texture sizes supported by the video card. */
719 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
720 unsigned int pow2Width, pow2Height;
722 TRACE("surface %p.\n", surface);
724 surface->texture_name = 0;
725 surface->texture_target = GL_TEXTURE_2D;
727 /* Non-power2 support */
728 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
730 pow2Width = surface->resource.width;
731 pow2Height = surface->resource.height;
733 else
735 /* Find the nearest pow2 match */
736 pow2Width = pow2Height = 1;
737 while (pow2Width < surface->resource.width)
738 pow2Width <<= 1;
739 while (pow2Height < surface->resource.height)
740 pow2Height <<= 1;
742 surface->pow2Width = pow2Width;
743 surface->pow2Height = pow2Height;
745 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
747 /* TODO: Add support for non power two compressed textures. */
748 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
750 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
751 surface, surface->resource.width, surface->resource.height);
752 return WINED3DERR_NOTAVAILABLE;
756 if (pow2Width != surface->resource.width
757 || pow2Height != surface->resource.height)
759 surface->flags |= SFLAG_NONPOW2;
762 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
763 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
765 /* One of three options:
766 * 1: Do the same as we do with NPOT and scale the texture, (any
767 * texture ops would require the texture to be scaled which is
768 * potentially slow)
769 * 2: Set the texture to the maximum size (bad idea).
770 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
771 * 4: Create the surface, but allow it to be used only for DirectDraw
772 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
773 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
774 * the render target. */
775 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
777 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
778 return WINED3DERR_NOTAVAILABLE;
781 /* We should never use this surface in combination with OpenGL! */
782 TRACE("Creating an oversized surface: %ux%u.\n",
783 surface->pow2Width, surface->pow2Height);
785 else
787 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
788 * and EXT_PALETTED_TEXTURE is used in combination with texture
789 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
790 * EXT_PALETTED_TEXTURE doesn't work in combination with
791 * ARB_TEXTURE_RECTANGLE. */
792 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
793 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
794 && gl_info->supported[EXT_PALETTED_TEXTURE]
795 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
797 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
798 surface->pow2Width = surface->resource.width;
799 surface->pow2Height = surface->resource.height;
800 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
804 switch (wined3d_settings.offscreen_rendering_mode)
806 case ORM_FBO:
807 surface->get_drawable_size = get_drawable_size_fbo;
808 break;
810 case ORM_BACKBUFFER:
811 surface->get_drawable_size = get_drawable_size_backbuffer;
812 break;
814 default:
815 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
816 return WINED3DERR_INVALIDCALL;
819 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
820 surface->flags |= SFLAG_DISCARDED;
822 return WINED3D_OK;
825 static void surface_realize_palette(struct wined3d_surface *surface)
827 struct wined3d_palette *palette = surface->palette;
829 TRACE("surface %p.\n", surface);
831 if (!palette) return;
833 if (surface->resource.format->id == WINED3DFMT_P8_UINT
834 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
836 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
838 /* Make sure the texture is up to date. This call doesn't do
839 * anything if the texture is already up to date. */
840 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
842 /* We want to force a palette refresh, so mark the drawable as not being up to date */
843 if (!surface_is_offscreen(surface))
844 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
846 else
848 if (!(surface->flags & SFLAG_INSYSMEM))
850 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
851 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
853 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
857 if (surface->flags & SFLAG_DIBSECTION)
859 RGBQUAD col[256];
860 unsigned int i;
862 TRACE("Updating the DC's palette.\n");
864 for (i = 0; i < 256; ++i)
866 col[i].rgbRed = palette->palents[i].peRed;
867 col[i].rgbGreen = palette->palents[i].peGreen;
868 col[i].rgbBlue = palette->palents[i].peBlue;
869 col[i].rgbReserved = 0;
871 SetDIBColorTable(surface->hDC, 0, 256, col);
874 /* Propagate the changes to the drawable when we have a palette. */
875 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
876 surface_load_location(surface, surface->draw_binding, NULL);
879 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
881 HRESULT hr;
883 /* If there's no destination surface there is nothing to do. */
884 if (!surface->overlay_dest)
885 return WINED3D_OK;
887 /* Blt calls ModifyLocation on the dest surface, which in turn calls
888 * DrawOverlay to update the overlay. Prevent an endless recursion. */
889 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
890 return WINED3D_OK;
892 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
893 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
894 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3D_TEXF_LINEAR);
895 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
897 return hr;
900 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
902 struct wined3d_device *device = surface->resource.device;
903 const RECT *pass_rect = rect;
905 TRACE("surface %p, rect %s, flags %#x.\n",
906 surface, wine_dbgstr_rect(rect), flags);
908 if (flags & WINED3D_MAP_DISCARD)
910 TRACE("WINED3D_MAP_DISCARD flag passed, marking SYSMEM as up to date.\n");
911 surface_prepare_system_memory(surface);
912 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
914 else
916 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
917 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
919 /* surface_load_location() does not check if the rectangle specifies
920 * the full surface. Most callers don't need that, so do it here. */
921 if (rect && !rect->top && !rect->left
922 && rect->right == surface->resource.width
923 && rect->bottom == surface->resource.height)
924 pass_rect = NULL;
925 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
928 if (surface->flags & SFLAG_PBO)
930 const struct wined3d_gl_info *gl_info;
931 struct wined3d_context *context;
933 context = context_acquire(device, NULL);
934 gl_info = context->gl_info;
936 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
937 checkGLcall("glBindBufferARB");
939 /* This shouldn't happen but could occur if some other function
940 * didn't handle the PBO properly. */
941 if (surface->resource.allocatedMemory)
942 ERR("The surface already has PBO memory allocated.\n");
944 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
945 checkGLcall("glMapBufferARB");
947 /* Make sure the PBO isn't set anymore in order not to break non-PBO
948 * calls. */
949 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
950 checkGLcall("glBindBufferARB");
952 context_release(context);
955 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
957 if (!rect)
958 surface_add_dirty_rect(surface, NULL);
959 else
961 struct wined3d_box b;
963 b.left = rect->left;
964 b.top = rect->top;
965 b.right = rect->right;
966 b.bottom = rect->bottom;
967 b.front = 0;
968 b.back = 1;
969 surface_add_dirty_rect(surface, &b);
974 static void surface_unmap(struct wined3d_surface *surface)
976 struct wined3d_device *device = surface->resource.device;
977 BOOL fullsurface;
979 TRACE("surface %p.\n", surface);
981 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
983 if (surface->flags & SFLAG_PBO)
985 const struct wined3d_gl_info *gl_info;
986 struct wined3d_context *context;
988 TRACE("Freeing PBO memory.\n");
990 context = context_acquire(device, NULL);
991 gl_info = context->gl_info;
993 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
994 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
995 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
996 checkGLcall("glUnmapBufferARB");
997 context_release(context);
999 surface->resource.allocatedMemory = NULL;
1002 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1004 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
1006 TRACE("Not dirtified, nothing to do.\n");
1007 goto done;
1010 if (surface->swapchain && surface->swapchain->front_buffer == surface)
1012 if (!surface->dirtyRect.left && !surface->dirtyRect.top
1013 && surface->dirtyRect.right == surface->resource.width
1014 && surface->dirtyRect.bottom == surface->resource.height)
1016 fullsurface = TRUE;
1018 else
1020 /* TODO: Proper partial rectangle tracking. */
1021 fullsurface = FALSE;
1022 surface->flags |= SFLAG_INSYSMEM;
1025 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
1027 /* Partial rectangle tracking is not commonly implemented, it is only
1028 * done for render targets. INSYSMEM was set before to tell
1029 * surface_load_location() where to read the rectangle from.
1030 * Indrawable is set because all modifications from the partial
1031 * sysmem copy are written back to the drawable, thus the surface is
1032 * merged again in the drawable. The sysmem copy is not fully up to
1033 * date because only a subrectangle was read in Map(). */
1034 if (!fullsurface)
1036 surface_modify_location(surface, surface->draw_binding, TRUE);
1037 surface_evict_sysmem(surface);
1040 surface->dirtyRect.left = surface->resource.width;
1041 surface->dirtyRect.top = surface->resource.height;
1042 surface->dirtyRect.right = 0;
1043 surface->dirtyRect.bottom = 0;
1045 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
1047 FIXME("Depth / stencil buffer locking is not implemented.\n");
1050 done:
1051 /* Overlays have to be redrawn manually after changes with the GL implementation */
1052 if (surface->overlay_dest)
1053 surface_draw_overlay(surface);
1056 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1058 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1059 return FALSE;
1060 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1061 return FALSE;
1062 return TRUE;
1065 static void surface_depth_blt_fbo(const struct wined3d_device *device,
1066 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1067 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1069 const struct wined3d_gl_info *gl_info;
1070 struct wined3d_context *context;
1071 DWORD src_mask, dst_mask;
1072 GLbitfield gl_mask;
1074 TRACE("device %p\n", device);
1075 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1076 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect));
1077 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1078 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect));
1080 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1081 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1083 if (src_mask != dst_mask)
1085 ERR("Incompatible formats %s and %s.\n",
1086 debug_d3dformat(src_surface->resource.format->id),
1087 debug_d3dformat(dst_surface->resource.format->id));
1088 return;
1091 if (!src_mask)
1093 ERR("Not a depth / stencil format: %s.\n",
1094 debug_d3dformat(src_surface->resource.format->id));
1095 return;
1098 gl_mask = 0;
1099 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1100 gl_mask |= GL_DEPTH_BUFFER_BIT;
1101 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1102 gl_mask |= GL_STENCIL_BUFFER_BIT;
1104 /* Make sure the locations are up-to-date. Loading the destination
1105 * surface isn't required if the entire surface is overwritten. */
1106 surface_load_location(src_surface, src_location, NULL);
1107 if (!surface_is_full_rect(dst_surface, dst_rect))
1108 surface_load_location(dst_surface, dst_location, NULL);
1110 context = context_acquire(device, NULL);
1111 if (!context->valid)
1113 context_release(context);
1114 WARN("Invalid context, skipping blit.\n");
1115 return;
1118 gl_info = context->gl_info;
1120 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
1121 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1123 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
1124 context_set_draw_buffer(context, GL_NONE);
1125 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1126 context_invalidate_state(context, STATE_FRAMEBUFFER);
1128 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1130 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
1131 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
1133 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1135 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1137 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1138 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
1140 gl_info->gl_ops.gl.p_glStencilMask(~0U);
1141 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
1144 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
1145 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1147 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1148 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1149 checkGLcall("glBlitFramebuffer()");
1151 if (wined3d_settings.strict_draw_ordering)
1152 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
1154 context_release(context);
1157 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1158 * Depth / stencil is not supported. */
1159 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
1160 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1161 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1163 const struct wined3d_gl_info *gl_info;
1164 struct wined3d_context *context;
1165 RECT src_rect, dst_rect;
1166 GLenum gl_filter;
1167 GLenum buffer;
1169 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1170 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1171 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1172 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1173 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1175 src_rect = *src_rect_in;
1176 dst_rect = *dst_rect_in;
1178 switch (filter)
1180 case WINED3D_TEXF_LINEAR:
1181 gl_filter = GL_LINEAR;
1182 break;
1184 default:
1185 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1186 case WINED3D_TEXF_NONE:
1187 case WINED3D_TEXF_POINT:
1188 gl_filter = GL_NEAREST;
1189 break;
1192 /* Resolve the source surface first if needed. */
1193 if (src_location == SFLAG_INRB_MULTISAMPLE
1194 && (src_surface->resource.format->id != dst_surface->resource.format->id
1195 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1196 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1197 src_location = SFLAG_INRB_RESOLVED;
1199 /* Make sure the locations are up-to-date. Loading the destination
1200 * surface isn't required if the entire surface is overwritten. (And is
1201 * in fact harmful if we're being called by surface_load_location() with
1202 * the purpose of loading the destination surface.) */
1203 surface_load_location(src_surface, src_location, NULL);
1204 if (!surface_is_full_rect(dst_surface, &dst_rect))
1205 surface_load_location(dst_surface, dst_location, NULL);
1207 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1208 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1209 else context = context_acquire(device, NULL);
1211 if (!context->valid)
1213 context_release(context);
1214 WARN("Invalid context, skipping blit.\n");
1215 return;
1218 gl_info = context->gl_info;
1220 if (src_location == SFLAG_INDRAWABLE)
1222 TRACE("Source surface %p is onscreen.\n", src_surface);
1223 buffer = surface_get_gl_buffer(src_surface);
1224 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1226 else
1228 TRACE("Source surface %p is offscreen.\n", src_surface);
1229 buffer = GL_COLOR_ATTACHMENT0;
1232 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1233 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1234 checkGLcall("glReadBuffer()");
1235 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1237 if (dst_location == SFLAG_INDRAWABLE)
1239 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1240 buffer = surface_get_gl_buffer(dst_surface);
1241 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1243 else
1245 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1246 buffer = GL_COLOR_ATTACHMENT0;
1249 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1250 context_set_draw_buffer(context, buffer);
1251 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1252 context_invalidate_state(context, STATE_FRAMEBUFFER);
1254 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1255 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
1256 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
1257 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
1258 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
1260 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
1261 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1263 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1264 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1265 checkGLcall("glBlitFramebuffer()");
1267 if (wined3d_settings.strict_draw_ordering
1268 || (dst_location == SFLAG_INDRAWABLE
1269 && dst_surface->swapchain->front_buffer == dst_surface))
1270 gl_info->gl_ops.gl.p_glFlush();
1272 context_release(context);
1275 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1276 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
1277 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
1279 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1280 return FALSE;
1282 /* Source and/or destination need to be on the GL side */
1283 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
1284 return FALSE;
1286 switch (blit_op)
1288 case WINED3D_BLIT_OP_COLOR_BLIT:
1289 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1290 return FALSE;
1291 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1292 return FALSE;
1293 break;
1295 case WINED3D_BLIT_OP_DEPTH_BLIT:
1296 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1297 return FALSE;
1298 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1299 return FALSE;
1300 break;
1302 default:
1303 return FALSE;
1306 if (!(src_format->id == dst_format->id
1307 || (is_identity_fixup(src_format->color_fixup)
1308 && is_identity_fixup(dst_format->color_fixup))))
1309 return FALSE;
1311 return TRUE;
1314 /* This function checks if the primary render target uses the 8bit paletted format. */
1315 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1317 if (device->fb.render_targets && device->fb.render_targets[0])
1319 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1320 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1321 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1322 return TRUE;
1324 return FALSE;
1327 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1328 DWORD color, struct wined3d_color *float_color)
1330 const struct wined3d_format *format = surface->resource.format;
1331 const struct wined3d_device *device = surface->resource.device;
1333 switch (format->id)
1335 case WINED3DFMT_P8_UINT:
1336 if (surface->palette)
1338 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1339 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1340 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1342 else
1344 float_color->r = 0.0f;
1345 float_color->g = 0.0f;
1346 float_color->b = 0.0f;
1348 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1349 break;
1351 case WINED3DFMT_B5G6R5_UNORM:
1352 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1353 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1354 float_color->b = (color & 0x1f) / 31.0f;
1355 float_color->a = 1.0f;
1356 break;
1358 case WINED3DFMT_B8G8R8_UNORM:
1359 case WINED3DFMT_B8G8R8X8_UNORM:
1360 float_color->r = D3DCOLOR_R(color);
1361 float_color->g = D3DCOLOR_G(color);
1362 float_color->b = D3DCOLOR_B(color);
1363 float_color->a = 1.0f;
1364 break;
1366 case WINED3DFMT_B8G8R8A8_UNORM:
1367 float_color->r = D3DCOLOR_R(color);
1368 float_color->g = D3DCOLOR_G(color);
1369 float_color->b = D3DCOLOR_B(color);
1370 float_color->a = D3DCOLOR_A(color);
1371 break;
1373 default:
1374 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1375 return FALSE;
1378 return TRUE;
1381 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1383 const struct wined3d_format *format = surface->resource.format;
1385 switch (format->id)
1387 case WINED3DFMT_S1_UINT_D15_UNORM:
1388 *float_depth = depth / (float)0x00007fff;
1389 break;
1391 case WINED3DFMT_D16_UNORM:
1392 *float_depth = depth / (float)0x0000ffff;
1393 break;
1395 case WINED3DFMT_D24_UNORM_S8_UINT:
1396 case WINED3DFMT_X8D24_UNORM:
1397 *float_depth = depth / (float)0x00ffffff;
1398 break;
1400 case WINED3DFMT_D32_UNORM:
1401 *float_depth = depth / (float)0xffffffff;
1402 break;
1404 default:
1405 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1406 return FALSE;
1409 return TRUE;
1412 /* Do not call while under the GL lock. */
1413 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1415 const struct wined3d_resource *resource = &surface->resource;
1416 struct wined3d_device *device = resource->device;
1417 const struct blit_shader *blitter;
1419 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1420 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1421 if (!blitter)
1423 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1424 return WINED3DERR_INVALIDCALL;
1427 return blitter->depth_fill(device, surface, rect, depth);
1430 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1431 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1433 struct wined3d_device *device = src_surface->resource.device;
1435 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1436 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1437 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1438 return WINED3DERR_INVALIDCALL;
1440 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1442 surface_modify_ds_location(dst_surface, dst_location,
1443 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1445 return WINED3D_OK;
1448 /* Do not call while under the GL lock. */
1449 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1450 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1451 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
1453 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1454 struct wined3d_device *device = dst_surface->resource.device;
1455 DWORD src_ds_flags, dst_ds_flags;
1456 RECT src_rect, dst_rect;
1457 BOOL scale, convert;
1459 static const DWORD simple_blit = WINEDDBLT_ASYNC
1460 | WINEDDBLT_COLORFILL
1461 | WINEDDBLT_WAIT
1462 | WINEDDBLT_DEPTHFILL
1463 | WINEDDBLT_DONOTWAIT;
1465 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1466 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1467 flags, fx, debug_d3dtexturefiltertype(filter));
1468 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1470 if (fx)
1472 TRACE("dwSize %#x.\n", fx->dwSize);
1473 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
1474 TRACE("dwROP %#x.\n", fx->dwROP);
1475 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
1476 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
1477 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
1478 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
1479 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
1480 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
1481 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
1482 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
1483 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
1484 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
1485 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
1486 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
1487 TRACE("dwReserved %#x.\n", fx->dwReserved);
1488 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
1489 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
1490 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
1491 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
1492 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
1493 TRACE("ddckDestColorkey {%#x, %#x}.\n",
1494 fx->ddckDestColorkey.color_space_low_value,
1495 fx->ddckDestColorkey.color_space_high_value);
1496 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
1497 fx->ddckSrcColorkey.color_space_low_value,
1498 fx->ddckSrcColorkey.color_space_high_value);
1501 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
1503 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1504 return WINEDDERR_SURFACEBUSY;
1507 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1509 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1510 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1511 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1512 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1513 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1515 WARN("The application gave us a bad destination rectangle.\n");
1516 return WINEDDERR_INVALIDRECT;
1519 if (src_surface)
1521 surface_get_rect(src_surface, src_rect_in, &src_rect);
1523 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1524 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1525 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1526 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1527 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1529 WARN("Application gave us bad source rectangle for Blt.\n");
1530 return WINEDDERR_INVALIDRECT;
1533 else
1535 memset(&src_rect, 0, sizeof(src_rect));
1538 if (!fx || !(fx->dwDDFX))
1539 flags &= ~WINEDDBLT_DDFX;
1541 if (flags & WINEDDBLT_WAIT)
1542 flags &= ~WINEDDBLT_WAIT;
1544 if (flags & WINEDDBLT_ASYNC)
1546 static unsigned int once;
1548 if (!once++)
1549 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1550 flags &= ~WINEDDBLT_ASYNC;
1553 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1554 if (flags & WINEDDBLT_DONOTWAIT)
1556 static unsigned int once;
1558 if (!once++)
1559 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1560 flags &= ~WINEDDBLT_DONOTWAIT;
1563 if (!device->d3d_initialized)
1565 WARN("D3D not initialized, using fallback.\n");
1566 goto cpu;
1569 /* We want to avoid invalidating the sysmem location for converted
1570 * surfaces, since otherwise we'd have to convert the data back when
1571 * locking them. */
1572 if (dst_surface->flags & SFLAG_CONVERTED)
1574 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
1575 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1578 if (flags & ~simple_blit)
1580 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
1581 goto fallback;
1584 if (src_surface)
1585 src_swapchain = src_surface->swapchain;
1586 else
1587 src_swapchain = NULL;
1589 dst_swapchain = dst_surface->swapchain;
1591 /* This isn't strictly needed. FBO blits for example could deal with
1592 * cross-swapchain blits by first downloading the source to a texture
1593 * before switching to the destination context. We just have this here to
1594 * not have to deal with the issue, since cross-swapchain blits should be
1595 * rare. */
1596 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1598 FIXME("Using fallback for cross-swapchain blit.\n");
1599 goto fallback;
1602 scale = src_surface
1603 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
1604 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
1605 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
1607 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1608 if (src_surface)
1609 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1610 else
1611 src_ds_flags = 0;
1613 if (src_ds_flags || dst_ds_flags)
1615 if (flags & WINEDDBLT_DEPTHFILL)
1617 float depth;
1619 TRACE("Depth fill.\n");
1621 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1622 return WINED3DERR_INVALIDCALL;
1624 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1625 return WINED3D_OK;
1627 else
1629 if (src_ds_flags != dst_ds_flags)
1631 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1632 return WINED3DERR_INVALIDCALL;
1635 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->draw_binding, &src_rect,
1636 dst_surface, dst_surface->draw_binding, &dst_rect)))
1637 return WINED3D_OK;
1640 else
1642 /* In principle this would apply to depth blits as well, but we don't
1643 * implement those in the CPU blitter at the moment. */
1644 if ((dst_surface->flags & SFLAG_INSYSMEM)
1645 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
1647 if (scale)
1648 TRACE("Not doing sysmem blit because of scaling.\n");
1649 else if (convert)
1650 TRACE("Not doing sysmem blit because of format conversion.\n");
1651 else
1652 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1655 if (flags & WINEDDBLT_COLORFILL)
1657 struct wined3d_color color;
1659 TRACE("Color fill.\n");
1661 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1662 goto fallback;
1664 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1665 return WINED3D_OK;
1667 else
1669 TRACE("Color blit.\n");
1671 /* Upload */
1672 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
1674 if (scale)
1675 TRACE("Not doing upload because of scaling.\n");
1676 else if (convert)
1677 TRACE("Not doing upload because of format conversion.\n");
1678 else
1680 POINT dst_point = {dst_rect.left, dst_rect.top};
1682 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
1684 if (!surface_is_offscreen(dst_surface))
1685 surface_load_location(dst_surface, dst_surface->draw_binding, NULL);
1686 return WINED3D_OK;
1691 /* Use present for back -> front blits. The idea behind this is
1692 * that present is potentially faster than a blit, in particular
1693 * when FBO blits aren't available. Some ddraw applications like
1694 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
1695 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
1696 * applications can't blit directly to the frontbuffer. */
1697 if (dst_swapchain && dst_swapchain->back_buffers
1698 && dst_surface == dst_swapchain->front_buffer
1699 && src_surface == dst_swapchain->back_buffers[0])
1701 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
1703 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1705 /* Set the swap effect to COPY, we don't want the backbuffer
1706 * to become undefined. */
1707 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
1708 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
1709 dst_swapchain->desc.swap_effect = swap_effect;
1711 return WINED3D_OK;
1714 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1715 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1716 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1718 TRACE("Using FBO blit.\n");
1720 surface_blt_fbo(device, filter,
1721 src_surface, src_surface->draw_binding, &src_rect,
1722 dst_surface, dst_surface->draw_binding, &dst_rect);
1723 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1724 return WINED3D_OK;
1727 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1728 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1729 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1731 TRACE("Using arbfp blit.\n");
1733 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1734 return WINED3D_OK;
1739 fallback:
1741 /* Special cases for render targets. */
1742 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1743 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1745 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1746 src_surface, &src_rect, flags, fx, filter)))
1747 return WINED3D_OK;
1750 cpu:
1752 /* For the rest call the X11 surface implementation. For render targets
1753 * this should be implemented OpenGL accelerated in BltOverride, other
1754 * blits are rather rare. */
1755 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1758 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1759 struct wined3d_surface *render_target)
1761 TRACE("surface %p, render_target %p.\n", surface, render_target);
1763 /* TODO: Check surface sizes, pools, etc. */
1765 if (render_target->resource.multisample_type)
1766 return WINED3DERR_INVALIDCALL;
1768 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1771 /* Context activation is done by the caller. */
1772 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1774 if (surface->flags & SFLAG_DIBSECTION)
1776 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1778 else
1780 if (!surface->resource.heap_memory)
1781 surface->resource.heap_memory = wined3d_resource_allocate_sysmem(surface->resource.size);
1782 else if (!(surface->flags & SFLAG_CLIENT))
1783 ERR("Surface %p has heap_memory %p and flags %#x.\n",
1784 surface, surface->resource.heap_memory, surface->flags);
1786 surface->resource.allocatedMemory = surface->resource.heap_memory;
1789 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1790 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1791 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1792 surface->resource.size, surface->resource.allocatedMemory));
1793 checkGLcall("glGetBufferSubDataARB");
1794 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1795 checkGLcall("glDeleteBuffersARB");
1797 surface->pbo = 0;
1798 surface->flags &= ~SFLAG_PBO;
1801 static BOOL surface_init_sysmem(struct wined3d_surface *surface)
1803 if (!surface->resource.allocatedMemory)
1805 if (!surface->resource.heap_memory)
1807 surface->resource.heap_memory = wined3d_resource_allocate_sysmem(surface->resource.size);
1808 if (!surface->resource.heap_memory)
1810 ERR("Failed to allocate memory.\n");
1811 return FALSE;
1814 else if (!(surface->flags & SFLAG_CLIENT))
1816 ERR("Surface %p has heap_memory %p and flags %#x.\n",
1817 surface, surface->resource.heap_memory, surface->flags);
1820 surface->resource.allocatedMemory = surface->resource.heap_memory;
1822 else
1824 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
1827 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1829 return TRUE;
1832 /* Do not call while under the GL lock. */
1833 static void surface_unload(struct wined3d_resource *resource)
1835 struct wined3d_surface *surface = surface_from_resource(resource);
1836 struct wined3d_renderbuffer_entry *entry, *entry2;
1837 struct wined3d_device *device = resource->device;
1838 const struct wined3d_gl_info *gl_info;
1839 struct wined3d_context *context;
1841 TRACE("surface %p.\n", surface);
1843 if (resource->pool == WINED3D_POOL_DEFAULT)
1845 /* Default pool resources are supposed to be destroyed before Reset is called.
1846 * Implicit resources stay however. So this means we have an implicit render target
1847 * or depth stencil. The content may be destroyed, but we still have to tear down
1848 * opengl resources, so we cannot leave early.
1850 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1851 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1852 * or the depth stencil into an FBO the texture or render buffer will be removed
1853 * and all flags get lost
1855 if (!(surface->flags & SFLAG_PBO))
1856 surface_init_sysmem(surface);
1857 /* We also get here when the ddraw swapchain is destroyed, for example
1858 * for a mode switch. In this case this surface won't necessarily be
1859 * an implicit surface. We have to mark it lost so that the
1860 * application can restore it after the mode switch. */
1861 surface->flags |= SFLAG_LOST;
1863 else
1865 /* Load the surface into system memory */
1866 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1867 surface_modify_location(surface, surface->draw_binding, FALSE);
1869 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1870 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1871 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1873 context = context_acquire(device, NULL);
1874 gl_info = context->gl_info;
1876 /* Destroy PBOs, but load them into real sysmem before */
1877 if (surface->flags & SFLAG_PBO)
1878 surface_remove_pbo(surface, gl_info);
1880 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1881 * all application-created targets the application has to release the surface
1882 * before calling _Reset
1884 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1886 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1887 list_remove(&entry->entry);
1888 HeapFree(GetProcessHeap(), 0, entry);
1890 list_init(&surface->renderbuffers);
1891 surface->current_renderbuffer = NULL;
1893 /* If we're in a texture, the texture name belongs to the texture.
1894 * Otherwise, destroy it. */
1895 if (!surface->container)
1897 gl_info->gl_ops.gl.p_glDeleteTextures(1, &surface->texture_name);
1898 surface->texture_name = 0;
1899 gl_info->gl_ops.gl.p_glDeleteTextures(1, &surface->texture_name_srgb);
1900 surface->texture_name_srgb = 0;
1902 if (surface->rb_multisample)
1904 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1905 surface->rb_multisample = 0;
1907 if (surface->rb_resolved)
1909 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1910 surface->rb_resolved = 0;
1913 context_release(context);
1915 resource_unload(resource);
1918 static const struct wined3d_resource_ops surface_resource_ops =
1920 surface_unload,
1923 static const struct wined3d_surface_ops surface_ops =
1925 surface_private_setup,
1926 surface_realize_palette,
1927 surface_map,
1928 surface_unmap,
1931 /*****************************************************************************
1932 * Initializes the GDI surface, aka creates the DIB section we render to
1933 * The DIB section creation is done by calling GetDC, which will create the
1934 * section and releasing the dc to allow the app to use it. The dib section
1935 * will stay until the surface is released
1937 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1938 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1939 * avoid confusion in the shared surface code.
1941 * Returns:
1942 * WINED3D_OK on success
1943 * The return values of called methods on failure
1945 *****************************************************************************/
1946 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1948 HRESULT hr;
1950 TRACE("surface %p.\n", surface);
1952 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1954 ERR("Overlays not yet supported by GDI surfaces.\n");
1955 return WINED3DERR_INVALIDCALL;
1958 /* Sysmem textures have memory already allocated - release it,
1959 * this avoids an unnecessary memcpy. */
1960 hr = surface_create_dib_section(surface);
1961 if (SUCCEEDED(hr))
1963 wined3d_resource_free_sysmem(surface->resource.heap_memory);
1964 surface->resource.heap_memory = NULL;
1965 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1968 /* We don't mind the nonpow2 stuff in GDI. */
1969 surface->pow2Width = surface->resource.width;
1970 surface->pow2Height = surface->resource.height;
1972 return WINED3D_OK;
1975 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1977 struct wined3d_palette *palette = surface->palette;
1979 TRACE("surface %p.\n", surface);
1981 if (!palette) return;
1983 if (surface->flags & SFLAG_DIBSECTION)
1985 RGBQUAD col[256];
1986 unsigned int i;
1988 TRACE("Updating the DC's palette.\n");
1990 for (i = 0; i < 256; ++i)
1992 col[i].rgbRed = palette->palents[i].peRed;
1993 col[i].rgbGreen = palette->palents[i].peGreen;
1994 col[i].rgbBlue = palette->palents[i].peBlue;
1995 col[i].rgbReserved = 0;
1997 SetDIBColorTable(surface->hDC, 0, 256, col);
2000 /* Update the image because of the palette change. Some games like e.g.
2001 * Red Alert call SetEntries a lot to implement fading. */
2002 /* Tell the swapchain to update the screen. */
2003 if (surface->swapchain && surface == surface->swapchain->front_buffer)
2004 x11_copy_to_screen(surface->swapchain, NULL);
2007 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
2009 TRACE("surface %p, rect %s, flags %#x.\n",
2010 surface, wine_dbgstr_rect(rect), flags);
2012 if (!(surface->flags & SFLAG_DIBSECTION))
2014 HRESULT hr;
2016 /* This happens on gdi surfaces if the application set a user pointer
2017 * and resets it. Recreate the DIB section. */
2018 if (FAILED(hr = surface_create_dib_section(surface)))
2020 ERR("Failed to create dib section, hr %#x.\n", hr);
2021 return;
2023 wined3d_resource_free_sysmem(surface->resource.heap_memory);
2024 surface->resource.heap_memory = NULL;
2025 surface->resource.allocatedMemory = surface->dib.bitmap_data;
2029 static void gdi_surface_unmap(struct wined3d_surface *surface)
2031 TRACE("surface %p.\n", surface);
2033 /* Tell the swapchain to update the screen. */
2034 if (surface->swapchain && surface == surface->swapchain->front_buffer)
2035 x11_copy_to_screen(surface->swapchain, &surface->lockedRect);
2037 memset(&surface->lockedRect, 0, sizeof(RECT));
2040 static const struct wined3d_surface_ops gdi_surface_ops =
2042 gdi_surface_private_setup,
2043 gdi_surface_realize_palette,
2044 gdi_surface_map,
2045 gdi_surface_unmap,
2048 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2050 GLuint *name;
2051 DWORD flag;
2053 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2055 if(srgb)
2057 name = &surface->texture_name_srgb;
2058 flag = SFLAG_INSRGBTEX;
2060 else
2062 name = &surface->texture_name;
2063 flag = SFLAG_INTEXTURE;
2066 if (!*name && new_name)
2068 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2069 * surface has no texture name yet. See if we can get rid of this. */
2070 if (surface->flags & flag)
2072 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2073 surface_modify_location(surface, flag, FALSE);
2077 *name = new_name;
2078 surface_force_reload(surface);
2081 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target, GLint level)
2083 TRACE("surface %p, target %#x.\n", surface, target);
2085 if (surface->texture_target != target)
2087 if (target == GL_TEXTURE_RECTANGLE_ARB)
2089 surface->flags &= ~SFLAG_NORMCOORD;
2091 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2093 surface->flags |= SFLAG_NORMCOORD;
2096 surface->texture_target = target;
2097 surface->texture_level = level;
2098 surface_force_reload(surface);
2101 /* This call just downloads data, the caller is responsible for binding the
2102 * correct texture. */
2103 /* Context activation is done by the caller. */
2104 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2106 const struct wined3d_format *format = surface->resource.format;
2108 /* Only support read back of converted P8 surfaces. */
2109 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2111 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
2112 return;
2115 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2117 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2118 surface, surface->texture_level, format->glFormat, format->glType,
2119 surface->resource.allocatedMemory);
2121 if (surface->flags & SFLAG_PBO)
2123 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2124 checkGLcall("glBindBufferARB");
2125 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2126 checkGLcall("glGetCompressedTexImageARB");
2127 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2128 checkGLcall("glBindBufferARB");
2130 else
2132 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2133 surface->texture_level, surface->resource.allocatedMemory));
2134 checkGLcall("glGetCompressedTexImageARB");
2137 else
2139 void *mem;
2140 GLenum gl_format = format->glFormat;
2141 GLenum gl_type = format->glType;
2142 int src_pitch = 0;
2143 int dst_pitch = 0;
2145 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2146 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2148 gl_format = GL_ALPHA;
2149 gl_type = GL_UNSIGNED_BYTE;
2152 if (surface->flags & SFLAG_NONPOW2)
2154 unsigned char alignment = surface->resource.device->surface_alignment;
2155 src_pitch = format->byte_count * surface->pow2Width;
2156 dst_pitch = wined3d_surface_get_pitch(surface);
2157 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2158 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2160 else
2162 mem = surface->resource.allocatedMemory;
2165 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2166 surface, surface->texture_level, gl_format, gl_type, mem);
2168 if (surface->flags & SFLAG_PBO)
2170 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2171 checkGLcall("glBindBufferARB");
2173 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
2174 gl_format, gl_type, NULL);
2175 checkGLcall("glGetTexImage");
2177 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2178 checkGLcall("glBindBufferARB");
2180 else
2182 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
2183 gl_format, gl_type, mem);
2184 checkGLcall("glGetTexImage");
2187 if (surface->flags & SFLAG_NONPOW2)
2189 const BYTE *src_data;
2190 BYTE *dst_data;
2191 UINT y;
2193 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2194 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2195 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2197 * We're doing this...
2199 * instead of boxing the texture :
2200 * |<-texture width ->| -->pow2width| /\
2201 * |111111111111111111| | |
2202 * |222 Texture 222222| boxed empty | texture height
2203 * |3333 Data 33333333| | |
2204 * |444444444444444444| | \/
2205 * ----------------------------------- |
2206 * | boxed empty | boxed empty | pow2height
2207 * | | | \/
2208 * -----------------------------------
2211 * we're repacking the data to the expected texture width
2213 * |<-texture width ->| -->pow2width| /\
2214 * |111111111111111111222222222222222| |
2215 * |222333333333333333333444444444444| texture height
2216 * |444444 | |
2217 * | | \/
2218 * | | |
2219 * | empty | pow2height
2220 * | | \/
2221 * -----------------------------------
2223 * == is the same as
2225 * |<-texture width ->| /\
2226 * |111111111111111111|
2227 * |222222222222222222|texture height
2228 * |333333333333333333|
2229 * |444444444444444444| \/
2230 * --------------------
2232 * this also means that any references to allocatedMemory should work with the data as if were a
2233 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2235 * internally the texture is still stored in a boxed format so any references to textureName will
2236 * get a boxed texture with width pow2width and not a texture of width resource.width.
2238 * Performance should not be an issue, because applications normally do not lock the surfaces when
2239 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2240 * and doesn't have to be re-read. */
2241 src_data = mem;
2242 dst_data = surface->resource.allocatedMemory;
2243 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2244 for (y = 0; y < surface->resource.height; ++y)
2246 memcpy(dst_data, src_data, dst_pitch);
2247 src_data += src_pitch;
2248 dst_data += dst_pitch;
2251 HeapFree(GetProcessHeap(), 0, mem);
2255 /* Surface has now been downloaded */
2256 surface->flags |= SFLAG_INSYSMEM;
2259 /* This call just uploads data, the caller is responsible for binding the
2260 * correct texture. */
2261 /* Context activation is done by the caller. */
2262 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2263 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
2264 BOOL srgb, const struct wined3d_bo_address *data)
2266 UINT update_w = src_rect->right - src_rect->left;
2267 UINT update_h = src_rect->bottom - src_rect->top;
2269 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
2270 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
2271 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2273 if (surface->resource.map_count)
2275 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
2276 surface->flags |= SFLAG_PIN_SYSMEM;
2279 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
2281 update_h *= format->height_scale.numerator;
2282 update_h /= format->height_scale.denominator;
2285 if (data->buffer_object)
2287 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2288 checkGLcall("glBindBufferARB");
2291 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2293 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
2294 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2295 const BYTE *addr = data->addr;
2296 GLenum internal;
2298 addr += (src_rect->top / format->block_height) * src_pitch;
2299 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2301 if (srgb)
2302 internal = format->glGammaInternal;
2303 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2304 internal = format->rtInternal;
2305 else
2306 internal = format->glInternal;
2308 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2309 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2310 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2312 if (row_length == src_pitch)
2314 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2315 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2317 else
2319 UINT row, y;
2321 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2322 * can't use the unpack row length like below. */
2323 for (row = 0, y = dst_point->y; row < row_count; ++row)
2325 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2326 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2327 y += format->block_height;
2328 addr += src_pitch;
2331 checkGLcall("glCompressedTexSubImage2DARB");
2333 else
2335 const BYTE *addr = data->addr;
2337 addr += src_rect->top * src_pitch;
2338 addr += src_rect->left * format->byte_count;
2340 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2341 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2342 update_w, update_h, format->glFormat, format->glType, addr);
2344 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
2345 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
2346 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
2347 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2348 checkGLcall("glTexSubImage2D");
2351 if (data->buffer_object)
2353 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2354 checkGLcall("glBindBufferARB");
2357 if (wined3d_settings.strict_draw_ordering)
2358 gl_info->gl_ops.gl.p_glFlush();
2360 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2362 struct wined3d_device *device = surface->resource.device;
2363 unsigned int i;
2365 for (i = 0; i < device->context_count; ++i)
2367 context_surface_update(device->contexts[i], surface);
2372 static HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck, BOOL use_texturing,
2373 struct wined3d_format *format, enum wined3d_conversion_type *conversion_type)
2375 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2376 const struct wined3d_device *device = surface->resource.device;
2377 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2378 BOOL blit_supported = FALSE;
2380 /* Copy the default values from the surface. Below we might perform fixups */
2381 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
2382 *format = *surface->resource.format;
2383 *conversion_type = WINED3D_CT_NONE;
2385 /* Ok, now look if we have to do any conversion */
2386 switch (surface->resource.format->id)
2388 case WINED3DFMT_P8_UINT:
2389 /* Below the call to blit_supported is disabled for Wine 1.2
2390 * because the function isn't operating correctly yet. At the
2391 * moment 8-bit blits are handled in software and if certain GL
2392 * extensions are around, surface conversion is performed at
2393 * upload time. The blit_supported call recognizes it as a
2394 * destination fixup. This type of upload 'fixup' and 8-bit to
2395 * 8-bit blits need to be handled by the blit_shader.
2396 * TODO: get rid of this #if 0. */
2397 #if 0
2398 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
2399 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
2400 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
2401 #endif
2402 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
2404 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
2405 * texturing. Further also use conversion in case of color keying.
2406 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
2407 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
2408 * conflicts with this.
2410 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
2411 || colorkey_active || !use_texturing)
2413 format->glFormat = GL_RGBA;
2414 format->glInternal = GL_RGBA;
2415 format->glType = GL_UNSIGNED_BYTE;
2416 format->conv_byte_count = 4;
2417 if (colorkey_active)
2418 *conversion_type = WINED3D_CT_PALETTED_CK;
2419 else
2420 *conversion_type = WINED3D_CT_PALETTED;
2422 break;
2424 case WINED3DFMT_B2G3R3_UNORM:
2425 /* **********************
2426 GL_UNSIGNED_BYTE_3_3_2
2427 ********************** */
2428 if (colorkey_active) {
2429 /* This texture format will never be used.. So do not care about color keying
2430 up until the point in time it will be needed :-) */
2431 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
2433 break;
2435 case WINED3DFMT_B5G6R5_UNORM:
2436 if (colorkey_active)
2438 *conversion_type = WINED3D_CT_CK_565;
2439 format->glFormat = GL_RGBA;
2440 format->glInternal = GL_RGB5_A1;
2441 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
2442 format->conv_byte_count = 2;
2444 break;
2446 case WINED3DFMT_B5G5R5X1_UNORM:
2447 if (colorkey_active)
2449 *conversion_type = WINED3D_CT_CK_5551;
2450 format->glFormat = GL_BGRA;
2451 format->glInternal = GL_RGB5_A1;
2452 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
2453 format->conv_byte_count = 2;
2455 break;
2457 case WINED3DFMT_B8G8R8_UNORM:
2458 if (colorkey_active)
2460 *conversion_type = WINED3D_CT_CK_RGB24;
2461 format->glFormat = GL_RGBA;
2462 format->glInternal = GL_RGBA8;
2463 format->glType = GL_UNSIGNED_INT_8_8_8_8;
2464 format->conv_byte_count = 4;
2466 break;
2468 case WINED3DFMT_B8G8R8X8_UNORM:
2469 if (colorkey_active)
2471 *conversion_type = WINED3D_CT_RGB32_888;
2472 format->glFormat = GL_RGBA;
2473 format->glInternal = GL_RGBA8;
2474 format->glType = GL_UNSIGNED_INT_8_8_8_8;
2475 format->conv_byte_count = 4;
2477 break;
2479 case WINED3DFMT_B8G8R8A8_UNORM:
2480 if (colorkey_active)
2482 *conversion_type = WINED3D_CT_CK_ARGB32;
2483 format->conv_byte_count = 4;
2485 break;
2487 default:
2488 break;
2491 if (*conversion_type != WINED3D_CT_NONE)
2493 format->rtInternal = format->glInternal;
2494 format->glGammaInternal = format->glInternal;
2497 return WINED3D_OK;
2500 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
2502 UINT width_mask, height_mask;
2504 if (!rect->left && !rect->top
2505 && rect->right == surface->resource.width
2506 && rect->bottom == surface->resource.height)
2507 return TRUE;
2509 /* This assumes power of two block sizes, but NPOT block sizes would be
2510 * silly anyway. */
2511 width_mask = surface->resource.format->block_width - 1;
2512 height_mask = surface->resource.format->block_height - 1;
2514 if (!(rect->left & width_mask) && !(rect->top & height_mask)
2515 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
2516 return TRUE;
2518 return FALSE;
2521 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
2522 struct wined3d_surface *src_surface, const RECT *src_rect)
2524 const struct wined3d_format *src_format;
2525 const struct wined3d_format *dst_format;
2526 const struct wined3d_gl_info *gl_info;
2527 enum wined3d_conversion_type convert;
2528 struct wined3d_context *context;
2529 struct wined3d_bo_address data;
2530 struct wined3d_format format;
2531 UINT update_w, update_h;
2532 UINT dst_w, dst_h;
2533 RECT r, dst_rect;
2534 UINT src_pitch;
2535 POINT p;
2537 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
2538 dst_surface, wine_dbgstr_point(dst_point),
2539 src_surface, wine_dbgstr_rect(src_rect));
2541 src_format = src_surface->resource.format;
2542 dst_format = dst_surface->resource.format;
2544 if (src_format->id != dst_format->id)
2546 WARN("Source and destination surfaces should have the same format.\n");
2547 return WINED3DERR_INVALIDCALL;
2550 if (!dst_point)
2552 p.x = 0;
2553 p.y = 0;
2554 dst_point = &p;
2556 else if (dst_point->x < 0 || dst_point->y < 0)
2558 WARN("Invalid destination point.\n");
2559 return WINED3DERR_INVALIDCALL;
2562 if (!src_rect)
2564 r.left = 0;
2565 r.top = 0;
2566 r.right = src_surface->resource.width;
2567 r.bottom = src_surface->resource.height;
2568 src_rect = &r;
2570 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
2571 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
2573 WARN("Invalid source rectangle.\n");
2574 return WINED3DERR_INVALIDCALL;
2577 dst_w = dst_surface->resource.width;
2578 dst_h = dst_surface->resource.height;
2580 update_w = src_rect->right - src_rect->left;
2581 update_h = src_rect->bottom - src_rect->top;
2583 if (update_w > dst_w || dst_point->x > dst_w - update_w
2584 || update_h > dst_h || dst_point->y > dst_h - update_h)
2586 WARN("Destination out of bounds.\n");
2587 return WINED3DERR_INVALIDCALL;
2590 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
2592 WARN("Source rectangle not block-aligned.\n");
2593 return WINED3DERR_INVALIDCALL;
2596 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
2597 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
2599 WARN("Destination rectangle not block-aligned.\n");
2600 return WINED3DERR_INVALIDCALL;
2603 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
2604 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
2605 if (convert != WINED3D_CT_NONE || format.convert)
2606 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
2608 context = context_acquire(dst_surface->resource.device, NULL);
2609 gl_info = context->gl_info;
2611 /* Only load the surface for partial updates. For newly allocated texture
2612 * the texture wouldn't be the current location, and we'd upload zeroes
2613 * just to overwrite them again. */
2614 if (update_w == dst_w && update_h == dst_h)
2615 surface_prepare_texture(dst_surface, context, FALSE);
2616 else
2617 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
2618 surface_bind(dst_surface, context, FALSE);
2620 data.buffer_object = src_surface->pbo;
2621 data.addr = src_surface->resource.allocatedMemory;
2622 src_pitch = wined3d_surface_get_pitch(src_surface);
2624 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
2626 invalidate_active_texture(dst_surface->resource.device, context);
2628 context_release(context);
2630 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
2631 return WINED3D_OK;
2634 /* This call just allocates the texture, the caller is responsible for binding
2635 * the correct texture. */
2636 /* Context activation is done by the caller. */
2637 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2638 const struct wined3d_format *format, BOOL srgb)
2640 BOOL disable_client_storage = FALSE;
2641 GLsizei width = surface->pow2Width;
2642 GLsizei height = surface->pow2Height;
2643 const BYTE *mem = NULL;
2644 GLenum internal;
2646 if (srgb)
2648 internal = format->glGammaInternal;
2650 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2652 internal = format->rtInternal;
2654 else
2656 internal = format->glInternal;
2659 if (!internal)
2660 FIXME("No GL internal format for format %s.\n", debug_d3dformat(format->id));
2662 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
2664 height *= format->height_scale.numerator;
2665 height /= format->height_scale.denominator;
2668 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",
2669 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2670 internal, width, height, format->glFormat, format->glType);
2672 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2674 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2675 || !surface->resource.allocatedMemory)
2677 /* In some cases we want to disable client storage.
2678 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2679 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2680 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2681 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2683 surface->flags &= ~SFLAG_CLIENT;
2685 else
2687 surface->flags |= SFLAG_CLIENT;
2689 /* Point OpenGL to our allocated texture memory. Do not use
2690 * resource.allocatedMemory here because it might point into a
2691 * PBO. Instead use heap_memory. */
2692 mem = surface->resource.heap_memory;
2694 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2695 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2696 disable_client_storage = TRUE;
2700 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2702 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2703 internal, width, height, 0, surface->resource.size, mem));
2704 checkGLcall("glCompressedTexImage2DARB");
2706 else
2708 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
2709 internal, width, height, 0, format->glFormat, format->glType, mem);
2710 checkGLcall("glTexImage2D");
2713 if (disable_client_storage)
2715 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2716 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2720 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2721 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2722 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2723 /* Context activation is done by the caller. */
2724 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2726 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2727 struct wined3d_renderbuffer_entry *entry;
2728 GLuint renderbuffer = 0;
2729 unsigned int src_width, src_height;
2730 unsigned int width, height;
2732 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2734 width = rt->pow2Width;
2735 height = rt->pow2Height;
2737 else
2739 width = surface->pow2Width;
2740 height = surface->pow2Height;
2743 src_width = surface->pow2Width;
2744 src_height = surface->pow2Height;
2746 /* A depth stencil smaller than the render target is not valid */
2747 if (width > src_width || height > src_height) return;
2749 /* Remove any renderbuffer set if the sizes match */
2750 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2751 || (width == src_width && height == src_height))
2753 surface->current_renderbuffer = NULL;
2754 return;
2757 /* Look if we've already got a renderbuffer of the correct dimensions */
2758 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2760 if (entry->width == width && entry->height == height)
2762 renderbuffer = entry->id;
2763 surface->current_renderbuffer = entry;
2764 break;
2768 if (!renderbuffer)
2770 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2771 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2772 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2773 surface->resource.format->glInternal, width, height);
2775 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2776 entry->width = width;
2777 entry->height = height;
2778 entry->id = renderbuffer;
2779 list_add_head(&surface->renderbuffers, &entry->entry);
2781 surface->current_renderbuffer = entry;
2784 checkGLcall("set_compatible_renderbuffer");
2787 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2789 const struct wined3d_swapchain *swapchain = surface->swapchain;
2791 TRACE("surface %p.\n", surface);
2793 if (!swapchain)
2795 ERR("Surface %p is not on a swapchain.\n", surface);
2796 return GL_NONE;
2799 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2801 if (swapchain->render_to_fbo)
2803 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2804 return GL_COLOR_ATTACHMENT0;
2806 TRACE("Returning GL_BACK\n");
2807 return GL_BACK;
2809 else if (surface == swapchain->front_buffer)
2811 TRACE("Returning GL_FRONT\n");
2812 return GL_FRONT;
2815 FIXME("Higher back buffer, returning GL_BACK\n");
2816 return GL_BACK;
2819 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2820 void surface_add_dirty_rect(struct wined3d_surface *surface, const struct wined3d_box *dirty_rect)
2822 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2824 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2825 /* No partial locking for textures yet. */
2826 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2828 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2829 if (dirty_rect)
2831 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->left);
2832 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->top);
2833 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->right);
2834 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->bottom);
2836 else
2838 surface->dirtyRect.left = 0;
2839 surface->dirtyRect.top = 0;
2840 surface->dirtyRect.right = surface->resource.width;
2841 surface->dirtyRect.bottom = surface->resource.height;
2844 /* if the container is a texture then mark it dirty. */
2845 if (surface->container)
2847 TRACE("Passing to container.\n");
2848 wined3d_texture_set_dirty(surface->container, TRUE);
2852 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2854 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2855 BOOL ck_changed;
2857 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2859 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
2861 ERR("Not supported on scratch surfaces.\n");
2862 return WINED3DERR_INVALIDCALL;
2865 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2867 /* Reload if either the texture and sysmem have different ideas about the
2868 * color key, or the actual key values changed. */
2869 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2870 && (surface->gl_color_key.color_space_low_value != surface->src_blt_color_key.color_space_low_value
2871 || surface->gl_color_key.color_space_high_value != surface->src_blt_color_key.color_space_high_value)))
2873 TRACE("Reloading because of color keying\n");
2874 /* To perform the color key conversion we need a sysmem copy of
2875 * the surface. Make sure we have it. */
2877 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2878 /* Make sure the texture is reloaded because of the color key change,
2879 * this kills performance though :( */
2880 /* TODO: This is not necessarily needed with hw palettized texture support. */
2881 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2882 /* Switching color keying on / off may change the internal format. */
2883 if (ck_changed)
2884 surface_force_reload(surface);
2886 else if (!(surface->flags & flag))
2888 TRACE("Reloading because surface is dirty.\n");
2890 else
2892 TRACE("surface is already in texture\n");
2893 return WINED3D_OK;
2896 /* No partial locking for textures yet. */
2897 surface_load_location(surface, flag, NULL);
2898 surface_evict_sysmem(surface);
2900 return WINED3D_OK;
2903 /* See also float_16_to_32() in wined3d_private.h */
2904 static inline unsigned short float_32_to_16(const float *in)
2906 int exp = 0;
2907 float tmp = fabsf(*in);
2908 unsigned int mantissa;
2909 unsigned short ret;
2911 /* Deal with special numbers */
2912 if (*in == 0.0f)
2913 return 0x0000;
2914 if (isnan(*in))
2915 return 0x7c01;
2916 if (isinf(*in))
2917 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2919 if (tmp < powf(2, 10))
2923 tmp = tmp * 2.0f;
2924 exp--;
2925 } while (tmp < powf(2, 10));
2927 else if (tmp >= powf(2, 11))
2931 tmp /= 2.0f;
2932 exp++;
2933 } while (tmp >= powf(2, 11));
2936 mantissa = (unsigned int)tmp;
2937 if (tmp - mantissa >= 0.5f)
2938 ++mantissa; /* Round to nearest, away from zero. */
2940 exp += 10; /* Normalize the mantissa. */
2941 exp += 15; /* Exponent is encoded with excess 15. */
2943 if (exp > 30) /* too big */
2945 ret = 0x7c00; /* INF */
2947 else if (exp <= 0)
2949 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2950 while (exp <= 0)
2952 mantissa = mantissa >> 1;
2953 ++exp;
2955 ret = mantissa & 0x3ff;
2957 else
2959 ret = (exp << 10) | (mantissa & 0x3ff);
2962 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2963 return ret;
2966 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2968 ULONG refcount;
2970 TRACE("surface %p, swapchain %p, container %p.\n",
2971 surface, surface->swapchain, surface->container);
2973 if (surface->swapchain)
2974 return wined3d_swapchain_incref(surface->swapchain);
2976 if (surface->container)
2977 return wined3d_texture_incref(surface->container);
2979 refcount = InterlockedIncrement(&surface->resource.ref);
2980 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2982 return refcount;
2985 /* Do not call while under the GL lock. */
2986 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2988 ULONG refcount;
2990 TRACE("surface %p, swapchain %p, container %p.\n",
2991 surface, surface->swapchain, surface->container);
2993 if (surface->swapchain)
2994 return wined3d_swapchain_decref(surface->swapchain);
2996 if (surface->container)
2997 return wined3d_texture_decref(surface->container);
2999 refcount = InterlockedDecrement(&surface->resource.ref);
3000 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
3002 if (!refcount)
3004 surface_cleanup(surface);
3005 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
3007 TRACE("Destroyed surface %p.\n", surface);
3008 HeapFree(GetProcessHeap(), 0, surface);
3011 return refcount;
3014 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
3016 return resource_set_priority(&surface->resource, priority);
3019 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
3021 return resource_get_priority(&surface->resource);
3024 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
3026 TRACE("surface %p.\n", surface);
3028 if (!surface->resource.device->d3d_initialized)
3030 ERR("D3D not initialized.\n");
3031 return;
3034 surface_internal_preload(surface, SRGB_ANY);
3037 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
3039 TRACE("surface %p.\n", surface);
3041 return surface->resource.parent;
3044 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
3046 TRACE("surface %p.\n", surface);
3048 return &surface->resource;
3051 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
3053 TRACE("surface %p, flags %#x.\n", surface, flags);
3055 switch (flags)
3057 case WINEDDGBS_CANBLT:
3058 case WINEDDGBS_ISBLTDONE:
3059 return WINED3D_OK;
3061 default:
3062 return WINED3DERR_INVALIDCALL;
3066 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
3068 TRACE("surface %p, flags %#x.\n", surface, flags);
3070 /* XXX: DDERR_INVALIDSURFACETYPE */
3072 switch (flags)
3074 case WINEDDGFS_CANFLIP:
3075 case WINEDDGFS_ISFLIPDONE:
3076 return WINED3D_OK;
3078 default:
3079 return WINED3DERR_INVALIDCALL;
3083 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
3085 TRACE("surface %p.\n", surface);
3087 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
3088 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
3091 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
3093 TRACE("surface %p.\n", surface);
3095 surface->flags &= ~SFLAG_LOST;
3096 return WINED3D_OK;
3099 void CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
3101 TRACE("surface %p, palette %p.\n", surface, palette);
3103 if (surface->palette == palette)
3105 TRACE("Nop palette change.\n");
3106 return;
3109 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
3110 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3112 surface->palette = palette;
3114 if (palette)
3116 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3117 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
3119 surface->surface_ops->surface_realize_palette(surface);
3123 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
3124 DWORD flags, const struct wined3d_color_key *color_key)
3126 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3128 if (flags & WINEDDCKEY_COLORSPACE)
3130 FIXME(" colorkey value not supported (%08x) !\n", flags);
3131 return WINED3DERR_INVALIDCALL;
3134 /* Dirtify the surface, but only if a key was changed. */
3135 if (color_key)
3137 switch (flags & ~WINEDDCKEY_COLORSPACE)
3139 case WINEDDCKEY_DESTBLT:
3140 surface->dst_blt_color_key = *color_key;
3141 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3142 break;
3144 case WINEDDCKEY_DESTOVERLAY:
3145 surface->dst_overlay_color_key = *color_key;
3146 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3147 break;
3149 case WINEDDCKEY_SRCOVERLAY:
3150 surface->src_overlay_color_key = *color_key;
3151 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3152 break;
3154 case WINEDDCKEY_SRCBLT:
3155 surface->src_blt_color_key = *color_key;
3156 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3157 break;
3160 else
3162 switch (flags & ~WINEDDCKEY_COLORSPACE)
3164 case WINEDDCKEY_DESTBLT:
3165 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3166 break;
3168 case WINEDDCKEY_DESTOVERLAY:
3169 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3170 break;
3172 case WINEDDCKEY_SRCOVERLAY:
3173 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3174 break;
3176 case WINEDDCKEY_SRCBLT:
3177 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3178 break;
3182 return WINED3D_OK;
3185 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3187 TRACE("surface %p.\n", surface);
3189 return surface->palette;
3192 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3194 const struct wined3d_format *format = surface->resource.format;
3195 DWORD pitch;
3197 TRACE("surface %p.\n", surface);
3199 if (surface->pitch)
3200 return surface->pitch;
3202 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
3204 /* Since compressed formats are block based, pitch means the amount of
3205 * bytes to the next row of block rather than the next row of pixels. */
3206 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3207 pitch = row_block_count * format->block_byte_count;
3209 else
3211 unsigned char alignment = surface->resource.device->surface_alignment;
3212 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3213 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3216 TRACE("Returning %u.\n", pitch);
3218 return pitch;
3221 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem, UINT pitch)
3223 TRACE("surface %p, mem %p.\n", surface, mem);
3225 if (surface->resource.map_count || (surface->flags & SFLAG_DCINUSE))
3227 WARN("Surface is mapped or the DC is in use.\n");
3228 return WINED3DERR_INVALIDCALL;
3231 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
3232 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3234 ERR("Not supported on render targets.\n");
3235 return WINED3DERR_INVALIDCALL;
3238 if (mem && mem != surface->resource.allocatedMemory)
3240 void *release = NULL;
3242 /* Do I have to copy the old surface content? */
3243 if (surface->flags & SFLAG_DIBSECTION)
3245 DeleteDC(surface->hDC);
3246 DeleteObject(surface->dib.DIBsection);
3247 surface->dib.bitmap_data = NULL;
3248 surface->resource.allocatedMemory = NULL;
3249 surface->hDC = NULL;
3250 surface->flags &= ~SFLAG_DIBSECTION;
3252 else if (!(surface->flags & SFLAG_USERPTR))
3254 release = surface->resource.heap_memory;
3255 surface->resource.heap_memory = NULL;
3257 surface->resource.allocatedMemory = mem;
3258 surface->flags |= SFLAG_USERPTR;
3260 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
3261 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3263 /* For client textures OpenGL has to be notified. */
3264 if (surface->flags & SFLAG_CLIENT)
3265 surface_release_client_storage(surface);
3267 /* Now free the old memory if any. */
3268 wined3d_resource_free_sysmem(release);
3270 else if (surface->flags & SFLAG_USERPTR)
3272 /* heap_memory should be NULL already. */
3273 if (surface->resource.heap_memory)
3274 ERR("User pointer surface has heap memory allocated.\n");
3276 if (!mem)
3278 surface->resource.allocatedMemory = NULL;
3279 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
3281 if (surface->flags & SFLAG_CLIENT)
3282 surface_release_client_storage(surface);
3284 surface_prepare_system_memory(surface);
3287 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3290 surface->pitch = pitch;
3292 return WINED3D_OK;
3295 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3297 LONG w, h;
3299 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3301 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3303 WARN("Not an overlay surface.\n");
3304 return WINEDDERR_NOTAOVERLAYSURFACE;
3307 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3308 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3309 surface->overlay_destrect.left = x;
3310 surface->overlay_destrect.top = y;
3311 surface->overlay_destrect.right = x + w;
3312 surface->overlay_destrect.bottom = y + h;
3314 surface_draw_overlay(surface);
3316 return WINED3D_OK;
3319 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3321 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3323 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3325 TRACE("Not an overlay surface.\n");
3326 return WINEDDERR_NOTAOVERLAYSURFACE;
3329 if (!surface->overlay_dest)
3331 TRACE("Overlay not visible.\n");
3332 *x = 0;
3333 *y = 0;
3334 return WINEDDERR_OVERLAYNOTVISIBLE;
3337 *x = surface->overlay_destrect.left;
3338 *y = surface->overlay_destrect.top;
3340 TRACE("Returning position %d, %d.\n", *x, *y);
3342 return WINED3D_OK;
3345 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3346 DWORD flags, struct wined3d_surface *ref)
3348 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3350 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3352 TRACE("Not an overlay surface.\n");
3353 return WINEDDERR_NOTAOVERLAYSURFACE;
3356 return WINED3D_OK;
3359 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3360 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3362 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3363 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3365 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3367 WARN("Not an overlay surface.\n");
3368 return WINEDDERR_NOTAOVERLAYSURFACE;
3370 else if (!dst_surface)
3372 WARN("Dest surface is NULL.\n");
3373 return WINED3DERR_INVALIDCALL;
3376 if (src_rect)
3378 surface->overlay_srcrect = *src_rect;
3380 else
3382 surface->overlay_srcrect.left = 0;
3383 surface->overlay_srcrect.top = 0;
3384 surface->overlay_srcrect.right = surface->resource.width;
3385 surface->overlay_srcrect.bottom = surface->resource.height;
3388 if (dst_rect)
3390 surface->overlay_destrect = *dst_rect;
3392 else
3394 surface->overlay_destrect.left = 0;
3395 surface->overlay_destrect.top = 0;
3396 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3397 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3400 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3402 surface->overlay_dest = NULL;
3403 list_remove(&surface->overlay_entry);
3406 if (flags & WINEDDOVER_SHOW)
3408 if (surface->overlay_dest != dst_surface)
3410 surface->overlay_dest = dst_surface;
3411 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3414 else if (flags & WINEDDOVER_HIDE)
3416 /* tests show that the rectangles are erased on hide */
3417 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3418 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3419 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3420 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3421 surface->overlay_dest = NULL;
3424 surface_draw_overlay(surface);
3426 return WINED3D_OK;
3429 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
3430 UINT width, UINT height, enum wined3d_format_id format_id,
3431 enum wined3d_multisample_type multisample_type, UINT multisample_quality)
3433 struct wined3d_device *device = surface->resource.device;
3434 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3435 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
3436 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height, 1);
3438 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u.\n",
3439 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type);
3441 if (!resource_size)
3442 return WINED3DERR_INVALIDCALL;
3444 if (device->d3d_initialized)
3445 surface->resource.resource_ops->resource_unload(&surface->resource);
3447 if (surface->flags & SFLAG_DIBSECTION)
3449 DeleteDC(surface->hDC);
3450 DeleteObject(surface->dib.DIBsection);
3451 surface->dib.bitmap_data = NULL;
3452 surface->flags &= ~SFLAG_DIBSECTION;
3455 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_USERPTR);
3456 surface->resource.allocatedMemory = NULL;
3457 wined3d_resource_free_sysmem(surface->resource.heap_memory);
3458 surface->resource.heap_memory = NULL;
3460 surface->resource.width = width;
3461 surface->resource.height = height;
3462 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
3463 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
3465 surface->pow2Width = width;
3466 surface->pow2Height = height;
3468 else
3470 surface->pow2Width = surface->pow2Height = 1;
3471 while (surface->pow2Width < width)
3472 surface->pow2Width <<= 1;
3473 while (surface->pow2Height < height)
3474 surface->pow2Height <<= 1;
3477 if (surface->pow2Width != width || surface->pow2Height != height)
3478 surface->flags |= SFLAG_NONPOW2;
3479 else
3480 surface->flags &= ~SFLAG_NONPOW2;
3482 surface->resource.format = format;
3483 surface->resource.multisample_type = multisample_type;
3484 surface->resource.multisample_quality = multisample_quality;
3485 surface->resource.size = resource_size;
3487 if (!surface_init_sysmem(surface))
3488 return E_OUTOFMEMORY;
3490 return WINED3D_OK;
3493 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3494 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3496 unsigned short *dst_s;
3497 const float *src_f;
3498 unsigned int x, y;
3500 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3502 for (y = 0; y < h; ++y)
3504 src_f = (const float *)(src + y * pitch_in);
3505 dst_s = (unsigned short *) (dst + y * pitch_out);
3506 for (x = 0; x < w; ++x)
3508 dst_s[x] = float_32_to_16(src_f + x);
3513 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3514 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3516 static const unsigned char convert_5to8[] =
3518 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3519 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3520 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3521 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3523 static const unsigned char convert_6to8[] =
3525 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3526 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3527 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3528 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3529 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3530 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3531 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3532 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3534 unsigned int x, y;
3536 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3538 for (y = 0; y < h; ++y)
3540 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3541 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3542 for (x = 0; x < w; ++x)
3544 WORD pixel = src_line[x];
3545 dst_line[x] = 0xff000000
3546 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3547 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3548 | convert_5to8[(pixel & 0x001f)];
3553 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3554 * in both cases we're just setting the X / Alpha channel to 0xff. */
3555 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3556 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3558 unsigned int x, y;
3560 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3562 for (y = 0; y < h; ++y)
3564 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3565 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3567 for (x = 0; x < w; ++x)
3569 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3574 static inline BYTE cliptobyte(int x)
3576 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3579 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3580 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3582 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3583 unsigned int x, y;
3585 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3587 for (y = 0; y < h; ++y)
3589 const BYTE *src_line = src + y * pitch_in;
3590 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3591 for (x = 0; x < w; ++x)
3593 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3594 * C = Y - 16; D = U - 128; E = V - 128;
3595 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3596 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3597 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3598 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3599 * U and V are shared between the pixels. */
3600 if (!(x & 1)) /* For every even pixel, read new U and V. */
3602 d = (int) src_line[1] - 128;
3603 e = (int) src_line[3] - 128;
3604 r2 = 409 * e + 128;
3605 g2 = - 100 * d - 208 * e + 128;
3606 b2 = 516 * d + 128;
3608 c2 = 298 * ((int) src_line[0] - 16);
3609 dst_line[x] = 0xff000000
3610 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3611 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3612 | cliptobyte((c2 + b2) >> 8); /* blue */
3613 /* Scale RGB values to 0..255 range,
3614 * then clip them if still not in range (may be negative),
3615 * then shift them within DWORD if necessary. */
3616 src_line += 2;
3621 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3622 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3624 unsigned int x, y;
3625 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3627 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3629 for (y = 0; y < h; ++y)
3631 const BYTE *src_line = src + y * pitch_in;
3632 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3633 for (x = 0; x < w; ++x)
3635 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3636 * C = Y - 16; D = U - 128; E = V - 128;
3637 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3638 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3639 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3640 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3641 * U and V are shared between the pixels. */
3642 if (!(x & 1)) /* For every even pixel, read new U and V. */
3644 d = (int) src_line[1] - 128;
3645 e = (int) src_line[3] - 128;
3646 r2 = 409 * e + 128;
3647 g2 = - 100 * d - 208 * e + 128;
3648 b2 = 516 * d + 128;
3650 c2 = 298 * ((int) src_line[0] - 16);
3651 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3652 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3653 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3654 /* Scale RGB values to 0..255 range,
3655 * then clip them if still not in range (may be negative),
3656 * then shift them within DWORD if necessary. */
3657 src_line += 2;
3662 struct d3dfmt_converter_desc
3664 enum wined3d_format_id from, to;
3665 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3668 static const struct d3dfmt_converter_desc converters[] =
3670 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3671 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3672 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3673 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3674 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3675 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3678 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
3679 enum wined3d_format_id to)
3681 unsigned int i;
3683 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
3685 if (converters[i].from == from && converters[i].to == to)
3686 return &converters[i];
3689 return NULL;
3692 /*****************************************************************************
3693 * surface_convert_format
3695 * Creates a duplicate of a surface in a different format. Is used by Blt to
3696 * blit between surfaces with different formats.
3698 * Parameters
3699 * source: Source surface
3700 * fmt: Requested destination format
3702 *****************************************************************************/
3703 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3705 struct wined3d_map_desc src_map, dst_map;
3706 const struct d3dfmt_converter_desc *conv;
3707 struct wined3d_surface *ret = NULL;
3708 HRESULT hr;
3710 conv = find_converter(source->resource.format->id, to_fmt);
3711 if (!conv)
3713 FIXME("Cannot find a conversion function from format %s to %s.\n",
3714 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3715 return NULL;
3718 /* FIXME: Multisampled conversion? */
3719 if (FAILED(hr = wined3d_surface_create(source->resource.device, source->resource.width, source->resource.height,
3720 to_fmt, 0, WINED3D_POOL_SCRATCH, WINED3D_MULTISAMPLE_NONE, 0,
3721 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, &wined3d_null_parent_ops, &ret)))
3723 ERR("Failed to create a destination surface for conversion.\n");
3724 return NULL;
3727 memset(&src_map, 0, sizeof(src_map));
3728 memset(&dst_map, 0, sizeof(dst_map));
3730 if (FAILED(hr = wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
3732 ERR("Failed to lock the source surface.\n");
3733 wined3d_surface_decref(ret);
3734 return NULL;
3736 if (FAILED(hr = wined3d_surface_map(ret, &dst_map, NULL, WINED3D_MAP_READONLY)))
3738 ERR("Failed to lock the destination surface.\n");
3739 wined3d_surface_unmap(source);
3740 wined3d_surface_decref(ret);
3741 return NULL;
3744 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
3745 source->resource.width, source->resource.height);
3747 wined3d_surface_unmap(ret);
3748 wined3d_surface_unmap(source);
3750 return ret;
3753 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3754 unsigned int bpp, UINT pitch, DWORD color)
3756 BYTE *first;
3757 unsigned int x, y;
3759 /* Do first row */
3761 #define COLORFILL_ROW(type) \
3762 do { \
3763 type *d = (type *)buf; \
3764 for (x = 0; x < width; ++x) \
3765 d[x] = (type)color; \
3766 } while(0)
3768 switch (bpp)
3770 case 1:
3771 COLORFILL_ROW(BYTE);
3772 break;
3774 case 2:
3775 COLORFILL_ROW(WORD);
3776 break;
3778 case 3:
3780 BYTE *d = buf;
3781 for (x = 0; x < width; ++x, d += 3)
3783 d[0] = (color ) & 0xff;
3784 d[1] = (color >> 8) & 0xff;
3785 d[2] = (color >> 16) & 0xff;
3787 break;
3789 case 4:
3790 COLORFILL_ROW(DWORD);
3791 break;
3793 default:
3794 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3795 return WINED3DERR_NOTAVAILABLE;
3798 #undef COLORFILL_ROW
3800 /* Now copy first row. */
3801 first = buf;
3802 for (y = 1; y < height; ++y)
3804 buf += pitch;
3805 memcpy(buf, first, width * bpp);
3808 return WINED3D_OK;
3811 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
3813 return surface_from_resource(resource);
3816 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3818 TRACE("surface %p.\n", surface);
3820 if (!surface->resource.map_count)
3822 WARN("Trying to unmap unmapped surface.\n");
3823 return WINEDDERR_NOTLOCKED;
3825 --surface->resource.map_count;
3827 surface->surface_ops->surface_unmap(surface);
3829 return WINED3D_OK;
3832 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3833 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
3835 const struct wined3d_format *format = surface->resource.format;
3837 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
3838 surface, map_desc, wine_dbgstr_rect(rect), flags);
3840 if (surface->resource.map_count)
3842 WARN("Surface is already mapped.\n");
3843 return WINED3DERR_INVALIDCALL;
3846 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
3847 && !surface_check_block_align(surface, rect))
3849 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3850 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3852 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
3853 return WINED3DERR_INVALIDCALL;
3856 ++surface->resource.map_count;
3858 if (!(surface->flags & SFLAG_LOCKABLE))
3859 WARN("Trying to lock unlockable surface.\n");
3861 /* Performance optimization: Count how often a surface is mapped, if it is
3862 * mapped regularly do not throw away the system memory copy. This avoids
3863 * the need to download the surface from OpenGL all the time. The surface
3864 * is still downloaded if the OpenGL texture is changed. */
3865 if (!(surface->flags & SFLAG_DYNLOCK))
3867 if (++surface->lockCount > MAXLOCKCOUNT)
3869 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3870 surface->flags |= SFLAG_DYNLOCK;
3874 surface->surface_ops->surface_map(surface, rect, flags);
3876 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3877 map_desc->row_pitch = surface->resource.width * format->byte_count;
3878 else
3879 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
3880 map_desc->slice_pitch = 0;
3882 if (!rect)
3884 map_desc->data = surface->resource.allocatedMemory;
3885 surface->lockedRect.left = 0;
3886 surface->lockedRect.top = 0;
3887 surface->lockedRect.right = surface->resource.width;
3888 surface->lockedRect.bottom = surface->resource.height;
3890 else
3892 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3894 /* Compressed textures are block based, so calculate the offset of
3895 * the block that contains the top-left pixel of the locked rectangle. */
3896 map_desc->data = surface->resource.allocatedMemory
3897 + ((rect->top / format->block_height) * map_desc->row_pitch)
3898 + ((rect->left / format->block_width) * format->block_byte_count);
3900 else
3902 map_desc->data = surface->resource.allocatedMemory
3903 + (map_desc->row_pitch * rect->top)
3904 + (rect->left * format->byte_count);
3906 surface->lockedRect.left = rect->left;
3907 surface->lockedRect.top = rect->top;
3908 surface->lockedRect.right = rect->right;
3909 surface->lockedRect.bottom = rect->bottom;
3912 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3913 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
3915 return WINED3D_OK;
3918 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3920 struct wined3d_map_desc map;
3921 HRESULT hr;
3923 TRACE("surface %p, dc %p.\n", surface, dc);
3925 if (surface->flags & SFLAG_USERPTR)
3927 ERR("Not supported on surfaces with application-provided memory.\n");
3928 return WINEDDERR_NODC;
3931 /* Give more detailed info for ddraw. */
3932 if (surface->flags & SFLAG_DCINUSE)
3933 return WINEDDERR_DCALREADYCREATED;
3935 /* Can't GetDC if the surface is locked. */
3936 if (surface->resource.map_count)
3937 return WINED3DERR_INVALIDCALL;
3939 /* Create a DIB section if there isn't a dc yet. */
3940 if (!surface->hDC)
3942 if (surface->flags & SFLAG_CLIENT)
3944 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3945 surface_release_client_storage(surface);
3947 hr = surface_create_dib_section(surface);
3948 if (FAILED(hr))
3949 return WINED3DERR_INVALIDCALL;
3951 /* Use the DIB section from now on if we are not using a PBO. */
3952 if (!(surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)))
3954 wined3d_resource_free_sysmem(surface->resource.heap_memory);
3955 surface->resource.heap_memory = NULL;
3956 surface->resource.allocatedMemory = surface->dib.bitmap_data;
3960 /* Map the surface. */
3961 hr = wined3d_surface_map(surface, &map, NULL, 0);
3962 if (FAILED(hr))
3964 ERR("Map failed, hr %#x.\n", hr);
3965 return hr;
3968 /* Sync the DIB with the PBO. This can't be done earlier because Map()
3969 * activates the allocatedMemory. */
3970 if (surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM))
3971 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
3973 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3974 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3976 /* GetDC on palettized formats is unsupported in D3D9, and the method
3977 * is missing in D3D8, so this should only be used for DX <=7
3978 * surfaces (with non-device palettes). */
3979 const PALETTEENTRY *pal = NULL;
3981 if (surface->palette)
3983 pal = surface->palette->palents;
3985 else
3987 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3988 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3990 if (dds_primary && dds_primary->palette)
3991 pal = dds_primary->palette->palents;
3994 if (pal)
3996 RGBQUAD col[256];
3997 unsigned int i;
3999 for (i = 0; i < 256; ++i)
4001 col[i].rgbRed = pal[i].peRed;
4002 col[i].rgbGreen = pal[i].peGreen;
4003 col[i].rgbBlue = pal[i].peBlue;
4004 col[i].rgbReserved = 0;
4006 SetDIBColorTable(surface->hDC, 0, 256, col);
4010 surface->flags |= SFLAG_DCINUSE;
4012 *dc = surface->hDC;
4013 TRACE("Returning dc %p.\n", *dc);
4015 return WINED3D_OK;
4018 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
4020 TRACE("surface %p, dc %p.\n", surface, dc);
4022 if (!(surface->flags & SFLAG_DCINUSE))
4023 return WINEDDERR_NODC;
4025 if (surface->hDC != dc)
4027 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
4028 dc, surface->hDC);
4029 return WINEDDERR_NODC;
4032 /* Copy the contents of the DIB over to the PBO. */
4033 if ((surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)) && surface->resource.allocatedMemory)
4034 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
4036 /* We locked first, so unlock now. */
4037 wined3d_surface_unmap(surface);
4039 surface->flags &= ~SFLAG_DCINUSE;
4041 return WINED3D_OK;
4044 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
4046 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
4048 if (flags)
4050 static UINT once;
4051 if (!once++)
4052 FIXME("Ignoring flags %#x.\n", flags);
4053 else
4054 WARN("Ignoring flags %#x.\n", flags);
4057 if (surface->swapchain)
4059 ERR("Not supported on swapchain surfaces.\n");
4060 return WINEDDERR_NOTFLIPPABLE;
4063 /* Flipping is only supported on render targets and overlays. */
4064 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
4066 WARN("Tried to flip a non-render target, non-overlay surface.\n");
4067 return WINEDDERR_NOTFLIPPABLE;
4070 flip_surface(surface, override);
4072 /* Update overlays if they're visible. */
4073 if ((surface->resource.usage & WINED3DUSAGE_OVERLAY) && surface->overlay_dest)
4074 return surface_draw_overlay(surface);
4076 return WINED3D_OK;
4079 /* Do not call while under the GL lock. */
4080 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
4082 struct wined3d_device *device = surface->resource.device;
4084 TRACE("iface %p, srgb %#x.\n", surface, srgb);
4086 if (surface->container)
4088 struct wined3d_texture *texture = surface->container;
4090 TRACE("Passing to container (%p).\n", texture);
4091 texture->texture_ops->texture_preload(texture, srgb);
4093 else
4095 struct wined3d_context *context;
4097 TRACE("(%p) : About to load surface\n", surface);
4099 /* TODO: Use already acquired context when possible. */
4100 context = context_acquire(device, NULL);
4102 surface_load(surface, srgb == SRGB_SRGB);
4104 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
4106 /* Tell opengl to try and keep this texture in video ram (well mostly) */
4107 GLclampf tmp;
4108 tmp = 0.9f;
4109 context->gl_info->gl_ops.gl.p_glPrioritizeTextures(1, &surface->texture_name, &tmp);
4112 context_release(context);
4116 /* Read the framebuffer back into the surface */
4117 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
4119 struct wined3d_device *device = surface->resource.device;
4120 const struct wined3d_gl_info *gl_info;
4121 struct wined3d_context *context;
4122 BYTE *mem;
4123 GLint fmt;
4124 GLint type;
4125 BYTE *row, *top, *bottom;
4126 int i;
4127 BOOL bpp;
4128 RECT local_rect;
4129 BOOL srcIsUpsideDown;
4130 GLint rowLen = 0;
4131 GLint skipPix = 0;
4132 GLint skipRow = 0;
4134 context = context_acquire(device, surface);
4135 context_apply_blit_state(context, device);
4136 gl_info = context->gl_info;
4138 /* Select the correct read buffer, and give some debug output.
4139 * There is no need to keep track of the current read buffer or reset it, every part of the code
4140 * that reads sets the read buffer as desired.
4142 if (surface_is_offscreen(surface))
4144 /* Mapping the primary render target which is not on a swapchain.
4145 * Read from the back buffer. */
4146 TRACE("Mapping offscreen render target.\n");
4147 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
4148 srcIsUpsideDown = TRUE;
4150 else
4152 /* Onscreen surfaces are always part of a swapchain */
4153 GLenum buffer = surface_get_gl_buffer(surface);
4154 TRACE("Mapping %#x buffer.\n", buffer);
4155 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
4156 checkGLcall("glReadBuffer");
4157 srcIsUpsideDown = FALSE;
4160 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
4161 if (!rect)
4163 local_rect.left = 0;
4164 local_rect.top = 0;
4165 local_rect.right = surface->resource.width;
4166 local_rect.bottom = surface->resource.height;
4168 else
4170 local_rect = *rect;
4172 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
4174 switch (surface->resource.format->id)
4176 case WINED3DFMT_P8_UINT:
4178 if (primary_render_target_is_p8(device))
4180 /* In case of P8 render targets the index is stored in the alpha component */
4181 fmt = GL_ALPHA;
4182 type = GL_UNSIGNED_BYTE;
4183 mem = dest;
4184 bpp = surface->resource.format->byte_count;
4186 else
4188 /* GL can't return palettized data, so read ARGB pixels into a
4189 * separate block of memory and convert them into palettized format
4190 * in software. Slow, but if the app means to use palettized render
4191 * targets and locks it...
4193 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4194 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4195 * for the color channels when palettizing the colors.
4197 fmt = GL_RGB;
4198 type = GL_UNSIGNED_BYTE;
4199 pitch *= 3;
4200 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4201 if (!mem)
4203 ERR("Out of memory\n");
4204 return;
4206 bpp = surface->resource.format->byte_count * 3;
4209 break;
4211 default:
4212 mem = dest;
4213 fmt = surface->resource.format->glFormat;
4214 type = surface->resource.format->glType;
4215 bpp = surface->resource.format->byte_count;
4218 if (surface->flags & SFLAG_PBO)
4220 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4221 checkGLcall("glBindBufferARB");
4222 if (mem)
4224 ERR("mem not null for pbo -- unexpected\n");
4225 mem = NULL;
4229 /* Save old pixel store pack state */
4230 gl_info->gl_ops.gl.p_glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4231 checkGLcall("glGetIntegerv");
4232 gl_info->gl_ops.gl.p_glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4233 checkGLcall("glGetIntegerv");
4234 gl_info->gl_ops.gl.p_glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4235 checkGLcall("glGetIntegerv");
4237 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4238 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4239 checkGLcall("glPixelStorei");
4240 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4241 checkGLcall("glPixelStorei");
4242 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4243 checkGLcall("glPixelStorei");
4245 gl_info->gl_ops.gl.p_glReadPixels(local_rect.left,
4246 !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4247 local_rect.right - local_rect.left,
4248 local_rect.bottom - local_rect.top,
4249 fmt, type, mem);
4250 checkGLcall("glReadPixels");
4252 /* Reset previous pixel store pack state */
4253 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4254 checkGLcall("glPixelStorei");
4255 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4256 checkGLcall("glPixelStorei");
4257 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4258 checkGLcall("glPixelStorei");
4260 if (surface->flags & SFLAG_PBO)
4262 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4263 checkGLcall("glBindBufferARB");
4265 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4266 * to get a pointer to it and perform the flipping in software. This is a lot
4267 * faster than calling glReadPixels for each line. In case we want more speed
4268 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4269 if (!srcIsUpsideDown)
4271 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4272 checkGLcall("glBindBufferARB");
4274 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4275 checkGLcall("glMapBufferARB");
4279 /* TODO: Merge this with the palettization loop below for P8 targets */
4280 if(!srcIsUpsideDown) {
4281 UINT len, off;
4282 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4283 Flip the lines in software */
4284 len = (local_rect.right - local_rect.left) * bpp;
4285 off = local_rect.left * bpp;
4287 row = HeapAlloc(GetProcessHeap(), 0, len);
4288 if(!row) {
4289 ERR("Out of memory\n");
4290 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4291 HeapFree(GetProcessHeap(), 0, mem);
4292 return;
4295 top = mem + pitch * local_rect.top;
4296 bottom = mem + pitch * (local_rect.bottom - 1);
4297 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4298 memcpy(row, top + off, len);
4299 memcpy(top + off, bottom + off, len);
4300 memcpy(bottom + off, row, len);
4301 top += pitch;
4302 bottom -= pitch;
4304 HeapFree(GetProcessHeap(), 0, row);
4306 /* Unmap the temp PBO buffer */
4307 if (surface->flags & SFLAG_PBO)
4309 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4310 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4314 context_release(context);
4316 /* For P8 textures we need to perform an inverse palette lookup. This is
4317 * done by searching for a palette index which matches the RGB value.
4318 * Note this isn't guaranteed to work when there are multiple entries for
4319 * the same color but we have no choice. In case of P8 render targets,
4320 * the index is stored in the alpha component so no conversion is needed. */
4321 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4323 const PALETTEENTRY *pal = NULL;
4324 DWORD width = pitch / 3;
4325 int x, y, c;
4327 if (surface->palette)
4329 pal = surface->palette->palents;
4331 else
4333 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4334 HeapFree(GetProcessHeap(), 0, mem);
4335 return;
4338 for(y = local_rect.top; y < local_rect.bottom; y++) {
4339 for(x = local_rect.left; x < local_rect.right; x++) {
4340 /* start lines pixels */
4341 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4342 const BYTE *green = blue + 1;
4343 const BYTE *red = green + 1;
4345 for(c = 0; c < 256; c++) {
4346 if(*red == pal[c].peRed &&
4347 *green == pal[c].peGreen &&
4348 *blue == pal[c].peBlue)
4350 *((BYTE *) dest + y * width + x) = c;
4351 break;
4356 HeapFree(GetProcessHeap(), 0, mem);
4360 /* Read the framebuffer contents into a texture. Note that this function
4361 * doesn't do any kind of flipping. Using this on an onscreen surface will
4362 * result in a flipped D3D texture. */
4363 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
4365 struct wined3d_device *device = surface->resource.device;
4366 const struct wined3d_gl_info *gl_info;
4367 struct wined3d_context *context;
4369 context = context_acquire(device, surface);
4370 gl_info = context->gl_info;
4371 device_invalidate_state(device, STATE_FRAMEBUFFER);
4373 surface_prepare_texture(surface, context, srgb);
4374 surface_bind_and_dirtify(surface, context, srgb);
4376 TRACE("Reading back offscreen render target %p.\n", surface);
4378 if (surface_is_offscreen(surface))
4379 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
4380 else
4381 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
4382 checkGLcall("glReadBuffer");
4384 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4385 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4386 checkGLcall("glCopyTexSubImage2D");
4388 context_release(context);
4391 /* Context activation is done by the caller. */
4392 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4393 struct wined3d_context *context, BOOL srgb)
4395 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4396 enum wined3d_conversion_type convert;
4397 struct wined3d_format format;
4399 if (surface->flags & alloc_flag) return;
4401 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4402 if (convert != WINED3D_CT_NONE || format.convert)
4403 surface->flags |= SFLAG_CONVERTED;
4404 else surface->flags &= ~SFLAG_CONVERTED;
4406 surface_bind_and_dirtify(surface, context, srgb);
4407 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4408 surface->flags |= alloc_flag;
4411 /* Context activation is done by the caller. */
4412 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4414 if (surface->container)
4416 struct wined3d_texture *texture = surface->container;
4417 UINT sub_count = texture->level_count * texture->layer_count;
4418 UINT i;
4420 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4422 for (i = 0; i < sub_count; ++i)
4424 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4425 surface_prepare_texture_internal(s, context, srgb);
4428 return;
4431 surface_prepare_texture_internal(surface, context, srgb);
4434 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4436 if (multisample)
4438 if (surface->rb_multisample)
4439 return;
4441 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4442 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4443 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4444 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4445 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4447 else
4449 if (surface->rb_resolved)
4450 return;
4452 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4453 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4454 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4455 surface->pow2Width, surface->pow2Height);
4456 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4460 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4461 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4463 struct wined3d_device *device = surface->resource.device;
4464 UINT pitch = wined3d_surface_get_pitch(surface);
4465 const struct wined3d_gl_info *gl_info;
4466 struct wined3d_context *context;
4467 RECT local_rect;
4468 UINT w, h;
4470 surface_get_rect(surface, rect, &local_rect);
4472 mem += local_rect.top * pitch + local_rect.left * bpp;
4473 w = local_rect.right - local_rect.left;
4474 h = local_rect.bottom - local_rect.top;
4476 /* Activate the correct context for the render target */
4477 context = context_acquire(device, surface);
4478 context_apply_blit_state(context, device);
4479 gl_info = context->gl_info;
4481 if (!surface_is_offscreen(surface))
4483 GLenum buffer = surface_get_gl_buffer(surface);
4484 TRACE("Unlocking %#x buffer.\n", buffer);
4485 context_set_draw_buffer(context, buffer);
4487 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4488 gl_info->gl_ops.gl.p_glPixelZoom(1.0f, -1.0f);
4490 else
4492 /* Primary offscreen render target */
4493 TRACE("Offscreen render target.\n");
4494 context_set_draw_buffer(context, device->offscreenBuffer);
4496 gl_info->gl_ops.gl.p_glPixelZoom(1.0f, 1.0f);
4499 gl_info->gl_ops.gl.p_glRasterPos3i(local_rect.left, local_rect.top, 1);
4500 checkGLcall("glRasterPos3i");
4502 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4503 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4505 if (surface->flags & SFLAG_PBO)
4507 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4508 checkGLcall("glBindBufferARB");
4511 gl_info->gl_ops.gl.p_glDrawPixels(w, h, fmt, type, mem);
4512 checkGLcall("glDrawPixels");
4514 if (surface->flags & SFLAG_PBO)
4516 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4517 checkGLcall("glBindBufferARB");
4520 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4521 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4523 if (wined3d_settings.strict_draw_ordering
4524 || (surface->swapchain && surface->swapchain->front_buffer == surface))
4525 gl_info->gl_ops.gl.p_glFlush();
4527 context_release(context);
4530 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
4532 /* FIXME: Is this really how color keys are supposed to work? I think it
4533 * makes more sense to compare the individual channels. */
4534 return color >= color_key->color_space_low_value
4535 && color <= color_key->color_space_high_value;
4538 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4540 const struct wined3d_device *device = surface->resource.device;
4541 const struct wined3d_palette *pal = surface->palette;
4542 BOOL index_in_alpha = FALSE;
4543 unsigned int i;
4545 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4546 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4547 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4548 * duplicate entries. Store the color key in the unused alpha component to speed the
4549 * download up and to make conversion unneeded. */
4550 index_in_alpha = primary_render_target_is_p8(device);
4552 if (!pal)
4554 FIXME("No palette set.\n");
4555 if (index_in_alpha)
4557 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4558 * there's no palette at this time. */
4559 for (i = 0; i < 256; i++) table[i][3] = i;
4562 else
4564 TRACE("Using surface palette %p\n", pal);
4565 /* Get the surface's palette */
4566 for (i = 0; i < 256; ++i)
4568 table[i][0] = pal->palents[i].peRed;
4569 table[i][1] = pal->palents[i].peGreen;
4570 table[i][2] = pal->palents[i].peBlue;
4572 /* When index_in_alpha is set the palette index is stored in the
4573 * alpha component. In case of a readback we can then read
4574 * GL_ALPHA. Color keying is handled in BltOverride using a
4575 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4576 * color key itself is passed to glAlphaFunc in other cases the
4577 * alpha component of pixels that should be masked away is set to 0. */
4578 if (index_in_alpha)
4579 table[i][3] = i;
4580 else if (colorkey && color_in_range(&surface->src_blt_color_key, i))
4581 table[i][3] = 0x00;
4582 else if (pal->flags & WINEDDPCAPS_ALPHA)
4583 table[i][3] = pal->palents[i].peFlags;
4584 else
4585 table[i][3] = 0xff;
4590 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
4591 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
4593 const BYTE *source;
4594 BYTE *dest;
4596 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
4597 src, dst, pitch, width, height, outpitch, conversion_type, surface);
4599 switch (conversion_type)
4601 case WINED3D_CT_NONE:
4603 memcpy(dst, src, pitch * height);
4604 break;
4607 case WINED3D_CT_PALETTED:
4608 case WINED3D_CT_PALETTED_CK:
4610 BYTE table[256][4];
4611 unsigned int x, y;
4613 d3dfmt_p8_init_palette(surface, table, (conversion_type == WINED3D_CT_PALETTED_CK));
4615 for (y = 0; y < height; y++)
4617 source = src + pitch * y;
4618 dest = dst + outpitch * y;
4619 /* This is an 1 bpp format, using the width here is fine */
4620 for (x = 0; x < width; x++) {
4621 BYTE color = *source++;
4622 *dest++ = table[color][0];
4623 *dest++ = table[color][1];
4624 *dest++ = table[color][2];
4625 *dest++ = table[color][3];
4629 break;
4631 case WINED3D_CT_CK_565:
4633 /* Converting the 565 format in 5551 packed to emulate color-keying.
4635 Note : in all these conversion, it would be best to average the averaging
4636 pixels to get the color of the pixel that will be color-keyed to
4637 prevent 'color bleeding'. This will be done later on if ever it is
4638 too visible.
4640 Note2: Nvidia documents say that their driver does not support alpha + color keying
4641 on the same surface and disables color keying in such a case
4643 unsigned int x, y;
4644 const WORD *Source;
4645 WORD *Dest;
4647 TRACE("Color keyed 565\n");
4649 for (y = 0; y < height; y++) {
4650 Source = (const WORD *)(src + y * pitch);
4651 Dest = (WORD *) (dst + y * outpitch);
4652 for (x = 0; x < width; x++ ) {
4653 WORD color = *Source++;
4654 *Dest = ((color & 0xffc0) | ((color & 0x1f) << 1));
4655 if (!color_in_range(&surface->src_blt_color_key, color))
4656 *Dest |= 0x0001;
4657 Dest++;
4661 break;
4663 case WINED3D_CT_CK_5551:
4665 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4666 unsigned int x, y;
4667 const WORD *Source;
4668 WORD *Dest;
4669 TRACE("Color keyed 5551\n");
4670 for (y = 0; y < height; y++) {
4671 Source = (const WORD *)(src + y * pitch);
4672 Dest = (WORD *) (dst + y * outpitch);
4673 for (x = 0; x < width; x++ ) {
4674 WORD color = *Source++;
4675 *Dest = color;
4676 if (!color_in_range(&surface->src_blt_color_key, color))
4677 *Dest |= (1 << 15);
4678 else
4679 *Dest &= ~(1 << 15);
4680 Dest++;
4684 break;
4686 case WINED3D_CT_CK_RGB24:
4688 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4689 unsigned int x, y;
4690 for (y = 0; y < height; y++)
4692 source = src + pitch * y;
4693 dest = dst + outpitch * y;
4694 for (x = 0; x < width; x++) {
4695 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4696 DWORD dstcolor = color << 8;
4697 if (!color_in_range(&surface->src_blt_color_key, color))
4698 dstcolor |= 0xff;
4699 *(DWORD*)dest = dstcolor;
4700 source += 3;
4701 dest += 4;
4705 break;
4707 case WINED3D_CT_RGB32_888:
4709 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4710 unsigned int x, y;
4711 for (y = 0; y < height; y++)
4713 source = src + pitch * y;
4714 dest = dst + outpitch * y;
4715 for (x = 0; x < width; x++) {
4716 DWORD color = 0xffffff & *(const DWORD*)source;
4717 DWORD dstcolor = color << 8;
4718 if (!color_in_range(&surface->src_blt_color_key, color))
4719 dstcolor |= 0xff;
4720 *(DWORD*)dest = dstcolor;
4721 source += 4;
4722 dest += 4;
4726 break;
4728 case WINED3D_CT_CK_ARGB32:
4730 unsigned int x, y;
4731 for (y = 0; y < height; ++y)
4733 source = src + pitch * y;
4734 dest = dst + outpitch * y;
4735 for (x = 0; x < width; ++x)
4737 DWORD color = *(const DWORD *)source;
4738 if (color_in_range(&surface->src_blt_color_key, color))
4739 color &= ~0xff000000;
4740 *(DWORD*)dest = color;
4741 source += 4;
4742 dest += 4;
4746 break;
4748 default:
4749 ERR("Unsupported conversion type %#x.\n", conversion_type);
4751 return WINED3D_OK;
4754 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4756 /* Flip the surface contents */
4757 /* Flip the DC */
4759 HDC tmp;
4760 tmp = front->hDC;
4761 front->hDC = back->hDC;
4762 back->hDC = tmp;
4765 /* Flip the DIBsection */
4767 HBITMAP tmp = front->dib.DIBsection;
4768 front->dib.DIBsection = back->dib.DIBsection;
4769 back->dib.DIBsection = tmp;
4772 /* Flip the surface data */
4774 void* tmp;
4776 tmp = front->dib.bitmap_data;
4777 front->dib.bitmap_data = back->dib.bitmap_data;
4778 back->dib.bitmap_data = tmp;
4780 tmp = front->resource.allocatedMemory;
4781 front->resource.allocatedMemory = back->resource.allocatedMemory;
4782 back->resource.allocatedMemory = tmp;
4784 tmp = front->resource.heap_memory;
4785 front->resource.heap_memory = back->resource.heap_memory;
4786 back->resource.heap_memory = tmp;
4789 /* Flip the PBO */
4791 GLuint tmp_pbo = front->pbo;
4792 front->pbo = back->pbo;
4793 back->pbo = tmp_pbo;
4796 /* Flip the opengl texture */
4798 GLuint tmp;
4800 tmp = back->texture_name;
4801 back->texture_name = front->texture_name;
4802 front->texture_name = tmp;
4804 tmp = back->texture_name_srgb;
4805 back->texture_name_srgb = front->texture_name_srgb;
4806 front->texture_name_srgb = tmp;
4808 tmp = back->rb_multisample;
4809 back->rb_multisample = front->rb_multisample;
4810 front->rb_multisample = tmp;
4812 tmp = back->rb_resolved;
4813 back->rb_resolved = front->rb_resolved;
4814 front->rb_resolved = tmp;
4816 resource_unload(&back->resource);
4817 resource_unload(&front->resource);
4821 DWORD tmp_flags = back->flags;
4822 back->flags = front->flags;
4823 front->flags = tmp_flags;
4827 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4828 * pixel copy calls. */
4829 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4830 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4832 struct wined3d_device *device = dst_surface->resource.device;
4833 const struct wined3d_gl_info *gl_info;
4834 float xrel, yrel;
4835 struct wined3d_context *context;
4836 BOOL upsidedown = FALSE;
4837 RECT dst_rect = *dst_rect_in;
4838 GLenum dst_target;
4840 if (dst_surface->container)
4841 dst_target = dst_surface->container->target;
4842 else
4843 dst_target = dst_surface->texture_target;
4845 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4846 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4848 if(dst_rect.top > dst_rect.bottom) {
4849 UINT tmp = dst_rect.bottom;
4850 dst_rect.bottom = dst_rect.top;
4851 dst_rect.top = tmp;
4852 upsidedown = TRUE;
4855 context = context_acquire(device, src_surface);
4856 gl_info = context->gl_info;
4857 context_apply_blit_state(context, device);
4858 surface_internal_preload(dst_surface, SRGB_RGB);
4860 /* Bind the target texture */
4861 context_bind_texture(context, dst_target, dst_surface->texture_name);
4862 if (surface_is_offscreen(src_surface))
4864 TRACE("Reading from an offscreen target\n");
4865 upsidedown = !upsidedown;
4866 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
4868 else
4870 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
4872 checkGLcall("glReadBuffer");
4874 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4875 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4877 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4879 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4881 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4882 ERR("Texture filtering not supported in direct blit.\n");
4884 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4885 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4887 ERR("Texture filtering not supported in direct blit\n");
4890 if (upsidedown
4891 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4892 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4894 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
4895 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4896 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4897 src_rect->left, src_surface->resource.height - src_rect->bottom,
4898 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4900 else
4902 LONG row;
4903 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4904 /* I have to process this row by row to swap the image,
4905 * otherwise it would be upside down, so stretching in y direction
4906 * doesn't cost extra time
4908 * However, stretching in x direction can be avoided if not necessary
4910 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4911 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4913 /* Well, that stuff works, but it's very slow.
4914 * find a better way instead
4916 LONG col;
4918 for (col = dst_rect.left; col < dst_rect.right; ++col)
4920 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4921 dst_rect.left + col /* x offset */, row /* y offset */,
4922 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4925 else
4927 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4928 dst_rect.left /* x offset */, row /* y offset */,
4929 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4933 checkGLcall("glCopyTexSubImage2D");
4935 context_release(context);
4937 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4938 * path is never entered
4940 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4943 /* Uses the hardware to stretch and flip the image */
4944 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4945 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4947 struct wined3d_device *device = dst_surface->resource.device;
4948 GLuint src, backup = 0;
4949 float left, right, top, bottom; /* Texture coordinates */
4950 UINT fbwidth = src_surface->resource.width;
4951 UINT fbheight = src_surface->resource.height;
4952 const struct wined3d_gl_info *gl_info;
4953 struct wined3d_context *context;
4954 GLenum drawBuffer = GL_BACK;
4955 GLenum texture_target;
4956 BOOL noBackBufferBackup;
4957 BOOL src_offscreen;
4958 BOOL upsidedown = FALSE;
4959 RECT dst_rect = *dst_rect_in;
4961 TRACE("Using hwstretch blit\n");
4962 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4963 context = context_acquire(device, src_surface);
4964 gl_info = context->gl_info;
4965 context_apply_blit_state(context, device);
4966 surface_internal_preload(dst_surface, SRGB_RGB);
4968 src_offscreen = surface_is_offscreen(src_surface);
4969 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4970 if (!noBackBufferBackup && !src_surface->texture_name)
4972 /* Get it a description */
4973 surface_internal_preload(src_surface, SRGB_RGB);
4976 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4977 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4979 if (context->aux_buffers >= 2)
4981 /* Got more than one aux buffer? Use the 2nd aux buffer */
4982 drawBuffer = GL_AUX1;
4984 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4986 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4987 drawBuffer = GL_AUX0;
4990 if (noBackBufferBackup)
4992 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
4993 checkGLcall("glGenTextures");
4994 context_bind_texture(context, GL_TEXTURE_2D, backup);
4995 texture_target = GL_TEXTURE_2D;
4997 else
4999 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
5000 * we are reading from the back buffer, the backup can be used as source texture
5002 texture_target = src_surface->texture_target;
5003 context_bind_texture(context, texture_target, src_surface->texture_name);
5004 gl_info->gl_ops.gl.p_glEnable(texture_target);
5005 checkGLcall("glEnable(texture_target)");
5007 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
5008 src_surface->flags &= ~SFLAG_INTEXTURE;
5011 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
5012 * glCopyTexSubImage is a bit picky about the parameters we pass to it
5014 if(dst_rect.top > dst_rect.bottom) {
5015 UINT tmp = dst_rect.bottom;
5016 dst_rect.bottom = dst_rect.top;
5017 dst_rect.top = tmp;
5018 upsidedown = TRUE;
5021 if (src_offscreen)
5023 TRACE("Reading from an offscreen target\n");
5024 upsidedown = !upsidedown;
5025 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
5027 else
5029 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
5032 /* TODO: Only back up the part that will be overwritten */
5033 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
5035 checkGLcall("glCopyTexSubImage2D");
5037 /* No issue with overriding these - the sampler is dirty due to blit usage */
5038 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
5039 wined3d_gl_mag_filter(magLookup, filter));
5040 checkGLcall("glTexParameteri");
5041 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5042 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
5043 checkGLcall("glTexParameteri");
5045 if (!src_surface->swapchain || src_surface == src_surface->swapchain->back_buffers[0])
5047 src = backup ? backup : src_surface->texture_name;
5049 else
5051 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
5052 checkGLcall("glReadBuffer(GL_FRONT)");
5054 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
5055 checkGLcall("glGenTextures(1, &src)");
5056 context_bind_texture(context, GL_TEXTURE_2D, src);
5058 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5059 * out for power of 2 sizes
5061 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5062 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5063 checkGLcall("glTexImage2D");
5064 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
5066 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5067 checkGLcall("glTexParameteri");
5068 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5069 checkGLcall("glTexParameteri");
5071 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
5072 checkGLcall("glReadBuffer(GL_BACK)");
5074 if (texture_target != GL_TEXTURE_2D)
5076 gl_info->gl_ops.gl.p_glDisable(texture_target);
5077 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
5078 texture_target = GL_TEXTURE_2D;
5081 checkGLcall("glEnd and previous");
5083 left = src_rect->left;
5084 right = src_rect->right;
5086 if (!upsidedown)
5088 top = src_surface->resource.height - src_rect->top;
5089 bottom = src_surface->resource.height - src_rect->bottom;
5091 else
5093 top = src_surface->resource.height - src_rect->bottom;
5094 bottom = src_surface->resource.height - src_rect->top;
5097 if (src_surface->flags & SFLAG_NORMCOORD)
5099 left /= src_surface->pow2Width;
5100 right /= src_surface->pow2Width;
5101 top /= src_surface->pow2Height;
5102 bottom /= src_surface->pow2Height;
5105 /* draw the source texture stretched and upside down. The correct surface is bound already */
5106 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5107 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5109 context_set_draw_buffer(context, drawBuffer);
5110 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
5112 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
5113 /* bottom left */
5114 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
5115 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
5117 /* top left */
5118 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
5119 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
5121 /* top right */
5122 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
5123 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5125 /* bottom right */
5126 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
5127 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
5128 gl_info->gl_ops.gl.p_glEnd();
5129 checkGLcall("glEnd and previous");
5131 if (texture_target != dst_surface->texture_target)
5133 gl_info->gl_ops.gl.p_glDisable(texture_target);
5134 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
5135 texture_target = dst_surface->texture_target;
5138 /* Now read the stretched and upside down image into the destination texture */
5139 context_bind_texture(context, texture_target, dst_surface->texture_name);
5140 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
5142 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5143 0, 0, /* We blitted the image to the origin */
5144 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5145 checkGLcall("glCopyTexSubImage2D");
5147 if (drawBuffer == GL_BACK)
5149 /* Write the back buffer backup back. */
5150 if (backup)
5152 if (texture_target != GL_TEXTURE_2D)
5154 gl_info->gl_ops.gl.p_glDisable(texture_target);
5155 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
5156 texture_target = GL_TEXTURE_2D;
5158 context_bind_texture(context, GL_TEXTURE_2D, backup);
5160 else
5162 if (texture_target != src_surface->texture_target)
5164 gl_info->gl_ops.gl.p_glDisable(texture_target);
5165 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
5166 texture_target = src_surface->texture_target;
5168 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5171 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
5172 /* top left */
5173 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
5174 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
5176 /* bottom left */
5177 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5178 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
5180 /* bottom right */
5181 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5182 (float)fbheight / (float)src_surface->pow2Height);
5183 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
5185 /* top right */
5186 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5187 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
5188 gl_info->gl_ops.gl.p_glEnd();
5190 gl_info->gl_ops.gl.p_glDisable(texture_target);
5191 checkGLcall("glDisable(texture_target)");
5193 /* Cleanup */
5194 if (src != src_surface->texture_name && src != backup)
5196 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
5197 checkGLcall("glDeleteTextures(1, &src)");
5199 if (backup)
5201 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
5202 checkGLcall("glDeleteTextures(1, &backup)");
5205 if (wined3d_settings.strict_draw_ordering)
5206 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
5208 context_release(context);
5210 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5211 * path is never entered
5213 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5216 /* Front buffer coordinates are always full screen coordinates, but our GL
5217 * drawable is limited to the window's client area. The sysmem and texture
5218 * copies do have the full screen size. Note that GL has a bottom-left
5219 * origin, while D3D has a top-left origin. */
5220 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5222 UINT drawable_height;
5224 if (surface->swapchain && surface == surface->swapchain->front_buffer)
5226 POINT offset = {0, 0};
5227 RECT windowsize;
5229 ScreenToClient(window, &offset);
5230 OffsetRect(rect, offset.x, offset.y);
5232 GetClientRect(window, &windowsize);
5233 drawable_height = windowsize.bottom - windowsize.top;
5235 else
5237 drawable_height = surface->resource.height;
5240 rect->top = drawable_height - rect->top;
5241 rect->bottom = drawable_height - rect->bottom;
5244 static void surface_blt_to_drawable(const struct wined3d_device *device,
5245 enum wined3d_texture_filter_type filter, BOOL color_key,
5246 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5247 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5249 const struct wined3d_gl_info *gl_info;
5250 struct wined3d_context *context;
5251 RECT src_rect, dst_rect;
5253 src_rect = *src_rect_in;
5254 dst_rect = *dst_rect_in;
5256 /* Make sure the surface is up-to-date. This should probably use
5257 * surface_load_location() and worry about the destination surface too,
5258 * unless we're overwriting it completely. */
5259 surface_internal_preload(src_surface, SRGB_RGB);
5261 /* Activate the destination context, set it up for blitting */
5262 context = context_acquire(device, dst_surface);
5263 gl_info = context->gl_info;
5264 context_apply_blit_state(context, device);
5266 if (!surface_is_offscreen(dst_surface))
5267 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5269 device->blitter->set_shader(device->blit_priv, context, src_surface);
5271 if (color_key)
5273 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
5274 checkGLcall("glEnable(GL_ALPHA_TEST)");
5276 /* When the primary render target uses P8, the alpha component
5277 * contains the palette index. Which means that the colorkey is one of
5278 * the palette entries. In other cases pixels that should be masked
5279 * away have alpha set to 0. */
5280 if (primary_render_target_is_p8(device))
5281 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
5282 (float)src_surface->src_blt_color_key.color_space_low_value / 256.0f);
5283 else
5284 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
5285 checkGLcall("glAlphaFunc");
5287 else
5289 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
5290 checkGLcall("glDisable(GL_ALPHA_TEST)");
5293 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5295 if (color_key)
5297 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
5298 checkGLcall("glDisable(GL_ALPHA_TEST)");
5301 /* Leave the opengl state valid for blitting */
5302 device->blitter->unset_shader(context->gl_info);
5304 if (wined3d_settings.strict_draw_ordering
5305 || (dst_surface->swapchain && dst_surface->swapchain->front_buffer == dst_surface))
5306 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
5308 context_release(context);
5311 /* Do not call while under the GL lock. */
5312 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
5314 struct wined3d_device *device = s->resource.device;
5315 const struct blit_shader *blitter;
5317 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5318 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5319 if (!blitter)
5321 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5322 return WINED3DERR_INVALIDCALL;
5325 return blitter->color_fill(device, s, rect, color);
5328 /* Do not call while under the GL lock. */
5329 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5330 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5331 enum wined3d_texture_filter_type filter)
5333 struct wined3d_device *device = dst_surface->resource.device;
5334 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5335 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5337 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5338 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5339 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
5341 /* Get the swapchain. One of the surfaces has to be a primary surface */
5342 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
5344 WARN("Destination is in sysmem, rejecting gl blt\n");
5345 return WINED3DERR_INVALIDCALL;
5348 dst_swapchain = dst_surface->swapchain;
5350 if (src_surface)
5352 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
5354 WARN("Src is in sysmem, rejecting gl blt\n");
5355 return WINED3DERR_INVALIDCALL;
5358 src_swapchain = src_surface->swapchain;
5360 else
5362 src_swapchain = NULL;
5365 /* Early sort out of cases where no render target is used */
5366 if (!dst_swapchain && !src_swapchain
5367 && src_surface != device->fb.render_targets[0]
5368 && dst_surface != device->fb.render_targets[0])
5370 TRACE("No surface is render target, not using hardware blit.\n");
5371 return WINED3DERR_INVALIDCALL;
5374 /* No destination color keying supported */
5375 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5377 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5378 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5379 return WINED3DERR_INVALIDCALL;
5382 if (dst_swapchain && dst_swapchain == src_swapchain)
5384 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5385 return WINED3DERR_INVALIDCALL;
5388 if (dst_swapchain && src_swapchain)
5390 FIXME("Implement hardware blit between two different swapchains\n");
5391 return WINED3DERR_INVALIDCALL;
5394 if (dst_swapchain)
5396 /* Handled with regular texture -> swapchain blit */
5397 if (src_surface == device->fb.render_targets[0])
5398 TRACE("Blit from active render target to a swapchain\n");
5400 else if (src_swapchain && dst_surface == device->fb.render_targets[0])
5402 FIXME("Implement blit from a swapchain to the active render target\n");
5403 return WINED3DERR_INVALIDCALL;
5406 if ((src_swapchain || src_surface == device->fb.render_targets[0]) && !dst_swapchain)
5408 /* Blit from render target to texture */
5409 BOOL stretchx;
5411 /* P8 read back is not implemented */
5412 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5413 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5415 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5416 return WINED3DERR_INVALIDCALL;
5419 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5421 TRACE("Color keying not supported by frame buffer to texture blit\n");
5422 return WINED3DERR_INVALIDCALL;
5423 /* Destination color key is checked above */
5426 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5427 stretchx = TRUE;
5428 else
5429 stretchx = FALSE;
5431 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5432 * flip the image nor scale it.
5434 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5435 * -> If the app wants a image width an unscaled width, copy it line per line
5436 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5437 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5438 * back buffer. This is slower than reading line per line, thus not used for flipping
5439 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5440 * pixel by pixel. */
5441 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5442 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5444 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
5445 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
5447 else
5449 TRACE("Using hardware stretching to flip / stretch the texture.\n");
5450 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
5453 if (!dst_surface->resource.map_count && !(dst_surface->flags & SFLAG_DONOTFREE))
5455 wined3d_resource_free_sysmem(dst_surface->resource.heap_memory);
5456 dst_surface->resource.allocatedMemory = NULL;
5457 dst_surface->resource.heap_memory = NULL;
5459 else
5461 dst_surface->flags &= ~SFLAG_INSYSMEM;
5464 return WINED3D_OK;
5466 else if (src_surface)
5468 /* Blit from offscreen surface to render target */
5469 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
5470 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5472 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5474 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5475 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5476 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5478 FIXME("Unsupported blit operation falling back to software\n");
5479 return WINED3DERR_INVALIDCALL;
5482 /* Color keying: Check if we have to do a color keyed blt,
5483 * and if not check if a color key is activated.
5485 * Just modify the color keying parameters in the surface and restore them afterwards
5486 * The surface keeps track of the color key last used to load the opengl surface.
5487 * PreLoad will catch the change to the flags and color key and reload if necessary.
5489 if (flags & WINEDDBLT_KEYSRC)
5491 /* Use color key from surface */
5493 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5495 /* Use color key from DDBltFx */
5496 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5497 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
5499 else
5501 /* Do not use color key */
5502 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5505 surface_blt_to_drawable(device, filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5506 src_surface, src_rect, dst_surface, dst_rect);
5508 /* Restore the color key parameters */
5509 src_surface->CKeyFlags = oldCKeyFlags;
5510 src_surface->src_blt_color_key = old_blt_key;
5512 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5514 return WINED3D_OK;
5517 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5518 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5519 return WINED3DERR_INVALIDCALL;
5522 /* Context activation is done by the caller. */
5523 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5524 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5526 struct wined3d_device *device = surface->resource.device;
5527 const struct wined3d_gl_info *gl_info = context->gl_info;
5528 GLint compare_mode = GL_NONE;
5529 struct blt_info info;
5530 GLint old_binding = 0;
5531 RECT rect;
5533 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5535 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
5536 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
5537 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
5538 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
5539 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
5540 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
5541 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
5542 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
5543 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5544 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
5545 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
5547 SetRect(&rect, 0, h, w, 0);
5548 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5549 context_active_texture(context, context->gl_info, 0);
5550 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
5551 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
5552 if (gl_info->supported[ARB_SHADOW])
5554 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5555 if (compare_mode != GL_NONE)
5556 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5559 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5560 gl_info, info.tex_type, &surface->ds_current_size);
5562 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
5563 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
5564 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
5565 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
5566 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
5567 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
5568 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
5569 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
5570 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
5571 gl_info->gl_ops.gl.p_glEnd();
5573 if (compare_mode != GL_NONE)
5574 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5575 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
5577 gl_info->gl_ops.gl.p_glPopAttrib();
5579 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5582 void surface_modify_ds_location(struct wined3d_surface *surface,
5583 DWORD location, UINT w, UINT h)
5585 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5587 if (location & ~(SFLAG_LOCATIONS | SFLAG_DISCARDED))
5588 FIXME("Invalid location (%#x) specified.\n", location);
5590 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5591 || (!(surface->flags & SFLAG_INTEXTURE) && (location & SFLAG_INTEXTURE)))
5593 if (surface->container)
5595 TRACE("Passing to container.\n");
5596 wined3d_texture_set_dirty(surface->container, TRUE);
5600 surface->ds_current_size.cx = w;
5601 surface->ds_current_size.cy = h;
5602 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_DISCARDED);
5603 surface->flags |= location;
5606 /* Context activation is done by the caller. */
5607 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5609 const struct wined3d_gl_info *gl_info = context->gl_info;
5610 struct wined3d_device *device = surface->resource.device;
5611 GLsizei w, h;
5613 TRACE("surface %p, new location %#x.\n", surface, location);
5615 /* TODO: Make this work for modes other than FBO */
5616 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5618 if (!(surface->flags & location))
5620 w = surface->ds_current_size.cx;
5621 h = surface->ds_current_size.cy;
5622 surface->ds_current_size.cx = 0;
5623 surface->ds_current_size.cy = 0;
5625 else
5627 w = surface->resource.width;
5628 h = surface->resource.height;
5631 if (surface->ds_current_size.cx == surface->resource.width
5632 && surface->ds_current_size.cy == surface->resource.height)
5634 TRACE("Location (%#x) is already up to date.\n", location);
5635 return;
5638 if (surface->current_renderbuffer)
5640 FIXME("Not supported with fixed up depth stencil.\n");
5641 return;
5644 if (surface->flags & SFLAG_DISCARDED)
5646 TRACE("Surface was discarded, no need copy data.\n");
5647 switch (location)
5649 case SFLAG_INTEXTURE:
5650 surface_prepare_texture(surface, context, FALSE);
5651 break;
5652 case SFLAG_INRB_MULTISAMPLE:
5653 surface_prepare_rb(surface, gl_info, TRUE);
5654 break;
5655 case SFLAG_INDRAWABLE:
5656 /* Nothing to do */
5657 break;
5658 default:
5659 FIXME("Unhandled location %#x\n", location);
5661 surface->flags &= ~SFLAG_DISCARDED;
5662 surface->flags |= location;
5663 surface->ds_current_size.cx = surface->resource.width;
5664 surface->ds_current_size.cy = surface->resource.height;
5665 return;
5668 if (!(surface->flags & SFLAG_LOCATIONS))
5670 FIXME("No up to date depth stencil location.\n");
5671 surface->flags |= location;
5672 surface->ds_current_size.cx = surface->resource.width;
5673 surface->ds_current_size.cy = surface->resource.height;
5674 return;
5677 if (location == SFLAG_INTEXTURE)
5679 GLint old_binding = 0;
5680 GLenum bind_target;
5682 /* The render target is allowed to be smaller than the depth/stencil
5683 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5684 * than the offscreen surface. Don't overwrite the offscreen surface
5685 * with undefined data. */
5686 w = min(w, context->swapchain->desc.backbuffer_width);
5687 h = min(h, context->swapchain->desc.backbuffer_height);
5689 TRACE("Copying onscreen depth buffer to depth texture.\n");
5691 if (!device->depth_blt_texture)
5692 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
5694 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5695 * directly on the FBO texture. That's because we need to flip. */
5696 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5697 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5698 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5700 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5701 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5703 else
5705 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5706 bind_target = GL_TEXTURE_2D;
5708 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
5709 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5710 * internal format, because the internal format might include stencil
5711 * data. In principle we should copy stencil data as well, but unless
5712 * the driver supports stencil export it's hard to do, and doesn't
5713 * seem to be needed in practice. If the hardware doesn't support
5714 * writing stencil data, the glCopyTexImage2D() call might trigger
5715 * software fallbacks. */
5716 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5717 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5718 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5719 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5720 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5721 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5722 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5723 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
5725 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5726 NULL, surface, SFLAG_INTEXTURE);
5727 context_set_draw_buffer(context, GL_NONE);
5729 /* Do the actual blit */
5730 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5731 checkGLcall("depth_blt");
5733 context_invalidate_state(context, STATE_FRAMEBUFFER);
5735 if (wined3d_settings.strict_draw_ordering)
5736 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
5738 else if (location == SFLAG_INDRAWABLE)
5740 TRACE("Copying depth texture to onscreen depth buffer.\n");
5742 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5743 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5744 surface_depth_blt(surface, context, surface->texture_name,
5745 0, surface->pow2Height - h, w, h, surface->texture_target);
5746 checkGLcall("depth_blt");
5748 context_invalidate_state(context, STATE_FRAMEBUFFER);
5750 if (wined3d_settings.strict_draw_ordering)
5751 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
5753 else
5755 ERR("Invalid location (%#x) specified.\n", location);
5758 surface->flags |= location;
5759 surface->ds_current_size.cx = surface->resource.width;
5760 surface->ds_current_size.cy = surface->resource.height;
5763 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5765 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5766 struct wined3d_surface *overlay;
5768 TRACE("surface %p, location %s, persistent %#x.\n",
5769 surface, debug_surflocation(location), persistent);
5771 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5772 && !(surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5773 && (location & SFLAG_INDRAWABLE))
5774 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5776 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5777 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5778 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5780 if (persistent)
5782 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5783 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5785 if (surface->container)
5787 TRACE("Passing to container.\n");
5788 wined3d_texture_set_dirty(surface->container, TRUE);
5791 surface->flags &= ~SFLAG_LOCATIONS;
5792 surface->flags |= location;
5794 /* Redraw emulated overlays, if any */
5795 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5797 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5799 surface_draw_overlay(overlay);
5803 else
5805 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5807 if (surface->container)
5809 TRACE("Passing to container\n");
5810 wined3d_texture_set_dirty(surface->container, TRUE);
5813 surface->flags &= ~location;
5816 if (!(surface->flags & SFLAG_LOCATIONS))
5818 ERR("Surface %p does not have any up to date location.\n", surface);
5822 static DWORD resource_access_from_location(DWORD location)
5824 switch (location)
5826 case SFLAG_INSYSMEM:
5827 return WINED3D_RESOURCE_ACCESS_CPU;
5829 case SFLAG_INDRAWABLE:
5830 case SFLAG_INSRGBTEX:
5831 case SFLAG_INTEXTURE:
5832 case SFLAG_INRB_MULTISAMPLE:
5833 case SFLAG_INRB_RESOLVED:
5834 return WINED3D_RESOURCE_ACCESS_GPU;
5836 default:
5837 FIXME("Unhandled location %#x.\n", location);
5838 return 0;
5842 static void surface_load_sysmem(struct wined3d_surface *surface,
5843 const struct wined3d_gl_info *gl_info, const RECT *rect)
5845 surface_prepare_system_memory(surface);
5847 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5848 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5850 /* Download the surface to system memory. */
5851 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5853 struct wined3d_device *device = surface->resource.device;
5854 struct wined3d_context *context;
5856 /* TODO: Use already acquired context when possible. */
5857 context = context_acquire(device, NULL);
5859 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5860 surface_download_data(surface, gl_info);
5862 context_release(context);
5864 return;
5867 if (surface->flags & SFLAG_INDRAWABLE)
5869 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5870 wined3d_surface_get_pitch(surface));
5871 return;
5874 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5875 surface, surface->flags & SFLAG_LOCATIONS);
5878 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5879 const struct wined3d_gl_info *gl_info, const RECT *rect)
5881 struct wined3d_device *device = surface->resource.device;
5882 enum wined3d_conversion_type convert;
5883 struct wined3d_format format;
5884 UINT byte_count;
5885 BYTE *mem;
5887 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5889 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5890 return WINED3DERR_INVALIDCALL;
5893 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5894 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5896 if (surface->flags & SFLAG_INTEXTURE)
5898 RECT r;
5900 surface_get_rect(surface, rect, &r);
5901 surface_blt_to_drawable(device, WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
5903 return WINED3D_OK;
5906 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5908 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5909 * path through sysmem. */
5910 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5913 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5915 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5916 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5917 * called. */
5918 if ((convert != WINED3D_CT_NONE) && (surface->flags & SFLAG_PBO))
5920 struct wined3d_context *context;
5922 TRACE("Removing the pbo attached to surface %p.\n", surface);
5924 /* TODO: Use already acquired context when possible. */
5925 context = context_acquire(device, NULL);
5927 surface_remove_pbo(surface, gl_info);
5929 context_release(context);
5932 if ((convert != WINED3D_CT_NONE) && surface->resource.allocatedMemory)
5934 UINT height = surface->resource.height;
5935 UINT width = surface->resource.width;
5936 UINT src_pitch, dst_pitch;
5938 byte_count = format.conv_byte_count;
5939 src_pitch = wined3d_surface_get_pitch(surface);
5941 /* Stick to the alignment for the converted surface too, makes it
5942 * easier to load the surface. */
5943 dst_pitch = width * byte_count;
5944 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5946 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5948 ERR("Out of memory (%u).\n", dst_pitch * height);
5949 return E_OUTOFMEMORY;
5952 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5953 src_pitch, width, height, dst_pitch, convert, surface);
5955 surface->flags |= SFLAG_CONVERTED;
5957 else
5959 surface->flags &= ~SFLAG_CONVERTED;
5960 mem = surface->resource.allocatedMemory;
5961 byte_count = format.byte_count;
5964 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5966 /* Don't delete PBO memory. */
5967 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5968 HeapFree(GetProcessHeap(), 0, mem);
5970 return WINED3D_OK;
5973 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5974 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5976 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5977 struct wined3d_device *device = surface->resource.device;
5978 enum wined3d_conversion_type convert;
5979 struct wined3d_context *context;
5980 UINT width, src_pitch, dst_pitch;
5981 struct wined3d_bo_address data;
5982 struct wined3d_format format;
5983 POINT dst_point = {0, 0};
5984 BYTE *mem;
5986 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
5987 && surface_is_offscreen(surface)
5988 && (surface->flags & SFLAG_INDRAWABLE))
5990 surface_load_fb_texture(surface, srgb);
5992 return WINED3D_OK;
5995 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
5996 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
5997 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5998 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5999 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6001 if (srgb)
6002 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INTEXTURE,
6003 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6004 else
6005 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INSRGBTEX,
6006 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6008 return WINED3D_OK;
6011 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
6012 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
6013 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6014 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6015 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6017 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
6018 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
6019 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6021 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
6022 &rect, surface, dst_location, &rect);
6024 return WINED3D_OK;
6027 /* Upload from system memory */
6029 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6030 TRUE /* We will use textures */, &format, &convert);
6032 if (srgb)
6034 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6036 /* Performance warning... */
6037 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6038 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6041 else
6043 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6045 /* Performance warning... */
6046 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6047 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6051 if (!(surface->flags & SFLAG_INSYSMEM))
6053 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6054 /* Lets hope we get it from somewhere... */
6055 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6058 /* TODO: Use already acquired context when possible. */
6059 context = context_acquire(device, NULL);
6061 surface_prepare_texture(surface, context, srgb);
6062 surface_bind_and_dirtify(surface, context, srgb);
6064 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6066 surface->flags |= SFLAG_GLCKEY;
6067 surface->gl_color_key = surface->src_blt_color_key;
6069 else surface->flags &= ~SFLAG_GLCKEY;
6071 width = surface->resource.width;
6072 src_pitch = wined3d_surface_get_pitch(surface);
6074 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6075 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6076 * called. */
6077 if ((convert != WINED3D_CT_NONE || format.convert) && (surface->flags & SFLAG_PBO))
6079 TRACE("Removing the pbo attached to surface %p.\n", surface);
6080 surface_remove_pbo(surface, gl_info);
6083 if (format.convert)
6085 /* This code is entered for texture formats which need a fixup. */
6086 UINT height = surface->resource.height;
6088 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6089 dst_pitch = width * format.conv_byte_count;
6090 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6092 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6094 ERR("Out of memory (%u).\n", dst_pitch * height);
6095 context_release(context);
6096 return E_OUTOFMEMORY;
6098 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6099 format.byte_count = format.conv_byte_count;
6100 src_pitch = dst_pitch;
6102 else if (convert != WINED3D_CT_NONE && surface->resource.allocatedMemory)
6104 /* This code is only entered for color keying fixups */
6105 UINT height = surface->resource.height;
6107 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6108 dst_pitch = width * format.conv_byte_count;
6109 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6111 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6113 ERR("Out of memory (%u).\n", dst_pitch * height);
6114 context_release(context);
6115 return E_OUTOFMEMORY;
6117 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6118 width, height, dst_pitch, convert, surface);
6119 format.byte_count = format.conv_byte_count;
6120 src_pitch = dst_pitch;
6122 else
6124 mem = surface->resource.allocatedMemory;
6127 data.buffer_object = surface->pbo;
6128 data.addr = mem;
6129 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
6131 context_release(context);
6133 /* Don't delete PBO memory. */
6134 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6135 HeapFree(GetProcessHeap(), 0, mem);
6137 return WINED3D_OK;
6140 static void surface_multisample_resolve(struct wined3d_surface *surface)
6142 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6144 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6145 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6147 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
6148 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6151 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6153 struct wined3d_device *device = surface->resource.device;
6154 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6155 HRESULT hr;
6157 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6159 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6161 if (location == SFLAG_INTEXTURE && surface->flags & SFLAG_INDRAWABLE)
6163 struct wined3d_context *context = context_acquire(device, NULL);
6164 surface_load_ds_location(surface, context, location);
6165 context_release(context);
6166 return WINED3D_OK;
6168 else if (location & surface->flags && surface->draw_binding != SFLAG_INDRAWABLE)
6170 /* Already up to date, nothing to do. */
6171 return WINED3D_OK;
6173 else
6175 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
6176 debug_surflocation(surface->flags & SFLAG_LOCATIONS), debug_surflocation(location));
6177 return WINED3DERR_INVALIDCALL;
6181 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6182 location = SFLAG_INTEXTURE;
6184 if (surface->flags & location)
6186 TRACE("Location already up to date.\n");
6188 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
6189 && surface_need_pbo(surface, gl_info))
6190 surface_load_pbo(surface, gl_info);
6192 return WINED3D_OK;
6195 if (WARN_ON(d3d_surface))
6197 DWORD required_access = resource_access_from_location(location);
6198 if ((surface->resource.access_flags & required_access) != required_access)
6199 WARN("Operation requires %#x access, but surface only has %#x.\n",
6200 required_access, surface->resource.access_flags);
6203 if (!(surface->flags & SFLAG_LOCATIONS))
6205 ERR("Surface %p does not have any up to date location.\n", surface);
6206 surface->flags |= SFLAG_LOST;
6207 return WINED3DERR_DEVICELOST;
6210 switch (location)
6212 case SFLAG_INSYSMEM:
6213 surface_load_sysmem(surface, gl_info, rect);
6214 break;
6216 case SFLAG_INDRAWABLE:
6217 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6218 return hr;
6219 break;
6221 case SFLAG_INRB_RESOLVED:
6222 surface_multisample_resolve(surface);
6223 break;
6225 case SFLAG_INTEXTURE:
6226 case SFLAG_INSRGBTEX:
6227 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6228 return hr;
6229 break;
6231 default:
6232 ERR("Don't know how to handle location %#x.\n", location);
6233 break;
6236 if (!rect)
6238 surface->flags |= location;
6240 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6241 surface_evict_sysmem(surface);
6244 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6245 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6247 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6250 return WINED3D_OK;
6253 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6255 struct wined3d_swapchain *swapchain;
6257 /* Not on a swapchain - must be offscreen */
6258 if (!(swapchain = surface->swapchain))
6259 return TRUE;
6261 /* The front buffer is always onscreen */
6262 if (surface == swapchain->front_buffer) return FALSE;
6264 /* If the swapchain is rendered to an FBO, the backbuffer is
6265 * offscreen, otherwise onscreen */
6266 return swapchain->render_to_fbo;
6269 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6270 /* Context activation is done by the caller. */
6271 static void ffp_blit_free(struct wined3d_device *device) { }
6273 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6274 /* Context activation is done by the caller. */
6275 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6277 BYTE table[256][4];
6278 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) != 0;
6279 GLenum target;
6281 if (surface->container)
6282 target = surface->container->target;
6283 else
6284 target = surface->texture_target;
6286 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6288 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6289 GL_EXTCALL(glColorTableEXT(target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6292 /* Context activation is done by the caller. */
6293 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6295 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6296 const struct wined3d_gl_info *gl_info = context->gl_info;
6297 GLenum target;
6299 if (surface->container)
6300 target = surface->container->target;
6301 else
6302 target = surface->texture_target;
6304 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6305 * else the surface is converted in software at upload time in LoadLocation.
6307 if (!(surface->flags & SFLAG_CONVERTED) && fixup == COMPLEX_FIXUP_P8
6308 && gl_info->supported[EXT_PALETTED_TEXTURE])
6309 ffp_blit_p8_upload_palette(surface, gl_info);
6311 gl_info->gl_ops.gl.p_glEnable(target);
6312 checkGLcall("glEnable(target)");
6314 return WINED3D_OK;
6317 /* Context activation is done by the caller. */
6318 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6320 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
6321 checkGLcall("glDisable(GL_TEXTURE_2D)");
6322 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6324 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6325 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6327 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6329 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
6330 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6334 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6335 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
6336 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
6338 enum complex_fixup src_fixup;
6340 switch (blit_op)
6342 case WINED3D_BLIT_OP_COLOR_BLIT:
6343 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
6344 return FALSE;
6346 src_fixup = get_complex_fixup(src_format->color_fixup);
6347 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6349 TRACE("Checking support for fixup:\n");
6350 dump_color_fixup_desc(src_format->color_fixup);
6353 if (!is_identity_fixup(dst_format->color_fixup))
6355 TRACE("Destination fixups are not supported\n");
6356 return FALSE;
6359 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6361 TRACE("P8 fixup supported\n");
6362 return TRUE;
6365 /* We only support identity conversions. */
6366 if (is_identity_fixup(src_format->color_fixup))
6368 TRACE("[OK]\n");
6369 return TRUE;
6372 TRACE("[FAILED]\n");
6373 return FALSE;
6375 case WINED3D_BLIT_OP_COLOR_FILL:
6376 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
6377 return FALSE;
6379 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6381 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6382 return FALSE;
6384 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6386 TRACE("Color fill not supported\n");
6387 return FALSE;
6390 /* FIXME: We should reject color fills on formats with fixups,
6391 * but this would break P8 color fills for example. */
6393 return TRUE;
6395 case WINED3D_BLIT_OP_DEPTH_FILL:
6396 return TRUE;
6398 default:
6399 TRACE("Unsupported blit_op=%d\n", blit_op);
6400 return FALSE;
6404 /* Do not call while under the GL lock. */
6405 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6406 const RECT *dst_rect, const struct wined3d_color *color)
6408 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6409 struct wined3d_fb_state fb = {&dst_surface, NULL};
6411 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6413 return WINED3D_OK;
6416 /* Do not call while under the GL lock. */
6417 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6418 struct wined3d_surface *surface, const RECT *rect, float depth)
6420 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6421 struct wined3d_fb_state fb = {NULL, surface};
6423 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6425 return WINED3D_OK;
6428 const struct blit_shader ffp_blit = {
6429 ffp_blit_alloc,
6430 ffp_blit_free,
6431 ffp_blit_set,
6432 ffp_blit_unset,
6433 ffp_blit_supported,
6434 ffp_blit_color_fill,
6435 ffp_blit_depth_fill,
6438 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6440 return WINED3D_OK;
6443 /* Context activation is done by the caller. */
6444 static void cpu_blit_free(struct wined3d_device *device)
6448 /* Context activation is done by the caller. */
6449 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6451 return WINED3D_OK;
6454 /* Context activation is done by the caller. */
6455 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6459 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6460 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
6461 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
6463 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6465 return TRUE;
6468 return FALSE;
6471 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
6472 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
6473 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
6475 UINT row_block_count;
6476 const BYTE *src_row;
6477 BYTE *dst_row;
6478 UINT x, y;
6480 src_row = src_data;
6481 dst_row = dst_data;
6483 row_block_count = (update_w + format->block_width - 1) / format->block_width;
6485 if (!flags)
6487 for (y = 0; y < update_h; y += format->block_height)
6489 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
6490 src_row += src_pitch;
6491 dst_row += dst_pitch;
6494 return WINED3D_OK;
6497 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
6499 src_row += (((update_h / format->block_height) - 1) * src_pitch);
6501 switch (format->id)
6503 case WINED3DFMT_DXT1:
6504 for (y = 0; y < update_h; y += format->block_height)
6506 struct block
6508 WORD color[2];
6509 BYTE control_row[4];
6512 const struct block *s = (const struct block *)src_row;
6513 struct block *d = (struct block *)dst_row;
6515 for (x = 0; x < row_block_count; ++x)
6517 d[x].color[0] = s[x].color[0];
6518 d[x].color[1] = s[x].color[1];
6519 d[x].control_row[0] = s[x].control_row[3];
6520 d[x].control_row[1] = s[x].control_row[2];
6521 d[x].control_row[2] = s[x].control_row[1];
6522 d[x].control_row[3] = s[x].control_row[0];
6524 src_row -= src_pitch;
6525 dst_row += dst_pitch;
6527 return WINED3D_OK;
6529 case WINED3DFMT_DXT3:
6530 for (y = 0; y < update_h; y += format->block_height)
6532 struct block
6534 WORD alpha_row[4];
6535 WORD color[2];
6536 BYTE control_row[4];
6539 const struct block *s = (const struct block *)src_row;
6540 struct block *d = (struct block *)dst_row;
6542 for (x = 0; x < row_block_count; ++x)
6544 d[x].alpha_row[0] = s[x].alpha_row[3];
6545 d[x].alpha_row[1] = s[x].alpha_row[2];
6546 d[x].alpha_row[2] = s[x].alpha_row[1];
6547 d[x].alpha_row[3] = s[x].alpha_row[0];
6548 d[x].color[0] = s[x].color[0];
6549 d[x].color[1] = s[x].color[1];
6550 d[x].control_row[0] = s[x].control_row[3];
6551 d[x].control_row[1] = s[x].control_row[2];
6552 d[x].control_row[2] = s[x].control_row[1];
6553 d[x].control_row[3] = s[x].control_row[0];
6555 src_row -= src_pitch;
6556 dst_row += dst_pitch;
6558 return WINED3D_OK;
6560 default:
6561 FIXME("Compressed flip not implemented for format %s.\n",
6562 debug_d3dformat(format->id));
6563 return E_NOTIMPL;
6567 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
6568 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
6570 return E_NOTIMPL;
6573 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6574 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6575 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
6577 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6578 const struct wined3d_format *src_format, *dst_format;
6579 struct wined3d_surface *orig_src = src_surface;
6580 struct wined3d_map_desc dst_map, src_map;
6581 const BYTE *sbase = NULL;
6582 HRESULT hr = WINED3D_OK;
6583 const BYTE *sbuf;
6584 BYTE *dbuf;
6585 int x, y;
6587 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6588 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6589 flags, fx, debug_d3dtexturefiltertype(filter));
6591 if (src_surface == dst_surface)
6593 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6594 src_map = dst_map;
6595 src_format = dst_surface->resource.format;
6596 dst_format = src_format;
6598 else
6600 dst_format = dst_surface->resource.format;
6601 if (src_surface)
6603 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6605 src_surface = surface_convert_format(src_surface, dst_format->id);
6606 if (!src_surface)
6608 /* The conv function writes a FIXME */
6609 WARN("Cannot convert source surface format to dest format.\n");
6610 goto release;
6613 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
6614 src_format = src_surface->resource.format;
6616 else
6618 src_format = dst_format;
6621 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
6624 bpp = dst_surface->resource.format->byte_count;
6625 srcheight = src_rect->bottom - src_rect->top;
6626 srcwidth = src_rect->right - src_rect->left;
6627 dstheight = dst_rect->bottom - dst_rect->top;
6628 dstwidth = dst_rect->right - dst_rect->left;
6629 width = (dst_rect->right - dst_rect->left) * bpp;
6631 if (src_surface)
6632 sbase = (BYTE *)src_map.data
6633 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
6634 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
6635 if (src_surface != dst_surface)
6636 dbuf = dst_map.data;
6637 else
6638 dbuf = (BYTE *)dst_map.data
6639 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
6640 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
6642 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
6644 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6646 if (src_surface == dst_surface)
6648 FIXME("Only plain blits supported on compressed surfaces.\n");
6649 hr = E_NOTIMPL;
6650 goto release;
6653 if (srcheight != dstheight || srcwidth != dstwidth)
6655 WARN("Stretching not supported on compressed surfaces.\n");
6656 hr = WINED3DERR_INVALIDCALL;
6657 goto release;
6660 if (!surface_check_block_align(src_surface, src_rect))
6662 WARN("Source rectangle not block-aligned.\n");
6663 hr = WINED3DERR_INVALIDCALL;
6664 goto release;
6667 if (!surface_check_block_align(dst_surface, dst_rect))
6669 WARN("Destination rectangle not block-aligned.\n");
6670 hr = WINED3DERR_INVALIDCALL;
6671 goto release;
6674 hr = surface_cpu_blt_compressed(sbase, dbuf,
6675 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
6676 src_format, flags, fx);
6677 goto release;
6680 /* First, all the 'source-less' blits */
6681 if (flags & WINEDDBLT_COLORFILL)
6683 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
6684 flags &= ~WINEDDBLT_COLORFILL;
6687 if (flags & WINEDDBLT_DEPTHFILL)
6689 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6691 if (flags & WINEDDBLT_ROP)
6693 /* Catch some degenerate cases here. */
6694 switch (fx->dwROP)
6696 case BLACKNESS:
6697 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
6698 break;
6699 case 0xaa0029: /* No-op */
6700 break;
6701 case WHITENESS:
6702 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
6703 break;
6704 case SRCCOPY: /* Well, we do that below? */
6705 break;
6706 default:
6707 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6708 goto error;
6710 flags &= ~WINEDDBLT_ROP;
6712 if (flags & WINEDDBLT_DDROPS)
6714 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6716 /* Now the 'with source' blits. */
6717 if (src_surface)
6719 int sx, xinc, sy, yinc;
6721 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6722 goto release;
6724 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
6725 && (srcwidth != dstwidth || srcheight != dstheight))
6727 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6728 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6731 xinc = (srcwidth << 16) / dstwidth;
6732 yinc = (srcheight << 16) / dstheight;
6734 if (!flags)
6736 /* No effects, we can cheat here. */
6737 if (dstwidth == srcwidth)
6739 if (dstheight == srcheight)
6741 /* No stretching in either direction. This needs to be as
6742 * fast as possible. */
6743 sbuf = sbase;
6745 /* Check for overlapping surfaces. */
6746 if (src_surface != dst_surface || dst_rect->top < src_rect->top
6747 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
6749 /* No overlap, or dst above src, so copy from top downwards. */
6750 for (y = 0; y < dstheight; ++y)
6752 memcpy(dbuf, sbuf, width);
6753 sbuf += src_map.row_pitch;
6754 dbuf += dst_map.row_pitch;
6757 else if (dst_rect->top > src_rect->top)
6759 /* Copy from bottom upwards. */
6760 sbuf += src_map.row_pitch * dstheight;
6761 dbuf += dst_map.row_pitch * dstheight;
6762 for (y = 0; y < dstheight; ++y)
6764 sbuf -= src_map.row_pitch;
6765 dbuf -= dst_map.row_pitch;
6766 memcpy(dbuf, sbuf, width);
6769 else
6771 /* Src and dst overlapping on the same line, use memmove. */
6772 for (y = 0; y < dstheight; ++y)
6774 memmove(dbuf, sbuf, width);
6775 sbuf += src_map.row_pitch;
6776 dbuf += dst_map.row_pitch;
6780 else
6782 /* Stretching in y direction only. */
6783 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6785 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6786 memcpy(dbuf, sbuf, width);
6787 dbuf += dst_map.row_pitch;
6791 else
6793 /* Stretching in X direction. */
6794 int last_sy = -1;
6795 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6797 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6799 if ((sy >> 16) == (last_sy >> 16))
6801 /* This source row is the same as last source row -
6802 * Copy the already stretched row. */
6803 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
6805 else
6807 #define STRETCH_ROW(type) \
6808 do { \
6809 const type *s = (const type *)sbuf; \
6810 type *d = (type *)dbuf; \
6811 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6812 d[x] = s[sx >> 16]; \
6813 } while(0)
6815 switch(bpp)
6817 case 1:
6818 STRETCH_ROW(BYTE);
6819 break;
6820 case 2:
6821 STRETCH_ROW(WORD);
6822 break;
6823 case 4:
6824 STRETCH_ROW(DWORD);
6825 break;
6826 case 3:
6828 const BYTE *s;
6829 BYTE *d = dbuf;
6830 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6832 DWORD pixel;
6834 s = sbuf + 3 * (sx >> 16);
6835 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6836 d[0] = (pixel ) & 0xff;
6837 d[1] = (pixel >> 8) & 0xff;
6838 d[2] = (pixel >> 16) & 0xff;
6839 d += 3;
6841 break;
6843 default:
6844 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6845 hr = WINED3DERR_NOTAVAILABLE;
6846 goto error;
6848 #undef STRETCH_ROW
6850 dbuf += dst_map.row_pitch;
6851 last_sy = sy;
6855 else
6857 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
6858 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
6859 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
6860 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6862 /* The color keying flags are checked for correctness in ddraw */
6863 if (flags & WINEDDBLT_KEYSRC)
6865 keylow = src_surface->src_blt_color_key.color_space_low_value;
6866 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
6868 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6870 keylow = fx->ddckSrcColorkey.color_space_low_value;
6871 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
6874 if (flags & WINEDDBLT_KEYDEST)
6876 /* Destination color keys are taken from the source surface! */
6877 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
6878 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
6880 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6882 destkeylow = fx->ddckDestColorkey.color_space_low_value;
6883 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
6886 if (bpp == 1)
6888 keymask = 0xff;
6890 else
6892 DWORD masks[3];
6893 get_color_masks(src_format, masks);
6894 keymask = masks[0]
6895 | masks[1]
6896 | masks[2];
6898 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6901 if (flags & WINEDDBLT_DDFX)
6903 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6904 LONG tmpxy;
6905 dTopLeft = dbuf;
6906 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6907 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
6908 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6910 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6912 /* I don't think we need to do anything about this flag */
6913 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6915 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6917 tmp = dTopRight;
6918 dTopRight = dTopLeft;
6919 dTopLeft = tmp;
6920 tmp = dBottomRight;
6921 dBottomRight = dBottomLeft;
6922 dBottomLeft = tmp;
6923 dstxinc = dstxinc * -1;
6925 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6927 tmp = dTopLeft;
6928 dTopLeft = dBottomLeft;
6929 dBottomLeft = tmp;
6930 tmp = dTopRight;
6931 dTopRight = dBottomRight;
6932 dBottomRight = tmp;
6933 dstyinc = dstyinc * -1;
6935 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6937 /* I don't think we need to do anything about this flag */
6938 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6940 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6942 tmp = dBottomRight;
6943 dBottomRight = dTopLeft;
6944 dTopLeft = tmp;
6945 tmp = dBottomLeft;
6946 dBottomLeft = dTopRight;
6947 dTopRight = tmp;
6948 dstxinc = dstxinc * -1;
6949 dstyinc = dstyinc * -1;
6951 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6953 tmp = dTopLeft;
6954 dTopLeft = dBottomLeft;
6955 dBottomLeft = dBottomRight;
6956 dBottomRight = dTopRight;
6957 dTopRight = tmp;
6958 tmpxy = dstxinc;
6959 dstxinc = dstyinc;
6960 dstyinc = tmpxy;
6961 dstxinc = dstxinc * -1;
6963 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6965 tmp = dTopLeft;
6966 dTopLeft = dTopRight;
6967 dTopRight = dBottomRight;
6968 dBottomRight = dBottomLeft;
6969 dBottomLeft = tmp;
6970 tmpxy = dstxinc;
6971 dstxinc = dstyinc;
6972 dstyinc = tmpxy;
6973 dstyinc = dstyinc * -1;
6975 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6977 /* I don't think we need to do anything about this flag */
6978 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6980 dbuf = dTopLeft;
6981 flags &= ~(WINEDDBLT_DDFX);
6984 #define COPY_COLORKEY_FX(type) \
6985 do { \
6986 const type *s; \
6987 type *d = (type *)dbuf, *dx, tmp; \
6988 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6990 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
6991 dx = d; \
6992 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6994 tmp = s[sx >> 16]; \
6995 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6996 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6998 dx[0] = tmp; \
7000 dx = (type *)(((BYTE *)dx) + dstxinc); \
7002 d = (type *)(((BYTE *)d) + dstyinc); \
7004 } while(0)
7006 switch (bpp)
7008 case 1:
7009 COPY_COLORKEY_FX(BYTE);
7010 break;
7011 case 2:
7012 COPY_COLORKEY_FX(WORD);
7013 break;
7014 case 4:
7015 COPY_COLORKEY_FX(DWORD);
7016 break;
7017 case 3:
7019 const BYTE *s;
7020 BYTE *d = dbuf, *dx;
7021 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
7023 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
7024 dx = d;
7025 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
7027 DWORD pixel, dpixel = 0;
7028 s = sbuf + 3 * (sx>>16);
7029 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
7030 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
7031 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
7032 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
7034 dx[0] = (pixel ) & 0xff;
7035 dx[1] = (pixel >> 8) & 0xff;
7036 dx[2] = (pixel >> 16) & 0xff;
7038 dx += dstxinc;
7040 d += dstyinc;
7042 break;
7044 default:
7045 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7046 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7047 hr = WINED3DERR_NOTAVAILABLE;
7048 goto error;
7049 #undef COPY_COLORKEY_FX
7054 error:
7055 if (flags && FIXME_ON(d3d_surface))
7057 FIXME("\tUnsupported flags: %#x.\n", flags);
7060 release:
7061 wined3d_surface_unmap(dst_surface);
7062 if (src_surface && src_surface != dst_surface)
7063 wined3d_surface_unmap(src_surface);
7064 /* Release the converted surface, if any. */
7065 if (src_surface && src_surface != orig_src)
7066 wined3d_surface_decref(src_surface);
7068 return hr;
7071 /* Do not call while under the GL lock. */
7072 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7073 const RECT *dst_rect, const struct wined3d_color *color)
7075 static const RECT src_rect;
7076 WINEDDBLTFX BltFx;
7078 memset(&BltFx, 0, sizeof(BltFx));
7079 BltFx.dwSize = sizeof(BltFx);
7080 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7081 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7082 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
7085 /* Do not call while under the GL lock. */
7086 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7087 struct wined3d_surface *surface, const RECT *rect, float depth)
7089 FIXME("Depth filling not implemented by cpu_blit.\n");
7090 return WINED3DERR_INVALIDCALL;
7093 const struct blit_shader cpu_blit = {
7094 cpu_blit_alloc,
7095 cpu_blit_free,
7096 cpu_blit_set,
7097 cpu_blit_unset,
7098 cpu_blit_supported,
7099 cpu_blit_color_fill,
7100 cpu_blit_depth_fill,
7103 static HRESULT surface_init(struct wined3d_surface *surface, UINT alignment, UINT width, UINT height,
7104 enum wined3d_multisample_type multisample_type, UINT multisample_quality,
7105 struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7106 enum wined3d_pool pool, DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops)
7108 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7109 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7110 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
7111 unsigned int resource_size;
7112 HRESULT hr;
7114 if (multisample_quality > 0)
7116 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7117 multisample_quality = 0;
7120 /* Quick lockable sanity check.
7121 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7122 * this function is too deep to need to care about things like this.
7123 * Levels need to be checked too, since they all affect what can be done. */
7124 switch (pool)
7126 case WINED3D_POOL_SCRATCH:
7127 if (!lockable)
7129 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7130 "which are mutually exclusive, setting lockable to TRUE.\n");
7131 lockable = TRUE;
7133 break;
7135 case WINED3D_POOL_SYSTEM_MEM:
7136 if (!lockable)
7137 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7138 break;
7140 case WINED3D_POOL_MANAGED:
7141 if (usage & WINED3DUSAGE_DYNAMIC)
7142 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7143 break;
7145 case WINED3D_POOL_DEFAULT:
7146 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7147 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7148 break;
7150 default:
7151 FIXME("Unknown pool %#x.\n", pool);
7152 break;
7155 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3D_POOL_DEFAULT)
7156 FIXME("Trying to create a render target that isn't in the default pool.\n");
7158 /* FIXME: Check that the format is supported by the device. */
7160 resource_size = wined3d_format_calculate_size(format, alignment, width, height, 1);
7161 if (!resource_size)
7162 return WINED3DERR_INVALIDCALL;
7164 if (device->wined3d->flags & WINED3D_NO3D)
7165 surface->surface_ops = &gdi_surface_ops;
7166 else
7167 surface->surface_ops = &surface_ops;
7169 hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
7170 multisample_type, multisample_quality, usage, pool, width, height, 1,
7171 resource_size, parent, parent_ops, &surface_resource_ops);
7172 if (FAILED(hr))
7174 WARN("Failed to initialize resource, returning %#x.\n", hr);
7175 return hr;
7178 /* "Standalone" surface. */
7179 surface_set_container(surface, NULL);
7181 list_init(&surface->overlays);
7183 /* Flags */
7184 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7185 if (flags & WINED3D_SURFACE_DISCARD)
7186 surface->flags |= SFLAG_DISCARD;
7187 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
7188 surface->flags |= SFLAG_PIN_SYSMEM;
7189 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7190 surface->flags |= SFLAG_LOCKABLE;
7191 /* I'm not sure if this qualifies as a hack or as an optimization. It
7192 * seems reasonable to assume that lockable render targets will get
7193 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7194 * creation. However, the other reason we want to do this is that several
7195 * ddraw applications access surface memory while the surface isn't
7196 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7197 * future locks prevents these from crashing. */
7198 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7199 surface->flags |= SFLAG_DYNLOCK;
7201 /* Mark the texture as dirty so that it gets loaded first time around. */
7202 surface_add_dirty_rect(surface, NULL);
7203 list_init(&surface->renderbuffers);
7205 TRACE("surface %p, memory %p, size %u\n",
7206 surface, surface->resource.allocatedMemory, surface->resource.size);
7208 /* Call the private setup routine */
7209 hr = surface->surface_ops->surface_private_setup(surface);
7210 if (FAILED(hr))
7212 ERR("Private setup failed, returning %#x\n", hr);
7213 surface_cleanup(surface);
7214 return hr;
7217 /* Similar to lockable rendertargets above, creating the DIB section
7218 * during surface initialization prevents the sysmem pointer from changing
7219 * after a wined3d_surface_getdc() call. */
7220 if ((usage & WINED3DUSAGE_OWNDC) && !surface->hDC
7221 && SUCCEEDED(surface_create_dib_section(surface)))
7223 wined3d_resource_free_sysmem(surface->resource.heap_memory);
7224 surface->resource.heap_memory = NULL;
7225 surface->resource.allocatedMemory = surface->dib.bitmap_data;
7228 return hr;
7231 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7232 enum wined3d_format_id format_id, DWORD usage, enum wined3d_pool pool,
7233 enum wined3d_multisample_type multisample_type, DWORD multisample_quality, DWORD flags,
7234 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7236 struct wined3d_surface *object;
7237 HRESULT hr;
7239 TRACE("device %p, width %u, height %u, format %s\n",
7240 device, width, height, debug_d3dformat(format_id));
7241 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7242 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7243 TRACE("flags %#x, parent %p, parent_ops %p.\n", flags, parent, parent_ops);
7245 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7246 if (!object)
7247 return WINED3DERR_OUTOFVIDEOMEMORY;
7249 if (FAILED(hr = surface_init(object, device->surface_alignment, width, height, multisample_type,
7250 multisample_quality, device, usage, format_id, pool, flags, parent, parent_ops)))
7252 WARN("Failed to initialize surface, returning %#x.\n", hr);
7253 HeapFree(GetProcessHeap(), 0, object);
7254 return hr;
7257 TRACE("Created surface %p.\n", object);
7258 *surface = object;
7260 return hr;