wined3d: Always store the palette index in the alpha component.
[wine/wine-gecko.git] / dlls / wined3d / surface.c
blob0e71de0e4e8139bd96a696da94fead725f1a0268
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-2014 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d);
37 #define MAXLOCKCOUNT 50 /* After this amount of locks do not free the sysmem copy. */
39 static const DWORD surface_simple_locations =
40 WINED3D_LOCATION_SYSMEM | WINED3D_LOCATION_USER_MEMORY
41 | WINED3D_LOCATION_DIB | WINED3D_LOCATION_BUFFER;
43 static void surface_cleanup(struct wined3d_surface *surface)
45 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->pbo || surface->rb_multisample
50 || surface->rb_resolved || !list_empty(&surface->renderbuffers))
52 struct wined3d_renderbuffer_entry *entry, *entry2;
53 const struct wined3d_gl_info *gl_info;
54 struct wined3d_context *context;
56 context = context_acquire(surface->resource.device, NULL);
57 gl_info = context->gl_info;
59 if (surface->pbo)
61 TRACE("Deleting PBO %u.\n", surface->pbo);
62 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
65 if (surface->rb_multisample)
67 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
68 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
71 if (surface->rb_resolved)
73 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
74 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
77 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
79 TRACE("Deleting renderbuffer %u.\n", entry->id);
80 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
81 HeapFree(GetProcessHeap(), 0, entry);
84 context_release(context);
87 if (surface->flags & SFLAG_DIBSECTION)
89 DeleteDC(surface->hDC);
90 DeleteObject(surface->dib.DIBsection);
91 surface->dib.bitmap_data = NULL;
94 if (surface->overlay_dest)
95 list_remove(&surface->overlay_entry);
97 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
99 list_remove(&overlay->overlay_entry);
100 overlay->overlay_dest = NULL;
103 resource_cleanup(&surface->resource);
106 void surface_update_draw_binding(struct wined3d_surface *surface)
108 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
109 surface->draw_binding = WINED3D_LOCATION_DRAWABLE;
110 else if (surface->resource.multisample_type)
111 surface->draw_binding = WINED3D_LOCATION_RB_MULTISAMPLE;
112 else
113 surface->draw_binding = WINED3D_LOCATION_TEXTURE_RGB;
116 void surface_set_swapchain(struct wined3d_surface *surface, struct wined3d_swapchain *swapchain)
118 TRACE("surface %p, swapchain %p.\n", surface, swapchain);
120 if (swapchain)
122 surface->get_drawable_size = get_drawable_size_swapchain;
124 else
126 switch (wined3d_settings.offscreen_rendering_mode)
128 case ORM_FBO:
129 surface->get_drawable_size = get_drawable_size_fbo;
130 break;
132 case ORM_BACKBUFFER:
133 surface->get_drawable_size = get_drawable_size_backbuffer;
134 break;
136 default:
137 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
138 return;
142 surface->swapchain = swapchain;
143 surface_update_draw_binding(surface);
146 void surface_set_container(struct wined3d_surface *surface, struct wined3d_texture *container)
148 TRACE("surface %p, container %p.\n", surface, container);
150 if (!surface->swapchain)
152 switch (wined3d_settings.offscreen_rendering_mode)
154 case ORM_FBO:
155 surface->get_drawable_size = get_drawable_size_fbo;
156 break;
158 case ORM_BACKBUFFER:
159 surface->get_drawable_size = get_drawable_size_backbuffer;
160 break;
162 default:
163 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
164 return;
168 surface->container = container;
169 surface_update_draw_binding(surface);
172 struct blt_info
174 GLenum binding;
175 GLenum bind_target;
176 enum tex_types tex_type;
177 GLfloat coords[4][3];
180 struct float_rect
182 float l;
183 float t;
184 float r;
185 float b;
188 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
190 f->l = ((r->left * 2.0f) / w) - 1.0f;
191 f->t = ((r->top * 2.0f) / h) - 1.0f;
192 f->r = ((r->right * 2.0f) / w) - 1.0f;
193 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
196 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
198 GLfloat (*coords)[3] = info->coords;
199 struct float_rect f;
201 switch (target)
203 default:
204 FIXME("Unsupported texture target %#x\n", target);
205 /* Fall back to GL_TEXTURE_2D */
206 case GL_TEXTURE_2D:
207 info->binding = GL_TEXTURE_BINDING_2D;
208 info->bind_target = GL_TEXTURE_2D;
209 info->tex_type = tex_2d;
210 coords[0][0] = (float)rect->left / w;
211 coords[0][1] = (float)rect->top / h;
212 coords[0][2] = 0.0f;
214 coords[1][0] = (float)rect->right / w;
215 coords[1][1] = (float)rect->top / h;
216 coords[1][2] = 0.0f;
218 coords[2][0] = (float)rect->left / w;
219 coords[2][1] = (float)rect->bottom / h;
220 coords[2][2] = 0.0f;
222 coords[3][0] = (float)rect->right / w;
223 coords[3][1] = (float)rect->bottom / h;
224 coords[3][2] = 0.0f;
225 break;
227 case GL_TEXTURE_RECTANGLE_ARB:
228 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
229 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
230 info->tex_type = tex_rect;
231 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
232 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
233 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
234 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
235 break;
237 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
238 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
239 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
240 info->tex_type = tex_cube;
241 cube_coords_float(rect, w, h, &f);
243 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
244 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
245 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
246 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
247 break;
249 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
250 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
251 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
252 info->tex_type = tex_cube;
253 cube_coords_float(rect, w, h, &f);
255 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
256 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
257 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
258 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
259 break;
261 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
262 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
263 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
264 info->tex_type = tex_cube;
265 cube_coords_float(rect, w, h, &f);
267 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
268 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
269 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
270 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
271 break;
273 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
274 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
275 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
276 info->tex_type = tex_cube;
277 cube_coords_float(rect, w, h, &f);
279 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
280 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
281 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
282 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
283 break;
285 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
286 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
287 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
288 info->tex_type = tex_cube;
289 cube_coords_float(rect, w, h, &f);
291 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
292 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
293 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
294 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
295 break;
297 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
298 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
299 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
300 info->tex_type = tex_cube;
301 cube_coords_float(rect, w, h, &f);
303 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
304 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
305 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
306 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
307 break;
311 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
313 if (rect_in)
314 *rect_out = *rect_in;
315 else
317 rect_out->left = 0;
318 rect_out->top = 0;
319 rect_out->right = surface->resource.width;
320 rect_out->bottom = surface->resource.height;
324 /* Context activation is done by the caller. */
325 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
326 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
328 const struct wined3d_gl_info *gl_info = context->gl_info;
329 struct wined3d_texture *texture = src_surface->container;
330 struct blt_info info;
332 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
334 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
335 checkGLcall("glEnable(bind_target)");
337 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
339 /* Filtering for StretchRect */
340 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
341 wined3d_gl_mag_filter(magLookup, filter));
342 checkGLcall("glTexParameteri");
343 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
344 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
345 checkGLcall("glTexParameteri");
346 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
347 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
348 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
349 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
350 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
351 checkGLcall("glTexEnvi");
353 /* Draw a quad */
354 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
355 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
356 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
358 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
359 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
361 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
362 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
364 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
365 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
366 gl_info->gl_ops.gl.p_glEnd();
368 /* Unbind the texture */
369 context_bind_texture(context, info.bind_target, 0);
371 /* We changed the filtering settings on the texture. Inform the
372 * container about this to get the filters reset properly next draw. */
373 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_POINT;
374 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
375 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_NONE;
376 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
379 /* Works correctly only for <= 4 bpp formats. */
380 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
382 masks[0] = ((1 << format->red_size) - 1) << format->red_offset;
383 masks[1] = ((1 << format->green_size) - 1) << format->green_offset;
384 masks[2] = ((1 << format->blue_size) - 1) << format->blue_offset;
387 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
389 const struct wined3d_format *format = surface->resource.format;
390 SYSTEM_INFO sysInfo;
391 BITMAPINFO *b_info;
392 int extraline = 0;
393 DWORD *masks;
395 TRACE("surface %p.\n", surface);
397 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
399 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
400 return WINED3DERR_INVALIDCALL;
403 switch (format->byte_count)
405 case 2:
406 case 4:
407 /* Allocate extra space to store the RGB bit masks. */
408 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
409 break;
411 case 3:
412 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
413 break;
415 default:
416 /* Allocate extra space for a palette. */
417 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
418 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
419 break;
422 if (!b_info)
423 return E_OUTOFMEMORY;
425 /* Some applications access the surface in via DWORDs, and do not take
426 * the necessary care at the end of the surface. So we need at least
427 * 4 extra bytes at the end of the surface. Check against the page size,
428 * if the last page used for the surface has at least 4 spare bytes we're
429 * safe, otherwise add an extra line to the DIB section. */
430 GetSystemInfo(&sysInfo);
431 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
433 extraline = 1;
434 TRACE("Adding an extra line to the DIB section.\n");
437 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
438 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
439 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
440 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
441 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
442 * wined3d_surface_get_pitch(surface);
443 b_info->bmiHeader.biPlanes = 1;
444 b_info->bmiHeader.biBitCount = format->byte_count * 8;
446 b_info->bmiHeader.biXPelsPerMeter = 0;
447 b_info->bmiHeader.biYPelsPerMeter = 0;
448 b_info->bmiHeader.biClrUsed = 0;
449 b_info->bmiHeader.biClrImportant = 0;
451 /* Get the bit masks */
452 masks = (DWORD *)b_info->bmiColors;
453 switch (surface->resource.format->id)
455 case WINED3DFMT_B8G8R8_UNORM:
456 b_info->bmiHeader.biCompression = BI_RGB;
457 break;
459 case WINED3DFMT_B5G5R5X1_UNORM:
460 case WINED3DFMT_B5G5R5A1_UNORM:
461 case WINED3DFMT_B4G4R4A4_UNORM:
462 case WINED3DFMT_B4G4R4X4_UNORM:
463 case WINED3DFMT_B2G3R3_UNORM:
464 case WINED3DFMT_B2G3R3A8_UNORM:
465 case WINED3DFMT_R10G10B10A2_UNORM:
466 case WINED3DFMT_R8G8B8A8_UNORM:
467 case WINED3DFMT_R8G8B8X8_UNORM:
468 case WINED3DFMT_B10G10R10A2_UNORM:
469 case WINED3DFMT_B5G6R5_UNORM:
470 case WINED3DFMT_R16G16B16A16_UNORM:
471 b_info->bmiHeader.biCompression = BI_BITFIELDS;
472 get_color_masks(format, masks);
473 break;
475 default:
476 /* Don't know palette */
477 b_info->bmiHeader.biCompression = BI_RGB;
478 break;
481 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
482 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
483 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
484 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
486 if (!surface->dib.DIBsection)
488 ERR("Failed to create DIB section.\n");
489 HeapFree(GetProcessHeap(), 0, b_info);
490 return HRESULT_FROM_WIN32(GetLastError());
493 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
494 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
496 HeapFree(GetProcessHeap(), 0, b_info);
498 /* Now allocate a DC. */
499 surface->hDC = CreateCompatibleDC(0);
500 SelectObject(surface->hDC, surface->dib.DIBsection);
501 TRACE("Using wined3d palette %p.\n", surface->palette);
503 surface->flags |= SFLAG_DIBSECTION;
505 return WINED3D_OK;
508 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
509 DWORD location)
511 if (location & WINED3D_LOCATION_BUFFER)
513 data->addr = NULL;
514 data->buffer_object = surface->pbo;
515 return;
517 if (location & WINED3D_LOCATION_USER_MEMORY)
519 data->addr = surface->user_memory;
520 data->buffer_object = 0;
521 return;
523 if (location & WINED3D_LOCATION_DIB)
525 data->addr = surface->dib.bitmap_data;
526 data->buffer_object = 0;
527 return;
529 if (location & WINED3D_LOCATION_SYSMEM)
531 data->addr = surface->resource.heap_memory;
532 data->buffer_object = 0;
533 return;
536 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
537 data->addr = NULL;
538 data->buffer_object = 0;
541 static void surface_prepare_buffer(struct wined3d_surface *surface)
543 struct wined3d_context *context;
544 GLenum error;
545 const struct wined3d_gl_info *gl_info;
547 if (surface->pbo)
548 return;
550 context = context_acquire(surface->resource.device, NULL);
551 gl_info = context->gl_info;
553 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
554 error = gl_info->gl_ops.gl.p_glGetError();
555 if (!surface->pbo || error != GL_NO_ERROR)
556 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
558 TRACE("Binding PBO %u.\n", surface->pbo);
560 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
561 checkGLcall("glBindBufferARB");
563 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
564 NULL, GL_STREAM_DRAW_ARB));
565 checkGLcall("glBufferDataARB");
567 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
568 checkGLcall("glBindBufferARB");
570 context_release(context);
573 static void surface_prepare_system_memory(struct wined3d_surface *surface)
575 TRACE("surface %p.\n", surface);
577 if (surface->resource.heap_memory)
578 return;
580 /* Whatever surface we have, make sure that there is memory allocated
581 * for the downloaded copy, or a PBO to map. */
582 if (!wined3d_resource_allocate_sysmem(&surface->resource))
583 ERR("Failed to allocate system memory.\n");
585 if (surface->locations & WINED3D_LOCATION_SYSMEM)
586 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
589 void surface_prepare_map_memory(struct wined3d_surface *surface)
591 switch (surface->map_binding)
593 case WINED3D_LOCATION_SYSMEM:
594 surface_prepare_system_memory(surface);
595 break;
597 case WINED3D_LOCATION_USER_MEMORY:
598 if (!surface->user_memory)
599 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
600 break;
602 case WINED3D_LOCATION_DIB:
603 if (!surface->dib.bitmap_data)
604 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
605 break;
607 case WINED3D_LOCATION_BUFFER:
608 surface_prepare_buffer(surface);
609 break;
611 default:
612 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->map_binding));
616 static void surface_evict_sysmem(struct wined3d_surface *surface)
618 if (surface->resource.map_count || surface->flags & SFLAG_DONOTFREE)
619 return;
621 wined3d_resource_free_sysmem(&surface->resource);
622 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
625 static void surface_force_reload(struct wined3d_surface *surface)
627 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
630 static void surface_release_client_storage(struct wined3d_surface *surface)
632 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
633 const struct wined3d_gl_info *gl_info = context->gl_info;
635 if (surface->container->texture_rgb.name)
637 wined3d_texture_bind_and_dirtify(surface->container, context, FALSE);
638 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
639 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
641 if (surface->container->texture_srgb.name)
643 wined3d_texture_bind_and_dirtify(surface->container, context, TRUE);
644 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
645 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
648 context_release(context);
650 surface_invalidate_location(surface, WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
651 surface_force_reload(surface);
654 static BOOL surface_use_pbo(const struct wined3d_surface *surface)
656 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
658 return surface->resource.pool == WINED3D_POOL_DEFAULT
659 && surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU
660 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
661 && !surface->resource.format->convert
662 && !(surface->flags & (SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM));
665 static HRESULT surface_private_setup(struct wined3d_surface *surface)
667 /* TODO: Check against the maximum texture sizes supported by the video card. */
668 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
669 unsigned int pow2Width, pow2Height;
671 TRACE("surface %p.\n", surface);
673 /* Non-power2 support */
674 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
675 || gl_info->supported[ARB_TEXTURE_RECTANGLE])
677 pow2Width = surface->resource.width;
678 pow2Height = surface->resource.height;
680 else
682 /* Find the nearest pow2 match */
683 pow2Width = pow2Height = 1;
684 while (pow2Width < surface->resource.width)
685 pow2Width <<= 1;
686 while (pow2Height < surface->resource.height)
687 pow2Height <<= 1;
689 surface->pow2Width = pow2Width;
690 surface->pow2Height = pow2Height;
692 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
694 /* TODO: Add support for non power two compressed textures. */
695 if (surface->resource.format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE))
697 FIXME("(%p) Compressed or height scaled non-power-two textures are not supported w(%d) h(%d)\n",
698 surface, surface->resource.width, surface->resource.height);
699 return WINED3DERR_NOTAVAILABLE;
703 if (pow2Width != surface->resource.width
704 || pow2Height != surface->resource.height)
706 surface->flags |= SFLAG_NONPOW2;
709 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
710 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
712 /* One of three options:
713 * 1: Do the same as we do with NPOT and scale the texture, (any
714 * texture ops would require the texture to be scaled which is
715 * potentially slow)
716 * 2: Set the texture to the maximum size (bad idea).
717 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
718 * 4: Create the surface, but allow it to be used only for DirectDraw
719 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
720 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
721 * the render target. */
722 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
724 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
725 return WINED3DERR_NOTAVAILABLE;
728 /* We should never use this surface in combination with OpenGL! */
729 TRACE("Creating an oversized surface: %ux%u.\n",
730 surface->pow2Width, surface->pow2Height);
733 switch (wined3d_settings.offscreen_rendering_mode)
735 case ORM_FBO:
736 surface->get_drawable_size = get_drawable_size_fbo;
737 break;
739 case ORM_BACKBUFFER:
740 surface->get_drawable_size = get_drawable_size_backbuffer;
741 break;
743 default:
744 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
745 return WINED3DERR_INVALIDCALL;
748 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
749 surface->locations = WINED3D_LOCATION_DISCARDED;
751 if (surface_use_pbo(surface))
752 surface->map_binding = WINED3D_LOCATION_BUFFER;
754 return WINED3D_OK;
757 static void surface_realize_palette(struct wined3d_surface *surface)
759 struct wined3d_palette *palette = surface->palette;
761 TRACE("surface %p.\n", surface);
763 if (!palette) return;
765 if (surface->resource.format->id == WINED3DFMT_P8_UINT
766 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
768 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
770 /* Make sure the texture is up to date. This call doesn't do
771 * anything if the texture is already up to date. */
772 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
774 /* We want to force a palette refresh, so mark the drawable as not being up to date */
775 if (!surface_is_offscreen(surface))
776 surface_invalidate_location(surface, WINED3D_LOCATION_DRAWABLE);
778 else
780 if (!(surface->locations & surface->map_binding))
782 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
783 surface_prepare_map_memory(surface);
784 surface_load_location(surface, surface->map_binding);
786 surface_invalidate_location(surface, ~surface->map_binding);
790 if (surface->flags & SFLAG_DIBSECTION)
792 TRACE("Updating the DC's palette.\n");
793 SetDIBColorTable(surface->hDC, 0, 256, palette->colors);
796 /* Propagate the changes to the drawable when we have a palette. */
797 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
798 surface_load_location(surface, surface->draw_binding);
801 static void surface_unmap(struct wined3d_surface *surface)
803 struct wined3d_device *device = surface->resource.device;
804 const struct wined3d_gl_info *gl_info;
805 struct wined3d_context *context;
807 TRACE("surface %p.\n", surface);
809 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
811 switch (surface->map_binding)
813 case WINED3D_LOCATION_SYSMEM:
814 case WINED3D_LOCATION_USER_MEMORY:
815 case WINED3D_LOCATION_DIB:
816 break;
818 case WINED3D_LOCATION_BUFFER:
819 context = context_acquire(device, NULL);
820 gl_info = context->gl_info;
822 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
823 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
824 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
825 checkGLcall("glUnmapBufferARB");
826 context_release(context);
827 break;
829 default:
830 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->map_binding));
833 if (surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_TEXTURE_RGB))
835 TRACE("Not dirtified, nothing to do.\n");
836 return;
839 if (surface->swapchain && surface->swapchain->front_buffer == surface)
840 surface_load_location(surface, surface->draw_binding);
841 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
842 FIXME("Depth / stencil buffer locking is not implemented.\n");
845 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
847 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
848 return FALSE;
849 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
850 return FALSE;
851 return TRUE;
854 static void surface_depth_blt_fbo(const struct wined3d_device *device,
855 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
856 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
858 const struct wined3d_gl_info *gl_info;
859 struct wined3d_context *context;
860 DWORD src_mask, dst_mask;
861 GLbitfield gl_mask;
863 TRACE("device %p\n", device);
864 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
865 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
866 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
867 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
869 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
870 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
872 if (src_mask != dst_mask)
874 ERR("Incompatible formats %s and %s.\n",
875 debug_d3dformat(src_surface->resource.format->id),
876 debug_d3dformat(dst_surface->resource.format->id));
877 return;
880 if (!src_mask)
882 ERR("Not a depth / stencil format: %s.\n",
883 debug_d3dformat(src_surface->resource.format->id));
884 return;
887 gl_mask = 0;
888 if (src_mask & WINED3DFMT_FLAG_DEPTH)
889 gl_mask |= GL_DEPTH_BUFFER_BIT;
890 if (src_mask & WINED3DFMT_FLAG_STENCIL)
891 gl_mask |= GL_STENCIL_BUFFER_BIT;
893 /* Make sure the locations are up-to-date. Loading the destination
894 * surface isn't required if the entire surface is overwritten. */
895 surface_load_location(src_surface, src_location);
896 if (!surface_is_full_rect(dst_surface, dst_rect))
897 surface_load_location(dst_surface, dst_location);
899 context = context_acquire(device, NULL);
900 if (!context->valid)
902 context_release(context);
903 WARN("Invalid context, skipping blit.\n");
904 return;
907 gl_info = context->gl_info;
909 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
910 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
912 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
913 context_set_draw_buffer(context, GL_NONE);
914 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
915 context_invalidate_state(context, STATE_FRAMEBUFFER);
917 if (gl_mask & GL_DEPTH_BUFFER_BIT)
919 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
920 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
922 if (gl_mask & GL_STENCIL_BUFFER_BIT)
924 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
926 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
927 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
929 gl_info->gl_ops.gl.p_glStencilMask(~0U);
930 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
933 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
934 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
936 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
937 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
938 checkGLcall("glBlitFramebuffer()");
940 if (wined3d_settings.strict_draw_ordering)
941 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
943 context_release(context);
946 /* Blit between surface locations. Onscreen on different swapchains is not supported.
947 * Depth / stencil is not supported. */
948 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
949 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
950 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
952 const struct wined3d_gl_info *gl_info;
953 struct wined3d_context *context;
954 RECT src_rect, dst_rect;
955 GLenum gl_filter;
956 GLenum buffer;
958 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
959 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
960 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
961 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
962 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
964 src_rect = *src_rect_in;
965 dst_rect = *dst_rect_in;
967 switch (filter)
969 case WINED3D_TEXF_LINEAR:
970 gl_filter = GL_LINEAR;
971 break;
973 default:
974 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
975 case WINED3D_TEXF_NONE:
976 case WINED3D_TEXF_POINT:
977 gl_filter = GL_NEAREST;
978 break;
981 /* Resolve the source surface first if needed. */
982 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
983 && (src_surface->resource.format->id != dst_surface->resource.format->id
984 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
985 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
986 src_location = WINED3D_LOCATION_RB_RESOLVED;
988 /* Make sure the locations are up-to-date. Loading the destination
989 * surface isn't required if the entire surface is overwritten. (And is
990 * in fact harmful if we're being called by surface_load_location() with
991 * the purpose of loading the destination surface.) */
992 surface_load_location(src_surface, src_location);
993 if (!surface_is_full_rect(dst_surface, &dst_rect))
994 surface_load_location(dst_surface, dst_location);
996 if (src_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, src_surface);
997 else if (dst_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, dst_surface);
998 else context = context_acquire(device, NULL);
1000 if (!context->valid)
1002 context_release(context);
1003 WARN("Invalid context, skipping blit.\n");
1004 return;
1007 gl_info = context->gl_info;
1009 if (src_location == WINED3D_LOCATION_DRAWABLE)
1011 TRACE("Source surface %p is onscreen.\n", src_surface);
1012 buffer = surface_get_gl_buffer(src_surface);
1013 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1015 else
1017 TRACE("Source surface %p is offscreen.\n", src_surface);
1018 buffer = GL_COLOR_ATTACHMENT0;
1021 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1022 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1023 checkGLcall("glReadBuffer()");
1024 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1026 if (dst_location == WINED3D_LOCATION_DRAWABLE)
1028 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1029 buffer = surface_get_gl_buffer(dst_surface);
1030 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1032 else
1034 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1035 buffer = GL_COLOR_ATTACHMENT0;
1038 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1039 context_set_draw_buffer(context, buffer);
1040 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1041 context_invalidate_state(context, STATE_FRAMEBUFFER);
1043 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1044 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
1045 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
1046 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
1047 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
1049 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
1050 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1052 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1053 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1054 checkGLcall("glBlitFramebuffer()");
1056 if (wined3d_settings.strict_draw_ordering
1057 || (dst_location == WINED3D_LOCATION_DRAWABLE
1058 && dst_surface->swapchain->front_buffer == dst_surface))
1059 gl_info->gl_ops.gl.p_glFlush();
1061 context_release(context);
1064 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1065 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
1066 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
1068 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1069 return FALSE;
1071 /* Source and/or destination need to be on the GL side */
1072 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
1073 return FALSE;
1075 switch (blit_op)
1077 case WINED3D_BLIT_OP_COLOR_BLIT:
1078 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1079 return FALSE;
1080 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1081 return FALSE;
1082 break;
1084 case WINED3D_BLIT_OP_DEPTH_BLIT:
1085 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1086 return FALSE;
1087 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1088 return FALSE;
1089 break;
1091 default:
1092 return FALSE;
1095 if (!(src_format->id == dst_format->id
1096 || (is_identity_fixup(src_format->color_fixup)
1097 && is_identity_fixup(dst_format->color_fixup))))
1098 return FALSE;
1100 return TRUE;
1103 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1104 DWORD color, struct wined3d_color *float_color)
1106 const struct wined3d_format *format = surface->resource.format;
1108 switch (format->id)
1110 case WINED3DFMT_P8_UINT:
1111 if (surface->palette)
1113 float_color->r = surface->palette->colors[color].rgbRed / 255.0f;
1114 float_color->g = surface->palette->colors[color].rgbGreen / 255.0f;
1115 float_color->b = surface->palette->colors[color].rgbBlue / 255.0f;
1117 else
1119 float_color->r = 0.0f;
1120 float_color->g = 0.0f;
1121 float_color->b = 0.0f;
1123 float_color->a = color / 255.0f;
1124 break;
1126 case WINED3DFMT_B5G6R5_UNORM:
1127 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1128 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1129 float_color->b = (color & 0x1f) / 31.0f;
1130 float_color->a = 1.0f;
1131 break;
1133 case WINED3DFMT_B8G8R8_UNORM:
1134 case WINED3DFMT_B8G8R8X8_UNORM:
1135 float_color->r = D3DCOLOR_R(color);
1136 float_color->g = D3DCOLOR_G(color);
1137 float_color->b = D3DCOLOR_B(color);
1138 float_color->a = 1.0f;
1139 break;
1141 case WINED3DFMT_B8G8R8A8_UNORM:
1142 float_color->r = D3DCOLOR_R(color);
1143 float_color->g = D3DCOLOR_G(color);
1144 float_color->b = D3DCOLOR_B(color);
1145 float_color->a = D3DCOLOR_A(color);
1146 break;
1148 default:
1149 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1150 return FALSE;
1153 return TRUE;
1156 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1158 const struct wined3d_format *format = surface->resource.format;
1160 switch (format->id)
1162 case WINED3DFMT_S1_UINT_D15_UNORM:
1163 *float_depth = depth / (float)0x00007fff;
1164 break;
1166 case WINED3DFMT_D16_UNORM:
1167 *float_depth = depth / (float)0x0000ffff;
1168 break;
1170 case WINED3DFMT_D24_UNORM_S8_UINT:
1171 case WINED3DFMT_X8D24_UNORM:
1172 *float_depth = depth / (float)0x00ffffff;
1173 break;
1175 case WINED3DFMT_D32_UNORM:
1176 *float_depth = depth / (float)0xffffffff;
1177 break;
1179 default:
1180 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1181 return FALSE;
1184 return TRUE;
1187 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1189 const struct wined3d_resource *resource = &surface->resource;
1190 struct wined3d_device *device = resource->device;
1191 const struct blit_shader *blitter;
1193 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1194 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1195 if (!blitter)
1197 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1198 return WINED3DERR_INVALIDCALL;
1201 return blitter->depth_fill(device, surface, rect, depth);
1204 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1205 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1207 struct wined3d_device *device = src_surface->resource.device;
1209 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1210 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1211 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1212 return WINED3DERR_INVALIDCALL;
1214 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1216 surface_modify_ds_location(dst_surface, dst_location,
1217 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1219 return WINED3D_OK;
1222 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1223 struct wined3d_surface *render_target)
1225 TRACE("surface %p, render_target %p.\n", surface, render_target);
1227 /* TODO: Check surface sizes, pools, etc. */
1229 if (render_target->resource.multisample_type)
1230 return WINED3DERR_INVALIDCALL;
1232 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1235 /* Context activation is done by the caller. */
1236 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1238 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1239 checkGLcall("glDeleteBuffersARB(1, &surface->pbo)");
1241 surface->pbo = 0;
1242 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1245 static void surface_unload(struct wined3d_resource *resource)
1247 struct wined3d_surface *surface = surface_from_resource(resource);
1248 struct wined3d_renderbuffer_entry *entry, *entry2;
1249 struct wined3d_device *device = resource->device;
1250 const struct wined3d_gl_info *gl_info;
1251 struct wined3d_context *context;
1253 TRACE("surface %p.\n", surface);
1255 if (resource->pool == WINED3D_POOL_DEFAULT)
1257 /* Default pool resources are supposed to be destroyed before Reset is called.
1258 * Implicit resources stay however. So this means we have an implicit render target
1259 * or depth stencil. The content may be destroyed, but we still have to tear down
1260 * opengl resources, so we cannot leave early.
1262 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1263 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1264 * or the depth stencil into an FBO the texture or render buffer will be removed
1265 * and all flags get lost */
1266 surface_prepare_system_memory(surface);
1267 memset(surface->resource.heap_memory, 0, surface->resource.size);
1268 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1269 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1271 /* We also get here when the ddraw swapchain is destroyed, for example
1272 * for a mode switch. In this case this surface won't necessarily be
1273 * an implicit surface. We have to mark it lost so that the
1274 * application can restore it after the mode switch. */
1275 surface->flags |= SFLAG_LOST;
1277 else
1279 surface_prepare_map_memory(surface);
1280 surface_load_location(surface, surface->map_binding);
1281 surface_invalidate_location(surface, ~surface->map_binding);
1283 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1285 context = context_acquire(device, NULL);
1286 gl_info = context->gl_info;
1288 /* Destroy PBOs, but load them into real sysmem before */
1289 if (surface->pbo)
1290 surface_remove_pbo(surface, gl_info);
1292 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1293 * all application-created targets the application has to release the surface
1294 * before calling _Reset
1296 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1298 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1299 list_remove(&entry->entry);
1300 HeapFree(GetProcessHeap(), 0, entry);
1302 list_init(&surface->renderbuffers);
1303 surface->current_renderbuffer = NULL;
1305 if (surface->rb_multisample)
1307 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1308 surface->rb_multisample = 0;
1310 if (surface->rb_resolved)
1312 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1313 surface->rb_resolved = 0;
1316 context_release(context);
1318 resource_unload(resource);
1321 static const struct wined3d_resource_ops surface_resource_ops =
1323 surface_unload,
1326 static const struct wined3d_surface_ops surface_ops =
1328 surface_private_setup,
1329 surface_realize_palette,
1330 surface_unmap,
1333 /*****************************************************************************
1334 * Initializes the GDI surface, aka creates the DIB section we render to
1335 * The DIB section creation is done by calling GetDC, which will create the
1336 * section and releasing the dc to allow the app to use it. The dib section
1337 * will stay until the surface is released
1339 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1340 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1341 * avoid confusion in the shared surface code.
1343 * Returns:
1344 * WINED3D_OK on success
1345 * The return values of called methods on failure
1347 *****************************************************************************/
1348 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1350 HRESULT hr;
1352 TRACE("surface %p.\n", surface);
1354 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1356 ERR("Overlays not yet supported by GDI surfaces.\n");
1357 return WINED3DERR_INVALIDCALL;
1360 /* Sysmem textures have memory already allocated - release it,
1361 * this avoids an unnecessary memcpy. */
1362 hr = surface_create_dib_section(surface);
1363 if (FAILED(hr))
1364 return hr;
1365 surface->map_binding = WINED3D_LOCATION_DIB;
1367 /* We don't mind the nonpow2 stuff in GDI. */
1368 surface->pow2Width = surface->resource.width;
1369 surface->pow2Height = surface->resource.height;
1371 return WINED3D_OK;
1374 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1376 struct wined3d_palette *palette = surface->palette;
1378 TRACE("surface %p.\n", surface);
1380 if (!palette) return;
1382 if (surface->flags & SFLAG_DIBSECTION)
1384 TRACE("Updating the DC's palette.\n");
1385 SetDIBColorTable(surface->hDC, 0, 256, palette->colors);
1388 /* Update the image because of the palette change. Some games like e.g.
1389 * Red Alert call SetEntries a lot to implement fading. */
1390 /* Tell the swapchain to update the screen. */
1391 if (surface->swapchain && surface == surface->swapchain->front_buffer)
1392 x11_copy_to_screen(surface->swapchain, NULL);
1395 static void gdi_surface_unmap(struct wined3d_surface *surface)
1397 TRACE("surface %p.\n", surface);
1399 /* Tell the swapchain to update the screen. */
1400 if (surface->swapchain && surface == surface->swapchain->front_buffer)
1401 x11_copy_to_screen(surface->swapchain, &surface->lockedRect);
1403 memset(&surface->lockedRect, 0, sizeof(RECT));
1406 static const struct wined3d_surface_ops gdi_surface_ops =
1408 gdi_surface_private_setup,
1409 gdi_surface_realize_palette,
1410 gdi_surface_unmap,
1413 /* This call just downloads data, the caller is responsible for binding the
1414 * correct texture. */
1415 /* Context activation is done by the caller. */
1416 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1417 DWORD dst_location)
1419 const struct wined3d_format *format = surface->resource.format;
1420 struct wined3d_bo_address data;
1422 /* Only support read back of converted P8 surfaces. */
1423 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1425 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1426 return;
1429 surface_get_memory(surface, &data, dst_location);
1431 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1433 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
1434 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1436 if (data.buffer_object)
1438 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1439 checkGLcall("glBindBufferARB");
1440 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
1441 checkGLcall("glGetCompressedTexImageARB");
1442 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1443 checkGLcall("glBindBufferARB");
1445 else
1447 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
1448 surface->texture_level, data.addr));
1449 checkGLcall("glGetCompressedTexImageARB");
1452 else
1454 void *mem;
1455 GLenum gl_format = format->glFormat;
1456 GLenum gl_type = format->glType;
1457 int src_pitch = 0;
1458 int dst_pitch = 0;
1460 if (format->id == WINED3DFMT_P8_UINT)
1462 gl_format = GL_ALPHA;
1463 gl_type = GL_UNSIGNED_BYTE;
1466 if (surface->flags & SFLAG_NONPOW2)
1468 unsigned char alignment = surface->resource.device->surface_alignment;
1469 src_pitch = format->byte_count * surface->pow2Width;
1470 dst_pitch = wined3d_surface_get_pitch(surface);
1471 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1472 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1474 else
1476 mem = data.addr;
1479 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1480 surface, surface->texture_level, gl_format, gl_type, mem);
1482 if (data.buffer_object)
1484 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1485 checkGLcall("glBindBufferARB");
1487 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1488 gl_format, gl_type, NULL);
1489 checkGLcall("glGetTexImage");
1491 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1492 checkGLcall("glBindBufferARB");
1494 else
1496 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1497 gl_format, gl_type, mem);
1498 checkGLcall("glGetTexImage");
1501 if (surface->flags & SFLAG_NONPOW2)
1503 const BYTE *src_data;
1504 BYTE *dst_data;
1505 UINT y;
1507 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1508 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1509 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1511 * We're doing this...
1513 * instead of boxing the texture :
1514 * |<-texture width ->| -->pow2width| /\
1515 * |111111111111111111| | |
1516 * |222 Texture 222222| boxed empty | texture height
1517 * |3333 Data 33333333| | |
1518 * |444444444444444444| | \/
1519 * ----------------------------------- |
1520 * | boxed empty | boxed empty | pow2height
1521 * | | | \/
1522 * -----------------------------------
1525 * we're repacking the data to the expected texture width
1527 * |<-texture width ->| -->pow2width| /\
1528 * |111111111111111111222222222222222| |
1529 * |222333333333333333333444444444444| texture height
1530 * |444444 | |
1531 * | | \/
1532 * | | |
1533 * | empty | pow2height
1534 * | | \/
1535 * -----------------------------------
1537 * == is the same as
1539 * |<-texture width ->| /\
1540 * |111111111111111111|
1541 * |222222222222222222|texture height
1542 * |333333333333333333|
1543 * |444444444444444444| \/
1544 * --------------------
1546 * This also means that any references to surface memory should work with the data as if it were a
1547 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1549 * internally the texture is still stored in a boxed format so any references to textureName will
1550 * get a boxed texture with width pow2width and not a texture of width resource.width.
1552 * Performance should not be an issue, because applications normally do not lock the surfaces when
1553 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
1554 * and doesn't have to be re-read. */
1555 src_data = mem;
1556 dst_data = data.addr;
1557 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1558 for (y = 0; y < surface->resource.height; ++y)
1560 memcpy(dst_data, src_data, dst_pitch);
1561 src_data += src_pitch;
1562 dst_data += dst_pitch;
1565 HeapFree(GetProcessHeap(), 0, mem);
1570 /* This call just uploads data, the caller is responsible for binding the
1571 * correct texture. */
1572 /* Context activation is done by the caller. */
1573 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1574 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1575 BOOL srgb, const struct wined3d_bo_address *data)
1577 UINT update_w = src_rect->right - src_rect->left;
1578 UINT update_h = src_rect->bottom - src_rect->top;
1580 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1581 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1582 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1584 if (surface->resource.map_count)
1586 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
1587 surface->flags |= SFLAG_PIN_SYSMEM;
1590 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1592 update_h *= format->height_scale.numerator;
1593 update_h /= format->height_scale.denominator;
1596 if (data->buffer_object)
1598 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
1599 checkGLcall("glBindBufferARB");
1602 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1604 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1605 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1606 const BYTE *addr = data->addr;
1607 GLenum internal;
1609 addr += (src_rect->top / format->block_height) * src_pitch;
1610 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1612 if (srgb)
1613 internal = format->glGammaInternal;
1614 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
1615 internal = format->rtInternal;
1616 else
1617 internal = format->glInternal;
1619 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
1620 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1621 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1623 if (row_length == src_pitch)
1625 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1626 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1628 else
1630 UINT row, y;
1632 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
1633 * can't use the unpack row length like below. */
1634 for (row = 0, y = dst_point->y; row < row_count; ++row)
1636 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1637 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1638 y += format->block_height;
1639 addr += src_pitch;
1642 checkGLcall("glCompressedTexSubImage2DARB");
1644 else
1646 const BYTE *addr = data->addr;
1648 addr += src_rect->top * src_pitch;
1649 addr += src_rect->left * format->byte_count;
1651 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1652 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1653 update_w, update_h, format->glFormat, format->glType, addr);
1655 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1656 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1657 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1658 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1659 checkGLcall("glTexSubImage2D");
1662 if (data->buffer_object)
1664 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1665 checkGLcall("glBindBufferARB");
1668 if (wined3d_settings.strict_draw_ordering)
1669 gl_info->gl_ops.gl.p_glFlush();
1671 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1673 struct wined3d_device *device = surface->resource.device;
1674 unsigned int i;
1676 for (i = 0; i < device->context_count; ++i)
1678 context_surface_update(device->contexts[i], surface);
1683 static HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck, BOOL use_texturing,
1684 struct wined3d_format *format, enum wined3d_conversion_type *conversion_type)
1686 BOOL colorkey_active = need_alpha_ck && (surface->container->color_key_flags & WINEDDSD_CKSRCBLT);
1687 const struct wined3d_device *device = surface->resource.device;
1688 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1689 BOOL blit_supported = FALSE;
1691 /* Copy the default values from the surface. Below we might perform fixups */
1692 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
1693 *format = *surface->resource.format;
1694 *conversion_type = WINED3D_CT_NONE;
1696 /* Ok, now look if we have to do any conversion */
1697 switch (surface->resource.format->id)
1699 case WINED3DFMT_P8_UINT:
1700 /* Below the call to blit_supported is disabled for Wine 1.2
1701 * because the function isn't operating correctly yet. At the
1702 * moment 8-bit blits are handled in software and if certain GL
1703 * extensions are around, surface conversion is performed at
1704 * upload time. The blit_supported call recognizes it as a
1705 * destination fixup. This type of upload 'fixup' and 8-bit to
1706 * 8-bit blits need to be handled by the blit_shader.
1707 * TODO: get rid of this #if 0. */
1708 #if 0
1709 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1710 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
1711 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
1712 #endif
1713 blit_supported = gl_info->supported[ARB_FRAGMENT_PROGRAM];
1715 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
1716 * texturing. Further also use conversion in case of color keying.
1717 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1718 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1719 * conflicts with this.
1721 if (!((blit_supported && surface->swapchain && surface == surface->swapchain->front_buffer))
1722 || colorkey_active || !use_texturing)
1724 format->glFormat = GL_RGBA;
1725 format->glInternal = GL_RGBA;
1726 format->glType = GL_UNSIGNED_BYTE;
1727 format->conv_byte_count = 4;
1728 if (colorkey_active)
1729 *conversion_type = WINED3D_CT_PALETTED_CK;
1730 else
1731 *conversion_type = WINED3D_CT_PALETTED;
1733 break;
1735 case WINED3DFMT_B2G3R3_UNORM:
1736 /* **********************
1737 GL_UNSIGNED_BYTE_3_3_2
1738 ********************** */
1739 if (colorkey_active) {
1740 /* This texture format will never be used.. So do not care about color keying
1741 up until the point in time it will be needed :-) */
1742 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1744 break;
1746 case WINED3DFMT_B5G6R5_UNORM:
1747 if (colorkey_active)
1749 *conversion_type = WINED3D_CT_CK_565;
1750 format->glFormat = GL_RGBA;
1751 format->glInternal = GL_RGB5_A1;
1752 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
1753 format->conv_byte_count = 2;
1755 break;
1757 case WINED3DFMT_B5G5R5X1_UNORM:
1758 if (colorkey_active)
1760 *conversion_type = WINED3D_CT_CK_5551;
1761 format->glFormat = GL_BGRA;
1762 format->glInternal = GL_RGB5_A1;
1763 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1764 format->conv_byte_count = 2;
1766 break;
1768 case WINED3DFMT_B8G8R8_UNORM:
1769 if (colorkey_active)
1771 *conversion_type = WINED3D_CT_CK_RGB24;
1772 format->glFormat = GL_RGBA;
1773 format->glInternal = GL_RGBA8;
1774 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1775 format->conv_byte_count = 4;
1777 break;
1779 case WINED3DFMT_B8G8R8X8_UNORM:
1780 if (colorkey_active)
1782 *conversion_type = WINED3D_CT_RGB32_888;
1783 format->glFormat = GL_RGBA;
1784 format->glInternal = GL_RGBA8;
1785 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1786 format->conv_byte_count = 4;
1788 break;
1790 case WINED3DFMT_B8G8R8A8_UNORM:
1791 if (colorkey_active)
1793 *conversion_type = WINED3D_CT_CK_ARGB32;
1794 format->conv_byte_count = 4;
1796 break;
1798 default:
1799 break;
1802 if (*conversion_type != WINED3D_CT_NONE)
1804 format->rtInternal = format->glInternal;
1805 format->glGammaInternal = format->glInternal;
1808 return WINED3D_OK;
1811 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
1813 UINT width_mask, height_mask;
1815 if (!rect->left && !rect->top
1816 && rect->right == surface->resource.width
1817 && rect->bottom == surface->resource.height)
1818 return TRUE;
1820 /* This assumes power of two block sizes, but NPOT block sizes would be
1821 * silly anyway. */
1822 width_mask = surface->resource.format->block_width - 1;
1823 height_mask = surface->resource.format->block_height - 1;
1825 if (!(rect->left & width_mask) && !(rect->top & height_mask)
1826 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
1827 return TRUE;
1829 return FALSE;
1832 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1833 struct wined3d_surface *src_surface, const RECT *src_rect)
1835 const struct wined3d_format *src_format;
1836 const struct wined3d_format *dst_format;
1837 const struct wined3d_gl_info *gl_info;
1838 enum wined3d_conversion_type convert;
1839 struct wined3d_context *context;
1840 struct wined3d_bo_address data;
1841 struct wined3d_format format;
1842 UINT update_w, update_h;
1843 UINT dst_w, dst_h;
1844 RECT r, dst_rect;
1845 UINT src_pitch;
1846 POINT p;
1848 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1849 dst_surface, wine_dbgstr_point(dst_point),
1850 src_surface, wine_dbgstr_rect(src_rect));
1852 src_format = src_surface->resource.format;
1853 dst_format = dst_surface->resource.format;
1855 if (src_format->id != dst_format->id)
1857 WARN("Source and destination surfaces should have the same format.\n");
1858 return WINED3DERR_INVALIDCALL;
1861 if (!dst_point)
1863 p.x = 0;
1864 p.y = 0;
1865 dst_point = &p;
1867 else if (dst_point->x < 0 || dst_point->y < 0)
1869 WARN("Invalid destination point.\n");
1870 return WINED3DERR_INVALIDCALL;
1873 if (!src_rect)
1875 r.left = 0;
1876 r.top = 0;
1877 r.right = src_surface->resource.width;
1878 r.bottom = src_surface->resource.height;
1879 src_rect = &r;
1881 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1882 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1884 WARN("Invalid source rectangle.\n");
1885 return WINED3DERR_INVALIDCALL;
1888 dst_w = dst_surface->resource.width;
1889 dst_h = dst_surface->resource.height;
1891 update_w = src_rect->right - src_rect->left;
1892 update_h = src_rect->bottom - src_rect->top;
1894 if (update_w > dst_w || dst_point->x > dst_w - update_w
1895 || update_h > dst_h || dst_point->y > dst_h - update_h)
1897 WARN("Destination out of bounds.\n");
1898 return WINED3DERR_INVALIDCALL;
1901 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
1903 WARN("Source rectangle not block-aligned.\n");
1904 return WINED3DERR_INVALIDCALL;
1907 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1908 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
1910 WARN("Destination rectangle not block-aligned.\n");
1911 return WINED3DERR_INVALIDCALL;
1914 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1915 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
1916 if (convert != WINED3D_CT_NONE || format.convert)
1917 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1919 context = context_acquire(dst_surface->resource.device, NULL);
1920 gl_info = context->gl_info;
1922 /* Only load the surface for partial updates. For newly allocated texture
1923 * the texture wouldn't be the current location, and we'd upload zeroes
1924 * just to overwrite them again. */
1925 if (update_w == dst_w && update_h == dst_h)
1926 surface_prepare_texture(dst_surface, context, FALSE);
1927 else
1928 surface_load_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1929 wined3d_texture_bind(dst_surface->container, context, FALSE);
1931 surface_get_memory(src_surface, &data, src_surface->locations);
1932 src_pitch = wined3d_surface_get_pitch(src_surface);
1934 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
1936 context_invalidate_active_texture(context);
1938 context_release(context);
1940 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1941 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1943 return WINED3D_OK;
1946 /* This call just allocates the texture, the caller is responsible for binding
1947 * the correct texture. */
1948 /* Context activation is done by the caller. */
1949 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1950 const struct wined3d_format *format, BOOL srgb)
1952 BOOL disable_client_storage = FALSE;
1953 GLsizei width = surface->pow2Width;
1954 GLsizei height = surface->pow2Height;
1955 const BYTE *mem = NULL;
1956 GLenum internal;
1958 if (srgb)
1960 internal = format->glGammaInternal;
1962 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
1964 internal = format->rtInternal;
1966 else
1968 internal = format->glInternal;
1971 if (!internal)
1972 FIXME("No GL internal format for format %s.\n", debug_d3dformat(format->id));
1974 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1976 height *= format->height_scale.numerator;
1977 height /= format->height_scale.denominator;
1980 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",
1981 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
1982 internal, width, height, format->glFormat, format->glType);
1984 if (gl_info->supported[APPLE_CLIENT_STORAGE])
1986 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
1987 || !surface->resource.heap_memory)
1989 /* In some cases we want to disable client storage.
1990 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
1991 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
1992 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
1993 * heap_memory == NULL: Not defined in the extension. Seems to disable client storage effectively
1995 surface->flags &= ~SFLAG_CLIENT;
1997 else
1999 surface->flags |= SFLAG_CLIENT;
2000 mem = surface->resource.heap_memory;
2002 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2003 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2004 disable_client_storage = TRUE;
2008 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2010 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2011 internal, width, height, 0, surface->resource.size, mem));
2012 checkGLcall("glCompressedTexImage2DARB");
2014 else
2016 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
2017 internal, width, height, 0, format->glFormat, format->glType, mem);
2018 checkGLcall("glTexImage2D");
2021 if (disable_client_storage)
2023 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2024 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2028 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2029 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2030 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2031 /* Context activation is done by the caller. */
2032 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2034 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2035 struct wined3d_renderbuffer_entry *entry;
2036 GLuint renderbuffer = 0;
2037 unsigned int src_width, src_height;
2038 unsigned int width, height;
2040 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2042 width = rt->pow2Width;
2043 height = rt->pow2Height;
2045 else
2047 width = surface->pow2Width;
2048 height = surface->pow2Height;
2051 src_width = surface->pow2Width;
2052 src_height = surface->pow2Height;
2054 /* A depth stencil smaller than the render target is not valid */
2055 if (width > src_width || height > src_height) return;
2057 /* Remove any renderbuffer set if the sizes match */
2058 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2059 || (width == src_width && height == src_height))
2061 surface->current_renderbuffer = NULL;
2062 return;
2065 /* Look if we've already got a renderbuffer of the correct dimensions */
2066 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2068 if (entry->width == width && entry->height == height)
2070 renderbuffer = entry->id;
2071 surface->current_renderbuffer = entry;
2072 break;
2076 if (!renderbuffer)
2078 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2079 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2080 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2081 surface->resource.format->glInternal, width, height);
2083 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2084 entry->width = width;
2085 entry->height = height;
2086 entry->id = renderbuffer;
2087 list_add_head(&surface->renderbuffers, &entry->entry);
2089 surface->current_renderbuffer = entry;
2092 checkGLcall("set_compatible_renderbuffer");
2095 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2097 const struct wined3d_swapchain *swapchain = surface->swapchain;
2099 TRACE("surface %p.\n", surface);
2101 if (!swapchain)
2103 ERR("Surface %p is not on a swapchain.\n", surface);
2104 return GL_NONE;
2107 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2109 if (swapchain->render_to_fbo)
2111 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2112 return GL_COLOR_ATTACHMENT0;
2114 TRACE("Returning GL_BACK\n");
2115 return GL_BACK;
2117 else if (surface == swapchain->front_buffer)
2119 TRACE("Returning GL_FRONT\n");
2120 return GL_FRONT;
2123 FIXME("Higher back buffer, returning GL_BACK\n");
2124 return GL_BACK;
2127 void surface_load(struct wined3d_surface *surface, BOOL srgb)
2129 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2130 BOOL ck_changed;
2132 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2134 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
2135 ERR("Not supported on scratch surfaces.\n");
2137 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->container->color_key_flags & WINEDDSD_CKSRCBLT);
2139 /* Reload if either the texture and sysmem have different ideas about the
2140 * color key, or the actual key values changed. */
2141 if (ck_changed || ((surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
2142 && (surface->gl_color_key.color_space_low_value
2143 != surface->container->src_blt_color_key.color_space_low_value
2144 || surface->gl_color_key.color_space_high_value
2145 != surface->container->src_blt_color_key.color_space_high_value)))
2147 TRACE("Reloading because of color keying\n");
2148 /* To perform the color key conversion we need a sysmem copy of
2149 * the surface. Make sure we have it. */
2151 surface_prepare_map_memory(surface);
2152 surface_load_location(surface, surface->map_binding);
2153 surface_invalidate_location(surface, ~surface->map_binding);
2154 /* Switching color keying on / off may change the internal format. */
2155 if (ck_changed)
2156 surface_force_reload(surface);
2158 else if (!(surface->locations & location))
2160 TRACE("Reloading because surface is dirty.\n");
2162 else
2164 TRACE("surface is already in texture\n");
2165 return;
2168 surface_load_location(surface, location);
2169 surface_evict_sysmem(surface);
2172 /* See also float_16_to_32() in wined3d_private.h */
2173 static inline unsigned short float_32_to_16(const float *in)
2175 int exp = 0;
2176 float tmp = fabsf(*in);
2177 unsigned int mantissa;
2178 unsigned short ret;
2180 /* Deal with special numbers */
2181 if (*in == 0.0f)
2182 return 0x0000;
2183 if (isnan(*in))
2184 return 0x7c01;
2185 if (isinf(*in))
2186 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2188 if (tmp < powf(2, 10))
2192 tmp = tmp * 2.0f;
2193 exp--;
2194 } while (tmp < powf(2, 10));
2196 else if (tmp >= powf(2, 11))
2200 tmp /= 2.0f;
2201 exp++;
2202 } while (tmp >= powf(2, 11));
2205 mantissa = (unsigned int)tmp;
2206 if (tmp - mantissa >= 0.5f)
2207 ++mantissa; /* Round to nearest, away from zero. */
2209 exp += 10; /* Normalize the mantissa. */
2210 exp += 15; /* Exponent is encoded with excess 15. */
2212 if (exp > 30) /* too big */
2214 ret = 0x7c00; /* INF */
2216 else if (exp <= 0)
2218 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2219 while (exp <= 0)
2221 mantissa = mantissa >> 1;
2222 ++exp;
2224 ret = mantissa & 0x3ff;
2226 else
2228 ret = (exp << 10) | (mantissa & 0x3ff);
2231 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2232 return ret;
2235 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2237 ULONG refcount;
2239 TRACE("surface %p, swapchain %p, container %p.\n",
2240 surface, surface->swapchain, surface->container);
2242 if (surface->swapchain)
2243 return wined3d_swapchain_incref(surface->swapchain);
2245 if (surface->container)
2246 return wined3d_texture_incref(surface->container);
2248 refcount = InterlockedIncrement(&surface->resource.ref);
2249 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2251 return refcount;
2254 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2256 ULONG refcount;
2258 TRACE("surface %p, swapchain %p, container %p.\n",
2259 surface, surface->swapchain, surface->container);
2261 if (surface->swapchain)
2262 return wined3d_swapchain_decref(surface->swapchain);
2264 if (surface->container)
2265 return wined3d_texture_decref(surface->container);
2267 refcount = InterlockedDecrement(&surface->resource.ref);
2268 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2270 if (!refcount)
2272 surface_cleanup(surface);
2273 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2275 TRACE("Destroyed surface %p.\n", surface);
2276 HeapFree(GetProcessHeap(), 0, surface);
2279 return refcount;
2282 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2284 return resource_set_priority(&surface->resource, priority);
2287 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2289 return resource_get_priority(&surface->resource);
2292 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2294 TRACE("surface %p.\n", surface);
2296 if (!surface->resource.device->d3d_initialized)
2298 ERR("D3D not initialized.\n");
2299 return;
2302 wined3d_texture_preload(surface->container);
2305 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2307 TRACE("surface %p.\n", surface);
2309 return surface->resource.parent;
2312 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2314 TRACE("surface %p.\n", surface);
2316 return &surface->resource;
2319 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2321 TRACE("surface %p, flags %#x.\n", surface, flags);
2323 switch (flags)
2325 case WINEDDGBS_CANBLT:
2326 case WINEDDGBS_ISBLTDONE:
2327 return WINED3D_OK;
2329 default:
2330 return WINED3DERR_INVALIDCALL;
2334 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2336 TRACE("surface %p, flags %#x.\n", surface, flags);
2338 /* XXX: DDERR_INVALIDSURFACETYPE */
2340 switch (flags)
2342 case WINEDDGFS_CANFLIP:
2343 case WINEDDGFS_ISFLIPDONE:
2344 return WINED3D_OK;
2346 default:
2347 return WINED3DERR_INVALIDCALL;
2351 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2353 TRACE("surface %p.\n", surface);
2355 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2356 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2359 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2361 TRACE("surface %p.\n", surface);
2363 surface->flags &= ~SFLAG_LOST;
2364 return WINED3D_OK;
2367 void CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2369 TRACE("surface %p, palette %p.\n", surface, palette);
2371 if (surface->palette == palette)
2373 TRACE("Nop palette change.\n");
2374 return;
2377 surface->palette = palette;
2378 if (palette)
2379 surface->surface_ops->surface_realize_palette(surface);
2382 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2384 unsigned int alignment;
2385 DWORD pitch;
2387 TRACE("surface %p.\n", surface);
2389 if (surface->pitch)
2390 return surface->pitch;
2392 alignment = surface->resource.device->surface_alignment;
2393 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
2394 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2396 TRACE("Returning %u.\n", pitch);
2398 return pitch;
2401 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2403 LONG w, h;
2405 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2407 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2409 WARN("Not an overlay surface.\n");
2410 return WINEDDERR_NOTAOVERLAYSURFACE;
2413 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2414 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2415 surface->overlay_destrect.left = x;
2416 surface->overlay_destrect.top = y;
2417 surface->overlay_destrect.right = x + w;
2418 surface->overlay_destrect.bottom = y + h;
2420 return WINED3D_OK;
2423 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2425 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2427 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2429 TRACE("Not an overlay surface.\n");
2430 return WINEDDERR_NOTAOVERLAYSURFACE;
2433 if (!surface->overlay_dest)
2435 TRACE("Overlay not visible.\n");
2436 *x = 0;
2437 *y = 0;
2438 return WINEDDERR_OVERLAYNOTVISIBLE;
2441 *x = surface->overlay_destrect.left;
2442 *y = surface->overlay_destrect.top;
2444 TRACE("Returning position %d, %d.\n", *x, *y);
2446 return WINED3D_OK;
2449 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2450 DWORD flags, struct wined3d_surface *ref)
2452 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2454 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2456 TRACE("Not an overlay surface.\n");
2457 return WINEDDERR_NOTAOVERLAYSURFACE;
2460 return WINED3D_OK;
2463 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2464 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2466 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2467 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2469 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2471 WARN("Not an overlay surface.\n");
2472 return WINEDDERR_NOTAOVERLAYSURFACE;
2474 else if (!dst_surface)
2476 WARN("Dest surface is NULL.\n");
2477 return WINED3DERR_INVALIDCALL;
2480 if (src_rect)
2482 surface->overlay_srcrect = *src_rect;
2484 else
2486 surface->overlay_srcrect.left = 0;
2487 surface->overlay_srcrect.top = 0;
2488 surface->overlay_srcrect.right = surface->resource.width;
2489 surface->overlay_srcrect.bottom = surface->resource.height;
2492 if (dst_rect)
2494 surface->overlay_destrect = *dst_rect;
2496 else
2498 surface->overlay_destrect.left = 0;
2499 surface->overlay_destrect.top = 0;
2500 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2501 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2504 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2506 surface->overlay_dest = NULL;
2507 list_remove(&surface->overlay_entry);
2510 if (flags & WINEDDOVER_SHOW)
2512 if (surface->overlay_dest != dst_surface)
2514 surface->overlay_dest = dst_surface;
2515 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2518 else if (flags & WINEDDOVER_HIDE)
2520 /* tests show that the rectangles are erased on hide */
2521 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2522 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2523 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2524 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2525 surface->overlay_dest = NULL;
2528 return WINED3D_OK;
2531 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
2532 UINT width, UINT height, enum wined3d_format_id format_id,
2533 enum wined3d_multisample_type multisample_type, UINT multisample_quality,
2534 void *mem, UINT pitch)
2536 struct wined3d_device *device = surface->resource.device;
2537 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2538 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
2539 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height, 1);
2540 BOOL create_dib = FALSE;
2541 HRESULT hr;
2542 DWORD valid_location = 0;
2544 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u, "
2545 "mem %p, pitch %u.\n",
2546 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type, mem, pitch);
2548 if (!resource_size)
2549 return WINED3DERR_INVALIDCALL;
2551 if (surface->resource.map_count || (surface->flags & SFLAG_DCINUSE))
2553 WARN("Surface is mapped or the DC is in use.\n");
2554 return WINED3DERR_INVALIDCALL;
2557 if (device->d3d_initialized)
2558 surface->resource.resource_ops->resource_unload(&surface->resource);
2560 if (surface->flags & SFLAG_DIBSECTION)
2562 DeleteDC(surface->hDC);
2563 DeleteObject(surface->dib.DIBsection);
2564 surface->dib.bitmap_data = NULL;
2565 surface->flags &= ~SFLAG_DIBSECTION;
2566 create_dib = TRUE;
2569 surface->locations = 0;
2570 wined3d_resource_free_sysmem(&surface->resource);
2572 surface->resource.width = width;
2573 surface->resource.height = height;
2574 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2575 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2577 surface->pow2Width = width;
2578 surface->pow2Height = height;
2580 else
2582 surface->pow2Width = surface->pow2Height = 1;
2583 while (surface->pow2Width < width)
2584 surface->pow2Width <<= 1;
2585 while (surface->pow2Height < height)
2586 surface->pow2Height <<= 1;
2589 if (surface->pow2Width != width || surface->pow2Height != height)
2590 surface->flags |= SFLAG_NONPOW2;
2591 else
2592 surface->flags &= ~SFLAG_NONPOW2;
2594 surface->user_memory = mem;
2595 if (surface->user_memory)
2597 surface->map_binding = WINED3D_LOCATION_USER_MEMORY;
2598 valid_location = WINED3D_LOCATION_USER_MEMORY;
2600 surface->pitch = pitch;
2601 surface->resource.format = format;
2602 surface->resource.multisample_type = multisample_type;
2603 surface->resource.multisample_quality = multisample_quality;
2604 if (surface->pitch)
2605 surface->resource.size = height * surface->pitch;
2606 else
2607 surface->resource.size = resource_size;
2609 /* The format might be changed to a format that needs conversion.
2610 * If the surface didn't use PBOs previously but could now, don't
2611 * change it - whatever made us not use PBOs might come back, e.g.
2612 * color keys. */
2613 if (surface->map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2614 surface->map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2616 if (create_dib)
2618 if (FAILED(hr = surface_create_dib_section(surface)))
2620 ERR("Failed to create dib section, hr %#x.\n", hr);
2621 return hr;
2623 if (!valid_location)
2624 valid_location = WINED3D_LOCATION_DIB;
2627 if (!valid_location)
2629 surface_prepare_system_memory(surface);
2630 valid_location = WINED3D_LOCATION_SYSMEM;
2633 surface_validate_location(surface, valid_location);
2635 return WINED3D_OK;
2638 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2639 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2641 unsigned short *dst_s;
2642 const float *src_f;
2643 unsigned int x, y;
2645 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2647 for (y = 0; y < h; ++y)
2649 src_f = (const float *)(src + y * pitch_in);
2650 dst_s = (unsigned short *) (dst + y * pitch_out);
2651 for (x = 0; x < w; ++x)
2653 dst_s[x] = float_32_to_16(src_f + x);
2658 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2659 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2661 static const unsigned char convert_5to8[] =
2663 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2664 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2665 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2666 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2668 static const unsigned char convert_6to8[] =
2670 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2671 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2672 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2673 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2674 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2675 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2676 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2677 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2679 unsigned int x, y;
2681 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2683 for (y = 0; y < h; ++y)
2685 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2686 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2687 for (x = 0; x < w; ++x)
2689 WORD pixel = src_line[x];
2690 dst_line[x] = 0xff000000
2691 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2692 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2693 | convert_5to8[(pixel & 0x001f)];
2698 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2699 * in both cases we're just setting the X / Alpha channel to 0xff. */
2700 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2701 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2703 unsigned int x, y;
2705 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2707 for (y = 0; y < h; ++y)
2709 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2710 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2712 for (x = 0; x < w; ++x)
2714 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2719 static inline BYTE cliptobyte(int x)
2721 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2724 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2725 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2727 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2728 unsigned int x, y;
2730 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2732 for (y = 0; y < h; ++y)
2734 const BYTE *src_line = src + y * pitch_in;
2735 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2736 for (x = 0; x < w; ++x)
2738 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2739 * C = Y - 16; D = U - 128; E = V - 128;
2740 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2741 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2742 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2743 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2744 * U and V are shared between the pixels. */
2745 if (!(x & 1)) /* For every even pixel, read new U and V. */
2747 d = (int) src_line[1] - 128;
2748 e = (int) src_line[3] - 128;
2749 r2 = 409 * e + 128;
2750 g2 = - 100 * d - 208 * e + 128;
2751 b2 = 516 * d + 128;
2753 c2 = 298 * ((int) src_line[0] - 16);
2754 dst_line[x] = 0xff000000
2755 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2756 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2757 | cliptobyte((c2 + b2) >> 8); /* blue */
2758 /* Scale RGB values to 0..255 range,
2759 * then clip them if still not in range (may be negative),
2760 * then shift them within DWORD if necessary. */
2761 src_line += 2;
2766 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2767 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2769 unsigned int x, y;
2770 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2772 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2774 for (y = 0; y < h; ++y)
2776 const BYTE *src_line = src + y * pitch_in;
2777 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2778 for (x = 0; x < w; ++x)
2780 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2781 * C = Y - 16; D = U - 128; E = V - 128;
2782 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2783 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2784 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2785 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2786 * U and V are shared between the pixels. */
2787 if (!(x & 1)) /* For every even pixel, read new U and V. */
2789 d = (int) src_line[1] - 128;
2790 e = (int) src_line[3] - 128;
2791 r2 = 409 * e + 128;
2792 g2 = - 100 * d - 208 * e + 128;
2793 b2 = 516 * d + 128;
2795 c2 = 298 * ((int) src_line[0] - 16);
2796 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2797 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2798 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2799 /* Scale RGB values to 0..255 range,
2800 * then clip them if still not in range (may be negative),
2801 * then shift them within DWORD if necessary. */
2802 src_line += 2;
2807 struct d3dfmt_converter_desc
2809 enum wined3d_format_id from, to;
2810 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2813 static const struct d3dfmt_converter_desc converters[] =
2815 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2816 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2817 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2818 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2819 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2820 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2823 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2824 enum wined3d_format_id to)
2826 unsigned int i;
2828 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2830 if (converters[i].from == from && converters[i].to == to)
2831 return &converters[i];
2834 return NULL;
2837 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2839 struct wined3d_map_desc src_map, dst_map;
2840 const struct d3dfmt_converter_desc *conv;
2841 struct wined3d_texture *ret = NULL;
2842 struct wined3d_resource_desc desc;
2843 struct wined3d_surface *dst;
2845 conv = find_converter(source->resource.format->id, to_fmt);
2846 if (!conv)
2848 FIXME("Cannot find a conversion function from format %s to %s.\n",
2849 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2850 return NULL;
2853 /* FIXME: Multisampled conversion? */
2854 wined3d_resource_get_desc(&source->resource, &desc);
2855 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2856 desc.format = to_fmt;
2857 desc.usage = 0;
2858 desc.pool = WINED3D_POOL_SCRATCH;
2859 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2860 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, &wined3d_null_parent_ops, &ret)))
2862 ERR("Failed to create a destination surface for conversion.\n");
2863 return NULL;
2865 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2867 memset(&src_map, 0, sizeof(src_map));
2868 memset(&dst_map, 0, sizeof(dst_map));
2870 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2872 ERR("Failed to lock the source surface.\n");
2873 wined3d_texture_decref(ret);
2874 return NULL;
2876 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2878 ERR("Failed to lock the destination surface.\n");
2879 wined3d_surface_unmap(source);
2880 wined3d_texture_decref(ret);
2881 return NULL;
2884 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2885 source->resource.width, source->resource.height);
2887 wined3d_surface_unmap(dst);
2888 wined3d_surface_unmap(source);
2890 return ret;
2893 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2894 unsigned int bpp, UINT pitch, DWORD color)
2896 BYTE *first;
2897 unsigned int x, y;
2899 /* Do first row */
2901 #define COLORFILL_ROW(type) \
2902 do { \
2903 type *d = (type *)buf; \
2904 for (x = 0; x < width; ++x) \
2905 d[x] = (type)color; \
2906 } while(0)
2908 switch (bpp)
2910 case 1:
2911 COLORFILL_ROW(BYTE);
2912 break;
2914 case 2:
2915 COLORFILL_ROW(WORD);
2916 break;
2918 case 3:
2920 BYTE *d = buf;
2921 for (x = 0; x < width; ++x, d += 3)
2923 d[0] = (color ) & 0xff;
2924 d[1] = (color >> 8) & 0xff;
2925 d[2] = (color >> 16) & 0xff;
2927 break;
2929 case 4:
2930 COLORFILL_ROW(DWORD);
2931 break;
2933 default:
2934 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2935 return WINED3DERR_NOTAVAILABLE;
2938 #undef COLORFILL_ROW
2940 /* Now copy first row. */
2941 first = buf;
2942 for (y = 1; y < height; ++y)
2944 buf += pitch;
2945 memcpy(buf, first, width * bpp);
2948 return WINED3D_OK;
2951 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2953 return surface_from_resource(resource);
2956 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2958 TRACE("surface %p.\n", surface);
2960 if (!surface->resource.map_count)
2962 WARN("Trying to unmap unmapped surface.\n");
2963 return WINEDDERR_NOTLOCKED;
2965 --surface->resource.map_count;
2967 surface->surface_ops->surface_unmap(surface);
2969 return WINED3D_OK;
2972 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2973 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
2975 const struct wined3d_format *format = surface->resource.format;
2976 struct wined3d_device *device = surface->resource.device;
2977 struct wined3d_context *context;
2978 const struct wined3d_gl_info *gl_info;
2979 BYTE *base_memory;
2981 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
2982 surface, map_desc, wine_dbgstr_rect(rect), flags);
2984 if (surface->resource.map_count)
2986 WARN("Surface is already mapped.\n");
2987 return WINED3DERR_INVALIDCALL;
2990 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
2991 && !surface_check_block_align(surface, rect))
2993 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
2994 wine_dbgstr_rect(rect), format->block_width, format->block_height);
2996 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2997 return WINED3DERR_INVALIDCALL;
3000 ++surface->resource.map_count;
3002 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
3003 WARN("Trying to lock unlockable surface.\n");
3005 /* Performance optimization: Count how often a surface is mapped, if it is
3006 * mapped regularly do not throw away the system memory copy. This avoids
3007 * the need to download the surface from OpenGL all the time. The surface
3008 * is still downloaded if the OpenGL texture is changed. */
3009 if (!(surface->flags & SFLAG_DYNLOCK) && surface->map_binding == WINED3D_LOCATION_SYSMEM)
3011 if (++surface->lockCount > MAXLOCKCOUNT)
3013 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3014 surface->flags |= SFLAG_DYNLOCK;
3018 surface_prepare_map_memory(surface);
3019 if (flags & WINED3D_MAP_DISCARD)
3021 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
3022 wined3d_debug_location(surface->map_binding));
3023 surface_validate_location(surface, surface->map_binding);
3025 else
3027 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
3028 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
3030 surface_load_location(surface, surface->map_binding);
3033 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
3034 surface_invalidate_location(surface, ~surface->map_binding);
3036 switch (surface->map_binding)
3038 case WINED3D_LOCATION_SYSMEM:
3039 base_memory = surface->resource.heap_memory;
3040 break;
3042 case WINED3D_LOCATION_USER_MEMORY:
3043 base_memory = surface->user_memory;
3044 break;
3046 case WINED3D_LOCATION_DIB:
3047 base_memory = surface->dib.bitmap_data;
3048 break;
3050 case WINED3D_LOCATION_BUFFER:
3051 context = context_acquire(device, NULL);
3052 gl_info = context->gl_info;
3054 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
3055 base_memory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3056 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
3057 checkGLcall("map PBO");
3059 context_release(context);
3060 break;
3062 default:
3063 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->map_binding));
3064 base_memory = NULL;
3067 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3068 map_desc->row_pitch = surface->resource.width * format->byte_count;
3069 else
3070 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
3071 map_desc->slice_pitch = 0;
3073 if (!rect)
3075 map_desc->data = base_memory;
3076 surface->lockedRect.left = 0;
3077 surface->lockedRect.top = 0;
3078 surface->lockedRect.right = surface->resource.width;
3079 surface->lockedRect.bottom = surface->resource.height;
3081 else
3083 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3085 /* Compressed textures are block based, so calculate the offset of
3086 * the block that contains the top-left pixel of the locked rectangle. */
3087 map_desc->data = base_memory
3088 + ((rect->top / format->block_height) * map_desc->row_pitch)
3089 + ((rect->left / format->block_width) * format->block_byte_count);
3091 else
3093 map_desc->data = base_memory
3094 + (map_desc->row_pitch * rect->top)
3095 + (rect->left * format->byte_count);
3097 surface->lockedRect.left = rect->left;
3098 surface->lockedRect.top = rect->top;
3099 surface->lockedRect.right = rect->right;
3100 surface->lockedRect.bottom = rect->bottom;
3103 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3104 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
3106 return WINED3D_OK;
3109 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3111 HRESULT hr;
3113 TRACE("surface %p, dc %p.\n", surface, dc);
3115 /* Give more detailed info for ddraw. */
3116 if (surface->flags & SFLAG_DCINUSE)
3117 return WINEDDERR_DCALREADYCREATED;
3119 /* Can't GetDC if the surface is locked. */
3120 if (surface->resource.map_count)
3121 return WINED3DERR_INVALIDCALL;
3123 /* Create a DIB section if there isn't a dc yet. */
3124 if (!surface->hDC)
3126 if (surface->flags & SFLAG_CLIENT)
3128 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
3129 surface_release_client_storage(surface);
3131 hr = surface_create_dib_section(surface);
3132 if (FAILED(hr))
3133 return WINED3DERR_INVALIDCALL;
3134 if (!(surface->map_binding == WINED3D_LOCATION_USER_MEMORY
3135 || surface->flags & SFLAG_PIN_SYSMEM
3136 || surface->pbo))
3137 surface->map_binding = WINED3D_LOCATION_DIB;
3140 surface_load_location(surface, WINED3D_LOCATION_DIB);
3141 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
3143 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3144 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3146 /* GetDC on palettized formats is unsupported in D3D9, and the method
3147 * is missing in D3D8, so this should only be used for DX <=7
3148 * surfaces (with non-device palettes). */
3149 const RGBQUAD *colors = NULL;
3151 if (surface->palette)
3153 colors = surface->palette->colors;
3155 else
3157 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3158 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3160 if (dds_primary && dds_primary->palette)
3161 colors = dds_primary->palette->colors;
3164 if (colors)
3165 SetDIBColorTable(surface->hDC, 0, 256, colors);
3168 surface->flags |= SFLAG_DCINUSE;
3169 surface->resource.map_count++;
3171 *dc = surface->hDC;
3172 TRACE("Returning dc %p.\n", *dc);
3174 return WINED3D_OK;
3177 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3179 TRACE("surface %p, dc %p.\n", surface, dc);
3181 if (!(surface->flags & SFLAG_DCINUSE))
3182 return WINEDDERR_NODC;
3184 if (surface->hDC != dc)
3186 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3187 dc, surface->hDC);
3188 return WINEDDERR_NODC;
3191 surface->resource.map_count--;
3192 surface->flags &= ~SFLAG_DCINUSE;
3194 if (surface->map_binding == WINED3D_LOCATION_USER_MEMORY || (surface->flags & SFLAG_PIN_SYSMEM
3195 && surface->map_binding != WINED3D_LOCATION_DIB))
3197 /* The game Salammbo modifies the surface contents without mapping the surface between
3198 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
3199 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
3200 * Do not only copy the DIB to the map location, but also make sure the map location is
3201 * copied back to the DIB in the next getdc call.
3203 * The same consideration applies to user memory surfaces. */
3204 surface_load_location(surface, surface->map_binding);
3205 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
3208 return WINED3D_OK;
3211 static void read_from_framebuffer(struct wined3d_surface *surface, DWORD dst_location)
3213 struct wined3d_device *device = surface->resource.device;
3214 const struct wined3d_gl_info *gl_info;
3215 struct wined3d_context *context;
3216 BYTE *mem;
3217 GLint fmt;
3218 GLint type;
3219 BYTE *row, *top, *bottom;
3220 int i;
3221 BOOL srcIsUpsideDown;
3222 struct wined3d_bo_address data;
3224 surface_get_memory(surface, &data, dst_location);
3226 context = context_acquire(device, surface);
3227 context_apply_blit_state(context, device);
3228 gl_info = context->gl_info;
3230 /* Select the correct read buffer, and give some debug output.
3231 * There is no need to keep track of the current read buffer or reset it, every part of the code
3232 * that reads sets the read buffer as desired.
3234 if (surface_is_offscreen(surface))
3236 /* Mapping the primary render target which is not on a swapchain.
3237 * Read from the back buffer. */
3238 TRACE("Mapping offscreen render target.\n");
3239 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3240 srcIsUpsideDown = TRUE;
3242 else
3244 /* Onscreen surfaces are always part of a swapchain */
3245 GLenum buffer = surface_get_gl_buffer(surface);
3246 TRACE("Mapping %#x buffer.\n", buffer);
3247 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
3248 checkGLcall("glReadBuffer");
3249 srcIsUpsideDown = FALSE;
3252 switch (surface->resource.format->id)
3254 case WINED3DFMT_P8_UINT:
3255 fmt = GL_ALPHA;
3256 type = GL_UNSIGNED_BYTE;
3257 break;
3259 default:
3260 fmt = surface->resource.format->glFormat;
3261 type = surface->resource.format->glType;
3264 if (data.buffer_object)
3266 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
3267 checkGLcall("glBindBufferARB");
3270 /* Setup pixel store pack state -- to glReadPixels into the correct place */
3271 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
3272 checkGLcall("glPixelStorei");
3274 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
3275 surface->resource.width, surface->resource.height,
3276 fmt, type, data.addr);
3277 checkGLcall("glReadPixels");
3279 /* Reset previous pixel store pack state */
3280 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
3281 checkGLcall("glPixelStorei");
3283 if (!srcIsUpsideDown)
3285 /* glReadPixels returns the image upside down, and there is no way to prevent this.
3286 * Flip the lines in software. */
3287 UINT pitch = wined3d_surface_get_pitch(surface);
3289 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
3290 goto error;
3292 if (data.buffer_object)
3294 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3295 checkGLcall("glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB)");
3297 else
3298 mem = data.addr;
3300 top = mem;
3301 bottom = mem + pitch * (surface->resource.height - 1);
3302 for (i = 0; i < surface->resource.height / 2; i++)
3304 memcpy(row, top, pitch);
3305 memcpy(top, bottom, pitch);
3306 memcpy(bottom, row, pitch);
3307 top += pitch;
3308 bottom -= pitch;
3310 HeapFree(GetProcessHeap(), 0, row);
3312 if (data.buffer_object)
3313 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB));
3316 error:
3317 if (data.buffer_object)
3319 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
3320 checkGLcall("glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0)");
3323 context_release(context);
3326 /* Read the framebuffer contents into a texture. Note that this function
3327 * doesn't do any kind of flipping. Using this on an onscreen surface will
3328 * result in a flipped D3D texture. */
3329 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
3331 struct wined3d_device *device = surface->resource.device;
3332 const struct wined3d_gl_info *gl_info;
3333 struct wined3d_context *context;
3335 context = context_acquire(device, surface);
3336 gl_info = context->gl_info;
3337 device_invalidate_state(device, STATE_FRAMEBUFFER);
3339 surface_prepare_texture(surface, context, srgb);
3340 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3342 TRACE("Reading back offscreen render target %p.\n", surface);
3344 if (surface_is_offscreen(surface))
3345 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3346 else
3347 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
3348 checkGLcall("glReadBuffer");
3350 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
3351 0, 0, 0, 0, surface->resource.width, surface->resource.height);
3352 checkGLcall("glCopyTexSubImage2D");
3354 context_release(context);
3357 /* Context activation is done by the caller. */
3358 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
3359 struct wined3d_context *context, BOOL srgb)
3361 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
3362 enum wined3d_conversion_type convert;
3363 struct wined3d_format format;
3365 if (surface->flags & alloc_flag) return;
3367 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
3368 if (convert != WINED3D_CT_NONE || format.convert)
3369 surface->flags |= SFLAG_CONVERTED;
3370 else surface->flags &= ~SFLAG_CONVERTED;
3372 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3373 surface_allocate_surface(surface, context->gl_info, &format, srgb);
3374 surface->flags |= alloc_flag;
3377 /* Context activation is done by the caller. */
3378 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
3380 struct wined3d_texture *texture = surface->container;
3381 UINT sub_count = texture->level_count * texture->layer_count;
3382 UINT i;
3384 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
3386 for (i = 0; i < sub_count; ++i)
3388 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
3389 surface_prepare_texture_internal(s, context, srgb);
3392 return;
3395 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
3397 if (multisample)
3399 if (surface->rb_multisample)
3400 return;
3402 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
3403 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
3404 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
3405 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
3406 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
3408 else
3410 if (surface->rb_resolved)
3411 return;
3413 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
3414 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
3415 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
3416 surface->pow2Width, surface->pow2Height);
3417 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
3421 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
3423 /* FIXME: Is this really how color keys are supposed to work? I think it
3424 * makes more sense to compare the individual channels. */
3425 return color >= color_key->color_space_low_value
3426 && color <= color_key->color_space_high_value;
3429 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
3431 const struct wined3d_palette *pal = surface->palette;
3432 unsigned int i;
3434 if (!pal)
3436 FIXME("No palette set.\n");
3437 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
3438 * there's no palette at this time. */
3439 for (i = 0; i < 256; i++) table[i][3] = i;
3441 else
3443 TRACE("Using surface palette %p\n", pal);
3444 for (i = 0; i < 256; ++i)
3446 table[i][0] = pal->colors[i].rgbRed;
3447 table[i][1] = pal->colors[i].rgbGreen;
3448 table[i][2] = pal->colors[i].rgbBlue;
3449 /* The palette index is stored in the alpha component. In case of a
3450 * readback we can then read GL_ALPHA. Color keying is handled in
3451 * surface_blt_to_drawable() using a GL_ALPHA_TEST using GL_NOT_EQUAL.
3452 * In case of a P8 surface the color key itself is passed to
3453 * glAlphaFunc in other cases the alpha component of pixels that
3454 * should be masked away is set to 0. */
3455 table[i][3] = i;
3460 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
3461 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
3463 const BYTE *source;
3464 BYTE *dest;
3466 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
3467 src, dst, pitch, width, height, outpitch, conversion_type, surface);
3469 switch (conversion_type)
3471 case WINED3D_CT_NONE:
3473 memcpy(dst, src, pitch * height);
3474 break;
3477 case WINED3D_CT_PALETTED:
3478 case WINED3D_CT_PALETTED_CK:
3480 BYTE table[256][4];
3481 unsigned int x, y;
3483 d3dfmt_p8_init_palette(surface, table, (conversion_type == WINED3D_CT_PALETTED_CK));
3485 for (y = 0; y < height; y++)
3487 source = src + pitch * y;
3488 dest = dst + outpitch * y;
3489 /* This is an 1 bpp format, using the width here is fine */
3490 for (x = 0; x < width; x++) {
3491 BYTE color = *source++;
3492 *dest++ = table[color][0];
3493 *dest++ = table[color][1];
3494 *dest++ = table[color][2];
3495 *dest++ = table[color][3];
3499 break;
3501 case WINED3D_CT_CK_565:
3503 /* Converting the 565 format in 5551 packed to emulate color-keying.
3505 Note : in all these conversion, it would be best to average the averaging
3506 pixels to get the color of the pixel that will be color-keyed to
3507 prevent 'color bleeding'. This will be done later on if ever it is
3508 too visible.
3510 Note2: Nvidia documents say that their driver does not support alpha + color keying
3511 on the same surface and disables color keying in such a case
3513 unsigned int x, y;
3514 const WORD *Source;
3515 WORD *Dest;
3517 TRACE("Color keyed 565\n");
3519 for (y = 0; y < height; y++) {
3520 Source = (const WORD *)(src + y * pitch);
3521 Dest = (WORD *) (dst + y * outpitch);
3522 for (x = 0; x < width; x++ ) {
3523 WORD color = *Source++;
3524 *Dest = ((color & 0xffc0) | ((color & 0x1f) << 1));
3525 if (!color_in_range(&surface->container->src_blt_color_key, color))
3526 *Dest |= 0x0001;
3527 Dest++;
3531 break;
3533 case WINED3D_CT_CK_5551:
3535 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
3536 unsigned int x, y;
3537 const WORD *Source;
3538 WORD *Dest;
3539 TRACE("Color keyed 5551\n");
3540 for (y = 0; y < height; y++) {
3541 Source = (const WORD *)(src + y * pitch);
3542 Dest = (WORD *) (dst + y * outpitch);
3543 for (x = 0; x < width; x++ ) {
3544 WORD color = *Source++;
3545 *Dest = color;
3546 if (!color_in_range(&surface->container->src_blt_color_key, color))
3547 *Dest |= (1 << 15);
3548 else
3549 *Dest &= ~(1 << 15);
3550 Dest++;
3554 break;
3556 case WINED3D_CT_CK_RGB24:
3558 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
3559 unsigned int x, y;
3560 for (y = 0; y < height; y++)
3562 source = src + pitch * y;
3563 dest = dst + outpitch * y;
3564 for (x = 0; x < width; x++) {
3565 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
3566 DWORD dstcolor = color << 8;
3567 if (!color_in_range(&surface->container->src_blt_color_key, color))
3568 dstcolor |= 0xff;
3569 *(DWORD*)dest = dstcolor;
3570 source += 3;
3571 dest += 4;
3575 break;
3577 case WINED3D_CT_RGB32_888:
3579 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
3580 unsigned int x, y;
3581 for (y = 0; y < height; y++)
3583 source = src + pitch * y;
3584 dest = dst + outpitch * y;
3585 for (x = 0; x < width; x++) {
3586 DWORD color = 0xffffff & *(const DWORD*)source;
3587 DWORD dstcolor = color << 8;
3588 if (!color_in_range(&surface->container->src_blt_color_key, color))
3589 dstcolor |= 0xff;
3590 *(DWORD*)dest = dstcolor;
3591 source += 4;
3592 dest += 4;
3596 break;
3598 case WINED3D_CT_CK_ARGB32:
3600 unsigned int x, y;
3601 for (y = 0; y < height; ++y)
3603 source = src + pitch * y;
3604 dest = dst + outpitch * y;
3605 for (x = 0; x < width; ++x)
3607 DWORD color = *(const DWORD *)source;
3608 if (color_in_range(&surface->container->src_blt_color_key, color))
3609 color &= ~0xff000000;
3610 *(DWORD*)dest = color;
3611 source += 4;
3612 dest += 4;
3616 break;
3618 default:
3619 ERR("Unsupported conversion type %#x.\n", conversion_type);
3621 return WINED3D_OK;
3624 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
3626 if (front->container->level_count != 1 || front->container->layer_count != 1
3627 || back->container->level_count != 1 || back->container->layer_count != 1)
3628 ERR("Flip between surfaces %p and %p not supported.\n", front, back);
3630 /* Flip the surface contents */
3631 /* Flip the DC */
3633 HDC tmp;
3634 tmp = front->hDC;
3635 front->hDC = back->hDC;
3636 back->hDC = tmp;
3639 /* Flip the DIBsection */
3641 HBITMAP tmp = front->dib.DIBsection;
3642 front->dib.DIBsection = back->dib.DIBsection;
3643 back->dib.DIBsection = tmp;
3646 /* Flip the surface data */
3648 void* tmp;
3650 tmp = front->dib.bitmap_data;
3651 front->dib.bitmap_data = back->dib.bitmap_data;
3652 back->dib.bitmap_data = tmp;
3654 tmp = front->resource.heap_memory;
3655 front->resource.heap_memory = back->resource.heap_memory;
3656 back->resource.heap_memory = tmp;
3659 /* Flip the PBO */
3661 GLuint tmp_pbo = front->pbo;
3662 front->pbo = back->pbo;
3663 back->pbo = tmp_pbo;
3666 /* Flip the opengl texture */
3668 GLuint tmp;
3670 tmp = back->container->texture_rgb.name;
3671 back->container->texture_rgb.name = front->container->texture_rgb.name;
3672 front->container->texture_rgb.name = tmp;
3674 tmp = back->container->texture_srgb.name;
3675 back->container->texture_srgb.name = front->container->texture_srgb.name;
3676 front->container->texture_srgb.name = tmp;
3678 tmp = back->rb_multisample;
3679 back->rb_multisample = front->rb_multisample;
3680 front->rb_multisample = tmp;
3682 tmp = back->rb_resolved;
3683 back->rb_resolved = front->rb_resolved;
3684 front->rb_resolved = tmp;
3686 resource_unload(&back->resource);
3687 resource_unload(&front->resource);
3691 DWORD tmp_flags = back->flags;
3692 back->flags = front->flags;
3693 front->flags = tmp_flags;
3695 tmp_flags = back->locations;
3696 back->locations = front->locations;
3697 front->locations = tmp_flags;
3701 /* Does a direct frame buffer -> texture copy. Stretching is done with single
3702 * pixel copy calls. */
3703 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3704 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3706 struct wined3d_device *device = dst_surface->resource.device;
3707 const struct wined3d_gl_info *gl_info;
3708 float xrel, yrel;
3709 struct wined3d_context *context;
3710 BOOL upsidedown = FALSE;
3711 RECT dst_rect = *dst_rect_in;
3713 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3714 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3716 if(dst_rect.top > dst_rect.bottom) {
3717 UINT tmp = dst_rect.bottom;
3718 dst_rect.bottom = dst_rect.top;
3719 dst_rect.top = tmp;
3720 upsidedown = TRUE;
3723 context = context_acquire(device, src_surface);
3724 gl_info = context->gl_info;
3725 context_apply_blit_state(context, device);
3726 wined3d_texture_load(dst_surface->container, context, FALSE);
3728 /* Bind the target texture */
3729 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
3730 if (surface_is_offscreen(src_surface))
3732 TRACE("Reading from an offscreen target\n");
3733 upsidedown = !upsidedown;
3734 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3736 else
3738 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3740 checkGLcall("glReadBuffer");
3742 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3743 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3745 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3747 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3749 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3750 ERR("Texture filtering not supported in direct blit.\n");
3752 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3753 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3755 ERR("Texture filtering not supported in direct blit\n");
3758 if (upsidedown
3759 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3760 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3762 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
3763 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3764 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3765 src_rect->left, src_surface->resource.height - src_rect->bottom,
3766 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3768 else
3770 LONG row;
3771 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
3772 /* I have to process this row by row to swap the image,
3773 * otherwise it would be upside down, so stretching in y direction
3774 * doesn't cost extra time
3776 * However, stretching in x direction can be avoided if not necessary
3778 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3779 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3781 /* Well, that stuff works, but it's very slow.
3782 * find a better way instead
3784 LONG col;
3786 for (col = dst_rect.left; col < dst_rect.right; ++col)
3788 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3789 dst_rect.left + col /* x offset */, row /* y offset */,
3790 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3793 else
3795 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3796 dst_rect.left /* x offset */, row /* y offset */,
3797 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3801 checkGLcall("glCopyTexSubImage2D");
3803 context_release(context);
3805 /* The texture is now most up to date - If the surface is a render target
3806 * and has a drawable, this path is never entered. */
3807 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3808 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3811 /* Uses the hardware to stretch and flip the image */
3812 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3813 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3815 struct wined3d_device *device = dst_surface->resource.device;
3816 GLuint src, backup = 0;
3817 float left, right, top, bottom; /* Texture coordinates */
3818 UINT fbwidth = src_surface->resource.width;
3819 UINT fbheight = src_surface->resource.height;
3820 const struct wined3d_gl_info *gl_info;
3821 struct wined3d_context *context;
3822 GLenum drawBuffer = GL_BACK;
3823 GLenum texture_target;
3824 BOOL noBackBufferBackup;
3825 BOOL src_offscreen;
3826 BOOL upsidedown = FALSE;
3827 RECT dst_rect = *dst_rect_in;
3829 TRACE("Using hwstretch blit\n");
3830 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3831 context = context_acquire(device, src_surface);
3832 gl_info = context->gl_info;
3833 context_apply_blit_state(context, device);
3834 wined3d_texture_load(dst_surface->container, context, FALSE);
3836 src_offscreen = surface_is_offscreen(src_surface);
3837 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3838 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3840 /* Get it a description */
3841 wined3d_texture_load(src_surface->container, context, FALSE);
3844 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3845 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3847 if (context->aux_buffers >= 2)
3849 /* Got more than one aux buffer? Use the 2nd aux buffer */
3850 drawBuffer = GL_AUX1;
3852 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3854 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3855 drawBuffer = GL_AUX0;
3858 if (noBackBufferBackup)
3860 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3861 checkGLcall("glGenTextures");
3862 context_bind_texture(context, GL_TEXTURE_2D, backup);
3863 texture_target = GL_TEXTURE_2D;
3865 else
3867 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3868 * we are reading from the back buffer, the backup can be used as source texture
3870 texture_target = src_surface->texture_target;
3871 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3872 gl_info->gl_ops.gl.p_glEnable(texture_target);
3873 checkGLcall("glEnable(texture_target)");
3875 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3876 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3879 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3880 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3882 if(dst_rect.top > dst_rect.bottom) {
3883 UINT tmp = dst_rect.bottom;
3884 dst_rect.bottom = dst_rect.top;
3885 dst_rect.top = tmp;
3886 upsidedown = TRUE;
3889 if (src_offscreen)
3891 TRACE("Reading from an offscreen target\n");
3892 upsidedown = !upsidedown;
3893 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3895 else
3897 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3900 /* TODO: Only back up the part that will be overwritten */
3901 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3903 checkGLcall("glCopyTexSubImage2D");
3905 /* No issue with overriding these - the sampler is dirty due to blit usage */
3906 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3907 wined3d_gl_mag_filter(magLookup, filter));
3908 checkGLcall("glTexParameteri");
3909 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3910 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
3911 checkGLcall("glTexParameteri");
3913 if (!src_surface->swapchain || src_surface == src_surface->swapchain->back_buffers[0])
3915 src = backup ? backup : src_surface->container->texture_rgb.name;
3917 else
3919 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3920 checkGLcall("glReadBuffer(GL_FRONT)");
3922 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3923 checkGLcall("glGenTextures(1, &src)");
3924 context_bind_texture(context, GL_TEXTURE_2D, src);
3926 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3927 * out for power of 2 sizes
3929 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3930 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3931 checkGLcall("glTexImage2D");
3932 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3934 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3935 checkGLcall("glTexParameteri");
3936 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3937 checkGLcall("glTexParameteri");
3939 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3940 checkGLcall("glReadBuffer(GL_BACK)");
3942 if (texture_target != GL_TEXTURE_2D)
3944 gl_info->gl_ops.gl.p_glDisable(texture_target);
3945 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3946 texture_target = GL_TEXTURE_2D;
3949 checkGLcall("glEnd and previous");
3951 left = src_rect->left;
3952 right = src_rect->right;
3954 if (!upsidedown)
3956 top = src_surface->resource.height - src_rect->top;
3957 bottom = src_surface->resource.height - src_rect->bottom;
3959 else
3961 top = src_surface->resource.height - src_rect->bottom;
3962 bottom = src_surface->resource.height - src_rect->top;
3965 if (src_surface->flags & SFLAG_NORMCOORD)
3967 left /= src_surface->pow2Width;
3968 right /= src_surface->pow2Width;
3969 top /= src_surface->pow2Height;
3970 bottom /= src_surface->pow2Height;
3973 /* draw the source texture stretched and upside down. The correct surface is bound already */
3974 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3975 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3977 context_set_draw_buffer(context, drawBuffer);
3978 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3980 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3981 /* bottom left */
3982 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3983 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3985 /* top left */
3986 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3987 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3989 /* top right */
3990 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3991 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3993 /* bottom right */
3994 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3995 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3996 gl_info->gl_ops.gl.p_glEnd();
3997 checkGLcall("glEnd and previous");
3999 if (texture_target != dst_surface->texture_target)
4001 gl_info->gl_ops.gl.p_glDisable(texture_target);
4002 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
4003 texture_target = dst_surface->texture_target;
4006 /* Now read the stretched and upside down image into the destination texture */
4007 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
4008 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
4010 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
4011 0, 0, /* We blitted the image to the origin */
4012 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4013 checkGLcall("glCopyTexSubImage2D");
4015 if (drawBuffer == GL_BACK)
4017 /* Write the back buffer backup back. */
4018 if (backup)
4020 if (texture_target != GL_TEXTURE_2D)
4022 gl_info->gl_ops.gl.p_glDisable(texture_target);
4023 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
4024 texture_target = GL_TEXTURE_2D;
4026 context_bind_texture(context, GL_TEXTURE_2D, backup);
4028 else
4030 if (texture_target != src_surface->texture_target)
4032 gl_info->gl_ops.gl.p_glDisable(texture_target);
4033 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
4034 texture_target = src_surface->texture_target;
4036 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
4039 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
4040 /* top left */
4041 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
4042 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
4044 /* bottom left */
4045 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
4046 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
4048 /* bottom right */
4049 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
4050 (float)fbheight / (float)src_surface->pow2Height);
4051 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
4053 /* top right */
4054 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
4055 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
4056 gl_info->gl_ops.gl.p_glEnd();
4058 gl_info->gl_ops.gl.p_glDisable(texture_target);
4059 checkGLcall("glDisable(texture_target)");
4061 /* Cleanup */
4062 if (src != src_surface->container->texture_rgb.name && src != backup)
4064 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
4065 checkGLcall("glDeleteTextures(1, &src)");
4067 if (backup)
4069 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
4070 checkGLcall("glDeleteTextures(1, &backup)");
4073 if (wined3d_settings.strict_draw_ordering)
4074 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4076 context_release(context);
4078 /* The texture is now most up to date - If the surface is a render target
4079 * and has a drawable, this path is never entered. */
4080 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
4081 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
4084 /* Front buffer coordinates are always full screen coordinates, but our GL
4085 * drawable is limited to the window's client area. The sysmem and texture
4086 * copies do have the full screen size. Note that GL has a bottom-left
4087 * origin, while D3D has a top-left origin. */
4088 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
4090 UINT drawable_height;
4092 if (surface->swapchain && surface == surface->swapchain->front_buffer)
4094 POINT offset = {0, 0};
4095 RECT windowsize;
4097 ScreenToClient(window, &offset);
4098 OffsetRect(rect, offset.x, offset.y);
4100 GetClientRect(window, &windowsize);
4101 drawable_height = windowsize.bottom - windowsize.top;
4103 else
4105 drawable_height = surface->resource.height;
4108 rect->top = drawable_height - rect->top;
4109 rect->bottom = drawable_height - rect->bottom;
4112 static void surface_blt_to_drawable(const struct wined3d_device *device,
4113 enum wined3d_texture_filter_type filter, BOOL alpha_test,
4114 struct wined3d_surface *src_surface, const RECT *src_rect_in,
4115 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
4117 const struct wined3d_gl_info *gl_info;
4118 struct wined3d_context *context;
4119 RECT src_rect, dst_rect;
4121 src_rect = *src_rect_in;
4122 dst_rect = *dst_rect_in;
4124 context = context_acquire(device, dst_surface);
4125 gl_info = context->gl_info;
4127 /* Make sure the surface is up-to-date. This should probably use
4128 * surface_load_location() and worry about the destination surface too,
4129 * unless we're overwriting it completely. */
4130 wined3d_texture_load(src_surface->container, context, FALSE);
4132 /* Activate the destination context, set it up for blitting */
4133 context_apply_blit_state(context, device);
4135 if (!surface_is_offscreen(dst_surface))
4136 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
4138 device->blitter->set_shader(device->blit_priv, context, src_surface);
4140 if (alpha_test)
4142 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
4143 checkGLcall("glEnable(GL_ALPHA_TEST)");
4145 /* For P8 surfaces, the alpha component contains the palette index.
4146 * Which means that the colorkey is one of the palette entries. In
4147 * other cases pixels that should be masked away have alpha set to 0. */
4148 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
4149 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
4150 (float)src_surface->container->src_blt_color_key.color_space_low_value / 256.0f);
4151 else
4152 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
4153 checkGLcall("glAlphaFunc");
4155 else
4157 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4158 checkGLcall("glDisable(GL_ALPHA_TEST)");
4161 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
4163 if (alpha_test)
4165 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4166 checkGLcall("glDisable(GL_ALPHA_TEST)");
4169 /* Leave the opengl state valid for blitting */
4170 device->blitter->unset_shader(context->gl_info);
4172 if (wined3d_settings.strict_draw_ordering
4173 || (dst_surface->swapchain && dst_surface->swapchain->front_buffer == dst_surface))
4174 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4176 context_release(context);
4179 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
4181 struct wined3d_device *device = s->resource.device;
4182 const struct blit_shader *blitter;
4184 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
4185 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
4186 if (!blitter)
4188 FIXME("No blitter is capable of performing the requested color fill operation.\n");
4189 return WINED3DERR_INVALIDCALL;
4192 return blitter->color_fill(device, s, rect, color);
4195 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4196 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
4197 enum wined3d_texture_filter_type filter)
4199 struct wined3d_device *device = dst_surface->resource.device;
4200 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4201 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
4203 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
4204 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4205 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
4207 /* Get the swapchain. One of the surfaces has to be a primary surface */
4208 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4210 WARN("Destination is in sysmem, rejecting gl blt\n");
4211 return WINED3DERR_INVALIDCALL;
4214 dst_swapchain = dst_surface->swapchain;
4216 if (src_surface)
4218 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4220 WARN("Src is in sysmem, rejecting gl blt\n");
4221 return WINED3DERR_INVALIDCALL;
4224 src_swapchain = src_surface->swapchain;
4226 else
4228 src_swapchain = NULL;
4231 /* Early sort out of cases where no render target is used */
4232 if (!dst_swapchain && !src_swapchain
4233 && src_surface != device->fb.render_targets[0]
4234 && dst_surface != device->fb.render_targets[0])
4236 TRACE("No surface is render target, not using hardware blit.\n");
4237 return WINED3DERR_INVALIDCALL;
4240 /* No destination color keying supported */
4241 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
4243 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
4244 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
4245 return WINED3DERR_INVALIDCALL;
4248 if (dst_swapchain && dst_swapchain == src_swapchain)
4250 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
4251 return WINED3DERR_INVALIDCALL;
4254 if (dst_swapchain && src_swapchain)
4256 FIXME("Implement hardware blit between two different swapchains\n");
4257 return WINED3DERR_INVALIDCALL;
4260 if (dst_swapchain)
4262 /* Handled with regular texture -> swapchain blit */
4263 if (src_surface == device->fb.render_targets[0])
4264 TRACE("Blit from active render target to a swapchain\n");
4266 else if (src_swapchain && dst_surface == device->fb.render_targets[0])
4268 FIXME("Implement blit from a swapchain to the active render target\n");
4269 return WINED3DERR_INVALIDCALL;
4272 if ((src_swapchain || src_surface == device->fb.render_targets[0]) && !dst_swapchain)
4274 /* Blit from render target to texture */
4275 BOOL stretchx;
4277 /* P8 read back is not implemented */
4278 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
4279 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
4281 TRACE("P8 read back not supported by frame buffer to texture blit\n");
4282 return WINED3DERR_INVALIDCALL;
4285 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
4287 TRACE("Color keying not supported by frame buffer to texture blit\n");
4288 return WINED3DERR_INVALIDCALL;
4289 /* Destination color key is checked above */
4292 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
4293 stretchx = TRUE;
4294 else
4295 stretchx = FALSE;
4297 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
4298 * flip the image nor scale it.
4300 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
4301 * -> If the app wants an image width an unscaled width, copy it line per line
4302 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
4303 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
4304 * back buffer. This is slower than reading line per line, thus not used for flipping
4305 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
4306 * pixel by pixel. */
4307 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
4308 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
4310 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
4311 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
4313 else
4315 TRACE("Using hardware stretching to flip / stretch the texture.\n");
4316 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
4319 surface_evict_sysmem(dst_surface);
4321 return WINED3D_OK;
4323 else if (src_surface)
4325 /* Blit from offscreen surface to render target */
4326 struct wined3d_color_key old_blt_key = src_surface->container->src_blt_color_key;
4327 DWORD old_color_key_flags = src_surface->container->color_key_flags;
4329 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4331 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4332 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
4333 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
4335 FIXME("Unsupported blit operation falling back to software\n");
4336 return WINED3DERR_INVALIDCALL;
4339 /* Color keying: Check if we have to do a color keyed blt,
4340 * and if not check if a color key is activated.
4342 * Just modify the color keying parameters in the surface and restore them afterwards
4343 * The surface keeps track of the color key last used to load the opengl surface.
4344 * PreLoad will catch the change to the flags and color key and reload if necessary.
4346 if (flags & WINEDDBLT_KEYSRC)
4348 /* Use color key from surface */
4350 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4352 /* Use color key from DDBltFx */
4353 src_surface->container->color_key_flags |= WINEDDSD_CKSRCBLT;
4354 src_surface->container->src_blt_color_key = DDBltFx->ddckSrcColorkey;
4356 else
4358 /* Do not use color key */
4359 src_surface->container->color_key_flags &= ~WINEDDSD_CKSRCBLT;
4362 surface_blt_to_drawable(device, filter,
4363 flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_ALPHATEST),
4364 src_surface, src_rect, dst_surface, dst_rect);
4366 /* Restore the color key parameters */
4367 src_surface->container->color_key_flags = old_color_key_flags;
4368 src_surface->container->src_blt_color_key = old_blt_key;
4370 surface_validate_location(dst_surface, dst_surface->draw_binding);
4371 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
4373 return WINED3D_OK;
4376 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
4377 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
4378 return WINED3DERR_INVALIDCALL;
4381 /* Context activation is done by the caller. */
4382 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
4383 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
4385 struct wined3d_device *device = surface->resource.device;
4386 const struct wined3d_gl_info *gl_info = context->gl_info;
4387 GLint compare_mode = GL_NONE;
4388 struct blt_info info;
4389 GLint old_binding = 0;
4390 RECT rect;
4392 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4394 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
4395 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
4396 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4397 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
4398 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
4399 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
4400 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
4401 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
4402 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4403 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
4404 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
4406 SetRect(&rect, 0, h, w, 0);
4407 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
4408 context_active_texture(context, context->gl_info, 0);
4409 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
4410 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
4411 if (gl_info->supported[ARB_SHADOW])
4413 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
4414 if (compare_mode != GL_NONE)
4415 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
4418 device->shader_backend->shader_select_depth_blt(device->shader_priv,
4419 gl_info, info.tex_type, &surface->ds_current_size);
4421 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
4422 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
4423 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
4424 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
4425 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
4426 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
4427 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
4428 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
4429 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
4430 gl_info->gl_ops.gl.p_glEnd();
4432 if (compare_mode != GL_NONE)
4433 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
4434 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
4436 gl_info->gl_ops.gl.p_glPopAttrib();
4438 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
4441 void surface_modify_ds_location(struct wined3d_surface *surface,
4442 DWORD location, UINT w, UINT h)
4444 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
4446 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
4447 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
4448 wined3d_texture_set_dirty(surface->container);
4450 surface->ds_current_size.cx = w;
4451 surface->ds_current_size.cy = h;
4452 surface->locations = location;
4455 /* Context activation is done by the caller. */
4456 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4458 const struct wined3d_gl_info *gl_info = context->gl_info;
4459 struct wined3d_device *device = surface->resource.device;
4460 GLsizei w, h;
4462 TRACE("surface %p, new location %#x.\n", surface, location);
4464 /* TODO: Make this work for modes other than FBO */
4465 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4467 if (!(surface->locations & location))
4469 w = surface->ds_current_size.cx;
4470 h = surface->ds_current_size.cy;
4471 surface->ds_current_size.cx = 0;
4472 surface->ds_current_size.cy = 0;
4474 else
4476 w = surface->resource.width;
4477 h = surface->resource.height;
4480 if (surface->ds_current_size.cx == surface->resource.width
4481 && surface->ds_current_size.cy == surface->resource.height)
4483 TRACE("Location (%#x) is already up to date.\n", location);
4484 return;
4487 if (surface->current_renderbuffer)
4489 FIXME("Not supported with fixed up depth stencil.\n");
4490 return;
4493 if (surface->locations & WINED3D_LOCATION_DISCARDED)
4495 TRACE("Surface was discarded, no need copy data.\n");
4496 switch (location)
4498 case WINED3D_LOCATION_TEXTURE_RGB:
4499 surface_prepare_texture(surface, context, FALSE);
4500 break;
4501 case WINED3D_LOCATION_RB_MULTISAMPLE:
4502 surface_prepare_rb(surface, gl_info, TRUE);
4503 break;
4504 case WINED3D_LOCATION_DRAWABLE:
4505 /* Nothing to do */
4506 break;
4507 default:
4508 FIXME("Unhandled location %#x\n", location);
4510 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
4511 surface->locations |= location;
4512 surface->ds_current_size.cx = surface->resource.width;
4513 surface->ds_current_size.cy = surface->resource.height;
4514 return;
4517 if (!surface->locations)
4519 FIXME("No up to date depth stencil location.\n");
4520 surface->locations |= location;
4521 surface->ds_current_size.cx = surface->resource.width;
4522 surface->ds_current_size.cy = surface->resource.height;
4523 return;
4526 if (location == WINED3D_LOCATION_TEXTURE_RGB)
4528 GLint old_binding = 0;
4529 GLenum bind_target;
4531 /* The render target is allowed to be smaller than the depth/stencil
4532 * buffer, so the onscreen depth/stencil buffer is potentially smaller
4533 * than the offscreen surface. Don't overwrite the offscreen surface
4534 * with undefined data. */
4535 w = min(w, context->swapchain->desc.backbuffer_width);
4536 h = min(h, context->swapchain->desc.backbuffer_height);
4538 TRACE("Copying onscreen depth buffer to depth texture.\n");
4540 if (!device->depth_blt_texture)
4541 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
4543 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4544 * directly on the FBO texture. That's because we need to flip. */
4545 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4546 context->swapchain->front_buffer, NULL, WINED3D_LOCATION_DRAWABLE);
4547 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4549 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4550 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4552 else
4554 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4555 bind_target = GL_TEXTURE_2D;
4557 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
4558 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
4559 * internal format, because the internal format might include stencil
4560 * data. In principle we should copy stencil data as well, but unless
4561 * the driver supports stencil export it's hard to do, and doesn't
4562 * seem to be needed in practice. If the hardware doesn't support
4563 * writing stencil data, the glCopyTexImage2D() call might trigger
4564 * software fallbacks. */
4565 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
4566 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4567 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4568 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4569 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4570 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4571 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4572 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
4574 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4575 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
4576 context_set_draw_buffer(context, GL_NONE);
4578 /* Do the actual blit */
4579 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
4580 checkGLcall("depth_blt");
4582 context_invalidate_state(context, STATE_FRAMEBUFFER);
4584 if (wined3d_settings.strict_draw_ordering)
4585 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4587 else if (location == WINED3D_LOCATION_DRAWABLE)
4589 TRACE("Copying depth texture to onscreen depth buffer.\n");
4591 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4592 context->swapchain->front_buffer, NULL, WINED3D_LOCATION_DRAWABLE);
4593 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
4594 0, surface->pow2Height - h, w, h, surface->texture_target);
4595 checkGLcall("depth_blt");
4597 context_invalidate_state(context, STATE_FRAMEBUFFER);
4599 if (wined3d_settings.strict_draw_ordering)
4600 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4602 else
4604 ERR("Invalid location (%#x) specified.\n", location);
4607 surface->locations |= location;
4608 surface->ds_current_size.cx = surface->resource.width;
4609 surface->ds_current_size.cy = surface->resource.height;
4612 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
4614 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4616 surface->locations |= location;
4619 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
4621 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4623 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4624 wined3d_texture_set_dirty(surface->container);
4625 surface->locations &= ~location;
4627 if (!surface->locations)
4628 ERR("Surface %p does not have any up to date location.\n", surface);
4631 static DWORD resource_access_from_location(DWORD location)
4633 switch (location)
4635 case WINED3D_LOCATION_SYSMEM:
4636 case WINED3D_LOCATION_USER_MEMORY:
4637 case WINED3D_LOCATION_DIB:
4638 case WINED3D_LOCATION_BUFFER:
4639 return WINED3D_RESOURCE_ACCESS_CPU;
4641 case WINED3D_LOCATION_DRAWABLE:
4642 case WINED3D_LOCATION_TEXTURE_SRGB:
4643 case WINED3D_LOCATION_TEXTURE_RGB:
4644 case WINED3D_LOCATION_RB_MULTISAMPLE:
4645 case WINED3D_LOCATION_RB_RESOLVED:
4646 return WINED3D_RESOURCE_ACCESS_GPU;
4648 default:
4649 FIXME("Unhandled location %#x.\n", location);
4650 return 0;
4654 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
4656 struct wined3d_device *device = surface->resource.device;
4657 struct wined3d_context *context;
4658 const struct wined3d_gl_info *gl_info;
4659 struct wined3d_bo_address dst, src;
4660 UINT size = surface->resource.size;
4662 surface_get_memory(surface, &dst, location);
4663 surface_get_memory(surface, &src, surface->locations);
4665 if (dst.buffer_object)
4667 context = context_acquire(device, NULL);
4668 gl_info = context->gl_info;
4669 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, dst.buffer_object));
4670 GL_EXTCALL(glBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, size, src.addr));
4671 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4672 checkGLcall("Upload PBO");
4673 context_release(context);
4674 return;
4676 if (src.buffer_object)
4678 context = context_acquire(device, NULL);
4679 gl_info = context->gl_info;
4680 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, src.buffer_object));
4681 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_PACK_BUFFER_ARB, 0, size, dst.addr));
4682 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4683 checkGLcall("Download PBO");
4684 context_release(context);
4685 return;
4687 memcpy(dst.addr, src.addr, size);
4690 static void surface_load_sysmem(struct wined3d_surface *surface,
4691 const struct wined3d_gl_info *gl_info, DWORD dst_location)
4693 if (surface->locations & surface_simple_locations)
4695 surface_copy_simple_location(surface, dst_location);
4696 return;
4699 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
4700 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4702 /* Download the surface to system memory. */
4703 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4705 struct wined3d_device *device = surface->resource.device;
4706 struct wined3d_context *context;
4708 /* TODO: Use already acquired context when possible. */
4709 context = context_acquire(device, NULL);
4711 wined3d_texture_bind_and_dirtify(surface->container, context,
4712 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
4713 surface_download_data(surface, gl_info, dst_location);
4715 context_release(context);
4717 return;
4720 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
4722 read_from_framebuffer(surface, dst_location);
4723 return;
4726 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
4727 surface, wined3d_debug_location(surface->locations));
4730 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
4731 const struct wined3d_gl_info *gl_info)
4733 RECT r;
4735 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
4737 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
4738 return WINED3DERR_INVALIDCALL;
4741 surface_get_rect(surface, NULL, &r);
4742 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4743 surface_blt_to_drawable(surface->resource.device,
4744 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
4746 return WINED3D_OK;
4749 static HRESULT surface_load_texture(struct wined3d_surface *surface,
4750 const struct wined3d_gl_info *gl_info, BOOL srgb)
4752 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
4753 struct wined3d_device *device = surface->resource.device;
4754 enum wined3d_conversion_type convert;
4755 struct wined3d_context *context;
4756 UINT width, src_pitch, dst_pitch;
4757 struct wined3d_bo_address data;
4758 struct wined3d_format format;
4759 POINT dst_point = {0, 0};
4760 BYTE *mem = NULL;
4762 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
4763 && surface_is_offscreen(surface)
4764 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
4766 surface_load_fb_texture(surface, srgb);
4768 return WINED3D_OK;
4771 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
4772 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
4773 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4774 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4775 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4777 if (srgb)
4778 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
4779 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
4780 else
4781 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
4782 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
4784 return WINED3D_OK;
4787 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
4788 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
4789 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4790 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4791 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4793 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
4794 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
4795 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
4796 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4798 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
4799 &rect, surface, dst_location, &rect);
4801 return WINED3D_OK;
4804 /* Upload from system memory */
4806 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
4807 TRUE /* We will use textures */, &format, &convert);
4809 if (srgb)
4811 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->map_binding))
4812 == WINED3D_LOCATION_TEXTURE_RGB)
4814 /* Performance warning... */
4815 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
4816 surface_prepare_map_memory(surface);
4817 surface_load_location(surface, surface->map_binding);
4820 else
4822 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->map_binding))
4823 == WINED3D_LOCATION_TEXTURE_SRGB)
4825 /* Performance warning... */
4826 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
4827 surface_prepare_map_memory(surface);
4828 surface_load_location(surface, surface->map_binding);
4832 if (!(surface->locations & surface_simple_locations))
4834 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
4835 /* Lets hope we get it from somewhere... */
4836 surface_prepare_system_memory(surface);
4837 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
4840 /* TODO: Use already acquired context when possible. */
4841 context = context_acquire(device, NULL);
4843 surface_prepare_texture(surface, context, srgb);
4844 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
4846 if (surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
4848 surface->flags |= SFLAG_GLCKEY;
4849 surface->gl_color_key = surface->container->src_blt_color_key;
4851 else surface->flags &= ~SFLAG_GLCKEY;
4853 width = surface->resource.width;
4854 src_pitch = wined3d_surface_get_pitch(surface);
4856 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
4857 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
4858 * called. */
4859 if ((convert != WINED3D_CT_NONE || format.convert) && surface->pbo)
4861 TRACE("Removing the pbo attached to surface %p.\n", surface);
4863 if (surface->flags & SFLAG_DIBSECTION)
4864 surface->map_binding = WINED3D_LOCATION_DIB;
4865 else
4866 surface->map_binding = WINED3D_LOCATION_SYSMEM;
4868 surface_prepare_map_memory(surface);
4869 surface_load_location(surface, surface->map_binding);
4870 surface_remove_pbo(surface, gl_info);
4873 surface_get_memory(surface, &data, surface->locations);
4874 if (format.convert)
4876 /* This code is entered for texture formats which need a fixup. */
4877 UINT height = surface->resource.height;
4879 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4880 dst_pitch = width * format.conv_byte_count;
4881 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4883 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4885 ERR("Out of memory (%u).\n", dst_pitch * height);
4886 context_release(context);
4887 return E_OUTOFMEMORY;
4889 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4890 dst_pitch, dst_pitch * height, width, height, 1);
4891 format.byte_count = format.conv_byte_count;
4892 src_pitch = dst_pitch;
4893 data.addr = mem;
4895 else if (convert != WINED3D_CT_NONE)
4897 /* This code is only entered for color keying fixups */
4898 UINT height = surface->resource.height;
4900 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4901 dst_pitch = width * format.conv_byte_count;
4902 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4904 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4906 ERR("Out of memory (%u).\n", dst_pitch * height);
4907 context_release(context);
4908 return E_OUTOFMEMORY;
4910 d3dfmt_convert_surface(data.addr, mem, src_pitch,
4911 width, height, dst_pitch, convert, surface);
4912 format.byte_count = format.conv_byte_count;
4913 src_pitch = dst_pitch;
4914 data.addr = mem;
4917 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
4919 context_release(context);
4921 HeapFree(GetProcessHeap(), 0, mem);
4923 return WINED3D_OK;
4926 static void surface_multisample_resolve(struct wined3d_surface *surface)
4928 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4930 if (!(surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE))
4931 ERR("Trying to resolve multisampled surface %p, but location WINED3D_LOCATION_RB_MULTISAMPLE not current.\n",
4932 surface);
4934 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
4935 surface, WINED3D_LOCATION_RB_MULTISAMPLE, &rect, surface, WINED3D_LOCATION_RB_RESOLVED, &rect);
4938 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
4940 struct wined3d_device *device = surface->resource.device;
4941 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4942 HRESULT hr;
4944 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4946 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4948 if (location == WINED3D_LOCATION_TEXTURE_RGB
4949 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4951 struct wined3d_context *context = context_acquire(device, NULL);
4952 surface_load_ds_location(surface, context, location);
4953 context_release(context);
4954 return WINED3D_OK;
4956 else if (location & surface->locations && surface->draw_binding != WINED3D_LOCATION_DRAWABLE)
4958 /* Already up to date, nothing to do. */
4959 return WINED3D_OK;
4961 else
4963 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4964 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4965 return WINED3DERR_INVALIDCALL;
4969 if (surface->locations & location)
4971 TRACE("Location already up to date.\n");
4972 return WINED3D_OK;
4975 if (WARN_ON(d3d_surface))
4977 DWORD required_access = resource_access_from_location(location);
4978 if ((surface->resource.access_flags & required_access) != required_access)
4979 WARN("Operation requires %#x access, but surface only has %#x.\n",
4980 required_access, surface->resource.access_flags);
4983 if (!surface->locations)
4985 ERR("Surface %p does not have any up to date location.\n", surface);
4986 surface->flags |= SFLAG_LOST;
4987 return WINED3DERR_DEVICELOST;
4990 switch (location)
4992 case WINED3D_LOCATION_DIB:
4993 case WINED3D_LOCATION_USER_MEMORY:
4994 case WINED3D_LOCATION_SYSMEM:
4995 case WINED3D_LOCATION_BUFFER:
4996 surface_load_sysmem(surface, gl_info, location);
4997 break;
4999 case WINED3D_LOCATION_DRAWABLE:
5000 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
5001 return hr;
5002 break;
5004 case WINED3D_LOCATION_RB_RESOLVED:
5005 surface_multisample_resolve(surface);
5006 break;
5008 case WINED3D_LOCATION_TEXTURE_RGB:
5009 case WINED3D_LOCATION_TEXTURE_SRGB:
5010 if (FAILED(hr = surface_load_texture(surface, gl_info, location == WINED3D_LOCATION_TEXTURE_SRGB)))
5011 return hr;
5012 break;
5014 default:
5015 ERR("Don't know how to handle location %#x.\n", location);
5016 break;
5019 surface_validate_location(surface, location);
5021 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
5022 surface_evict_sysmem(surface);
5024 return WINED3D_OK;
5027 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
5029 struct wined3d_swapchain *swapchain;
5031 /* Not on a swapchain - must be offscreen */
5032 if (!(swapchain = surface->swapchain))
5033 return TRUE;
5035 /* The front buffer is always onscreen */
5036 if (surface == swapchain->front_buffer) return FALSE;
5038 /* If the swapchain is rendered to an FBO, the backbuffer is
5039 * offscreen, otherwise onscreen */
5040 return swapchain->render_to_fbo;
5043 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
5044 /* Context activation is done by the caller. */
5045 static void ffp_blit_free(struct wined3d_device *device) { }
5047 /* Context activation is done by the caller. */
5048 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5050 const struct wined3d_gl_info *gl_info = context->gl_info;
5052 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
5053 checkGLcall("glEnable(target)");
5055 return WINED3D_OK;
5058 /* Context activation is done by the caller. */
5059 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
5061 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
5062 checkGLcall("glDisable(GL_TEXTURE_2D)");
5063 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
5065 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5066 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5068 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
5070 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
5071 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5075 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5076 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5077 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5079 switch (blit_op)
5081 case WINED3D_BLIT_OP_COLOR_BLIT:
5082 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
5083 return FALSE;
5085 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5087 TRACE("Checking support for fixup:\n");
5088 dump_color_fixup_desc(src_format->color_fixup);
5091 /* We only support identity conversions. */
5092 if (!is_identity_fixup(src_format->color_fixup)
5093 || !is_identity_fixup(dst_format->color_fixup))
5095 TRACE("Fixups are not supported.\n");
5096 return FALSE;
5099 return TRUE;
5101 case WINED3D_BLIT_OP_COLOR_FILL:
5102 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
5103 return FALSE;
5105 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5107 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
5108 return FALSE;
5110 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
5112 TRACE("Color fill not supported\n");
5113 return FALSE;
5116 /* FIXME: We should reject color fills on formats with fixups,
5117 * but this would break P8 color fills for example. */
5119 return TRUE;
5121 case WINED3D_BLIT_OP_DEPTH_FILL:
5122 return TRUE;
5124 default:
5125 TRACE("Unsupported blit_op=%d\n", blit_op);
5126 return FALSE;
5130 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5131 const RECT *dst_rect, const struct wined3d_color *color)
5133 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
5134 struct wined3d_fb_state fb = {&dst_surface, NULL};
5136 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
5138 return WINED3D_OK;
5141 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
5142 struct wined3d_surface *surface, const RECT *rect, float depth)
5144 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
5145 struct wined3d_fb_state fb = {NULL, surface};
5147 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
5149 return WINED3D_OK;
5152 const struct blit_shader ffp_blit = {
5153 ffp_blit_alloc,
5154 ffp_blit_free,
5155 ffp_blit_set,
5156 ffp_blit_unset,
5157 ffp_blit_supported,
5158 ffp_blit_color_fill,
5159 ffp_blit_depth_fill,
5162 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
5164 return WINED3D_OK;
5167 /* Context activation is done by the caller. */
5168 static void cpu_blit_free(struct wined3d_device *device)
5172 /* Context activation is done by the caller. */
5173 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5175 return WINED3D_OK;
5178 /* Context activation is done by the caller. */
5179 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
5183 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5184 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5185 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5187 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
5189 return TRUE;
5192 return FALSE;
5195 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
5196 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
5197 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
5199 UINT row_block_count;
5200 const BYTE *src_row;
5201 BYTE *dst_row;
5202 UINT x, y;
5204 src_row = src_data;
5205 dst_row = dst_data;
5207 row_block_count = (update_w + format->block_width - 1) / format->block_width;
5209 if (!flags)
5211 for (y = 0; y < update_h; y += format->block_height)
5213 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
5214 src_row += src_pitch;
5215 dst_row += dst_pitch;
5218 return WINED3D_OK;
5221 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
5223 src_row += (((update_h / format->block_height) - 1) * src_pitch);
5225 switch (format->id)
5227 case WINED3DFMT_DXT1:
5228 for (y = 0; y < update_h; y += format->block_height)
5230 struct block
5232 WORD color[2];
5233 BYTE control_row[4];
5236 const struct block *s = (const struct block *)src_row;
5237 struct block *d = (struct block *)dst_row;
5239 for (x = 0; x < row_block_count; ++x)
5241 d[x].color[0] = s[x].color[0];
5242 d[x].color[1] = s[x].color[1];
5243 d[x].control_row[0] = s[x].control_row[3];
5244 d[x].control_row[1] = s[x].control_row[2];
5245 d[x].control_row[2] = s[x].control_row[1];
5246 d[x].control_row[3] = s[x].control_row[0];
5248 src_row -= src_pitch;
5249 dst_row += dst_pitch;
5251 return WINED3D_OK;
5253 case WINED3DFMT_DXT2:
5254 case WINED3DFMT_DXT3:
5255 for (y = 0; y < update_h; y += format->block_height)
5257 struct block
5259 WORD alpha_row[4];
5260 WORD color[2];
5261 BYTE control_row[4];
5264 const struct block *s = (const struct block *)src_row;
5265 struct block *d = (struct block *)dst_row;
5267 for (x = 0; x < row_block_count; ++x)
5269 d[x].alpha_row[0] = s[x].alpha_row[3];
5270 d[x].alpha_row[1] = s[x].alpha_row[2];
5271 d[x].alpha_row[2] = s[x].alpha_row[1];
5272 d[x].alpha_row[3] = s[x].alpha_row[0];
5273 d[x].color[0] = s[x].color[0];
5274 d[x].color[1] = s[x].color[1];
5275 d[x].control_row[0] = s[x].control_row[3];
5276 d[x].control_row[1] = s[x].control_row[2];
5277 d[x].control_row[2] = s[x].control_row[1];
5278 d[x].control_row[3] = s[x].control_row[0];
5280 src_row -= src_pitch;
5281 dst_row += dst_pitch;
5283 return WINED3D_OK;
5285 default:
5286 FIXME("Compressed flip not implemented for format %s.\n",
5287 debug_d3dformat(format->id));
5288 return E_NOTIMPL;
5292 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
5293 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
5295 return E_NOTIMPL;
5298 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5299 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
5300 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5302 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
5303 const struct wined3d_format *src_format, *dst_format;
5304 struct wined3d_texture *src_texture = NULL;
5305 struct wined3d_map_desc dst_map, src_map;
5306 const BYTE *sbase = NULL;
5307 HRESULT hr = WINED3D_OK;
5308 const BYTE *sbuf;
5309 BYTE *dbuf;
5310 int x, y;
5312 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5313 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5314 flags, fx, debug_d3dtexturefiltertype(filter));
5316 if (src_surface == dst_surface)
5318 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
5319 src_map = dst_map;
5320 src_format = dst_surface->resource.format;
5321 dst_format = src_format;
5323 else
5325 dst_format = dst_surface->resource.format;
5326 if (src_surface)
5328 if (dst_surface->resource.format->id != src_surface->resource.format->id)
5330 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
5332 /* The conv function writes a FIXME */
5333 WARN("Cannot convert source surface format to dest format.\n");
5334 goto release;
5336 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
5338 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
5339 src_format = src_surface->resource.format;
5341 else
5343 src_format = dst_format;
5346 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
5349 bpp = dst_surface->resource.format->byte_count;
5350 srcheight = src_rect->bottom - src_rect->top;
5351 srcwidth = src_rect->right - src_rect->left;
5352 dstheight = dst_rect->bottom - dst_rect->top;
5353 dstwidth = dst_rect->right - dst_rect->left;
5354 width = (dst_rect->right - dst_rect->left) * bpp;
5356 if (src_surface)
5357 sbase = (BYTE *)src_map.data
5358 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
5359 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
5360 if (src_surface != dst_surface)
5361 dbuf = dst_map.data;
5362 else
5363 dbuf = (BYTE *)dst_map.data
5364 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
5365 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
5367 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
5369 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
5371 if (src_surface == dst_surface)
5373 FIXME("Only plain blits supported on compressed surfaces.\n");
5374 hr = E_NOTIMPL;
5375 goto release;
5378 if (srcheight != dstheight || srcwidth != dstwidth)
5380 WARN("Stretching not supported on compressed surfaces.\n");
5381 hr = WINED3DERR_INVALIDCALL;
5382 goto release;
5385 if (!surface_check_block_align(src_surface, src_rect))
5387 WARN("Source rectangle not block-aligned.\n");
5388 hr = WINED3DERR_INVALIDCALL;
5389 goto release;
5392 if (!surface_check_block_align(dst_surface, dst_rect))
5394 WARN("Destination rectangle not block-aligned.\n");
5395 hr = WINED3DERR_INVALIDCALL;
5396 goto release;
5399 hr = surface_cpu_blt_compressed(sbase, dbuf,
5400 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
5401 src_format, flags, fx);
5402 goto release;
5405 /* First, all the 'source-less' blits */
5406 if (flags & WINEDDBLT_COLORFILL)
5408 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
5409 flags &= ~WINEDDBLT_COLORFILL;
5412 if (flags & WINEDDBLT_DEPTHFILL)
5414 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
5416 if (flags & WINEDDBLT_ROP)
5418 /* Catch some degenerate cases here. */
5419 switch (fx->dwROP)
5421 case BLACKNESS:
5422 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
5423 break;
5424 case 0xaa0029: /* No-op */
5425 break;
5426 case WHITENESS:
5427 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
5428 break;
5429 case SRCCOPY: /* Well, we do that below? */
5430 break;
5431 default:
5432 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
5433 goto error;
5435 flags &= ~WINEDDBLT_ROP;
5437 if (flags & WINEDDBLT_DDROPS)
5439 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
5441 /* Now the 'with source' blits. */
5442 if (src_surface)
5444 int sx, xinc, sy, yinc;
5446 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
5447 goto release;
5449 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
5450 && (srcwidth != dstwidth || srcheight != dstheight))
5452 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
5453 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
5456 xinc = (srcwidth << 16) / dstwidth;
5457 yinc = (srcheight << 16) / dstheight;
5459 if (!flags)
5461 /* No effects, we can cheat here. */
5462 if (dstwidth == srcwidth)
5464 if (dstheight == srcheight)
5466 /* No stretching in either direction. This needs to be as
5467 * fast as possible. */
5468 sbuf = sbase;
5470 /* Check for overlapping surfaces. */
5471 if (src_surface != dst_surface || dst_rect->top < src_rect->top
5472 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
5474 /* No overlap, or dst above src, so copy from top downwards. */
5475 for (y = 0; y < dstheight; ++y)
5477 memcpy(dbuf, sbuf, width);
5478 sbuf += src_map.row_pitch;
5479 dbuf += dst_map.row_pitch;
5482 else if (dst_rect->top > src_rect->top)
5484 /* Copy from bottom upwards. */
5485 sbuf += src_map.row_pitch * dstheight;
5486 dbuf += dst_map.row_pitch * dstheight;
5487 for (y = 0; y < dstheight; ++y)
5489 sbuf -= src_map.row_pitch;
5490 dbuf -= dst_map.row_pitch;
5491 memcpy(dbuf, sbuf, width);
5494 else
5496 /* Src and dst overlapping on the same line, use memmove. */
5497 for (y = 0; y < dstheight; ++y)
5499 memmove(dbuf, sbuf, width);
5500 sbuf += src_map.row_pitch;
5501 dbuf += dst_map.row_pitch;
5505 else
5507 /* Stretching in y direction only. */
5508 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5510 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5511 memcpy(dbuf, sbuf, width);
5512 dbuf += dst_map.row_pitch;
5516 else
5518 /* Stretching in X direction. */
5519 int last_sy = -1;
5520 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5522 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5524 if ((sy >> 16) == (last_sy >> 16))
5526 /* This source row is the same as last source row -
5527 * Copy the already stretched row. */
5528 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
5530 else
5532 #define STRETCH_ROW(type) \
5533 do { \
5534 const type *s = (const type *)sbuf; \
5535 type *d = (type *)dbuf; \
5536 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5537 d[x] = s[sx >> 16]; \
5538 } while(0)
5540 switch(bpp)
5542 case 1:
5543 STRETCH_ROW(BYTE);
5544 break;
5545 case 2:
5546 STRETCH_ROW(WORD);
5547 break;
5548 case 4:
5549 STRETCH_ROW(DWORD);
5550 break;
5551 case 3:
5553 const BYTE *s;
5554 BYTE *d = dbuf;
5555 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
5557 DWORD pixel;
5559 s = sbuf + 3 * (sx >> 16);
5560 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5561 d[0] = (pixel ) & 0xff;
5562 d[1] = (pixel >> 8) & 0xff;
5563 d[2] = (pixel >> 16) & 0xff;
5564 d += 3;
5566 break;
5568 default:
5569 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
5570 hr = WINED3DERR_NOTAVAILABLE;
5571 goto error;
5573 #undef STRETCH_ROW
5575 dbuf += dst_map.row_pitch;
5576 last_sy = sy;
5580 else
5582 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
5583 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
5584 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
5585 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
5587 /* The color keying flags are checked for correctness in ddraw */
5588 if (flags & WINEDDBLT_KEYSRC)
5590 keylow = src_surface->container->src_blt_color_key.color_space_low_value;
5591 keyhigh = src_surface->container->src_blt_color_key.color_space_high_value;
5593 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5595 keylow = fx->ddckSrcColorkey.color_space_low_value;
5596 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
5599 if (flags & WINEDDBLT_KEYDEST)
5601 /* Destination color keys are taken from the source surface! */
5602 destkeylow = src_surface->container->dst_blt_color_key.color_space_low_value;
5603 destkeyhigh = src_surface->container->dst_blt_color_key.color_space_high_value;
5605 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
5607 destkeylow = fx->ddckDestColorkey.color_space_low_value;
5608 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
5611 if (bpp == 1)
5613 keymask = 0xff;
5615 else
5617 DWORD masks[3];
5618 get_color_masks(src_format, masks);
5619 keymask = masks[0]
5620 | masks[1]
5621 | masks[2];
5623 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
5626 if (flags & WINEDDBLT_DDFX)
5628 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
5629 LONG tmpxy;
5630 dTopLeft = dbuf;
5631 dTopRight = dbuf + ((dstwidth - 1) * bpp);
5632 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
5633 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
5635 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
5637 /* I don't think we need to do anything about this flag */
5638 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
5640 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
5642 tmp = dTopRight;
5643 dTopRight = dTopLeft;
5644 dTopLeft = tmp;
5645 tmp = dBottomRight;
5646 dBottomRight = dBottomLeft;
5647 dBottomLeft = tmp;
5648 dstxinc = dstxinc * -1;
5650 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
5652 tmp = dTopLeft;
5653 dTopLeft = dBottomLeft;
5654 dBottomLeft = tmp;
5655 tmp = dTopRight;
5656 dTopRight = dBottomRight;
5657 dBottomRight = tmp;
5658 dstyinc = dstyinc * -1;
5660 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
5662 /* I don't think we need to do anything about this flag */
5663 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
5665 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
5667 tmp = dBottomRight;
5668 dBottomRight = dTopLeft;
5669 dTopLeft = tmp;
5670 tmp = dBottomLeft;
5671 dBottomLeft = dTopRight;
5672 dTopRight = tmp;
5673 dstxinc = dstxinc * -1;
5674 dstyinc = dstyinc * -1;
5676 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
5678 tmp = dTopLeft;
5679 dTopLeft = dBottomLeft;
5680 dBottomLeft = dBottomRight;
5681 dBottomRight = dTopRight;
5682 dTopRight = tmp;
5683 tmpxy = dstxinc;
5684 dstxinc = dstyinc;
5685 dstyinc = tmpxy;
5686 dstxinc = dstxinc * -1;
5688 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
5690 tmp = dTopLeft;
5691 dTopLeft = dTopRight;
5692 dTopRight = dBottomRight;
5693 dBottomRight = dBottomLeft;
5694 dBottomLeft = tmp;
5695 tmpxy = dstxinc;
5696 dstxinc = dstyinc;
5697 dstyinc = tmpxy;
5698 dstyinc = dstyinc * -1;
5700 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
5702 /* I don't think we need to do anything about this flag */
5703 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
5705 dbuf = dTopLeft;
5706 flags &= ~(WINEDDBLT_DDFX);
5709 #define COPY_COLORKEY_FX(type) \
5710 do { \
5711 const type *s; \
5712 type *d = (type *)dbuf, *dx, tmp; \
5713 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
5715 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
5716 dx = d; \
5717 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5719 tmp = s[sx >> 16]; \
5720 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
5721 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
5723 dx[0] = tmp; \
5725 dx = (type *)(((BYTE *)dx) + dstxinc); \
5727 d = (type *)(((BYTE *)d) + dstyinc); \
5729 } while(0)
5731 switch (bpp)
5733 case 1:
5734 COPY_COLORKEY_FX(BYTE);
5735 break;
5736 case 2:
5737 COPY_COLORKEY_FX(WORD);
5738 break;
5739 case 4:
5740 COPY_COLORKEY_FX(DWORD);
5741 break;
5742 case 3:
5744 const BYTE *s;
5745 BYTE *d = dbuf, *dx;
5746 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5748 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5749 dx = d;
5750 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
5752 DWORD pixel, dpixel = 0;
5753 s = sbuf + 3 * (sx>>16);
5754 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5755 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
5756 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
5757 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
5759 dx[0] = (pixel ) & 0xff;
5760 dx[1] = (pixel >> 8) & 0xff;
5761 dx[2] = (pixel >> 16) & 0xff;
5763 dx += dstxinc;
5765 d += dstyinc;
5767 break;
5769 default:
5770 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
5771 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
5772 hr = WINED3DERR_NOTAVAILABLE;
5773 goto error;
5774 #undef COPY_COLORKEY_FX
5779 error:
5780 if (flags && FIXME_ON(d3d_surface))
5782 FIXME("\tUnsupported flags: %#x.\n", flags);
5785 release:
5786 wined3d_surface_unmap(dst_surface);
5787 if (src_surface && src_surface != dst_surface)
5788 wined3d_surface_unmap(src_surface);
5789 /* Release the converted surface, if any. */
5790 if (src_texture)
5791 wined3d_texture_decref(src_texture);
5793 return hr;
5796 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5797 const RECT *dst_rect, const struct wined3d_color *color)
5799 static const RECT src_rect;
5800 WINEDDBLTFX BltFx;
5802 memset(&BltFx, 0, sizeof(BltFx));
5803 BltFx.dwSize = sizeof(BltFx);
5804 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
5805 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
5806 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
5809 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
5810 struct wined3d_surface *surface, const RECT *rect, float depth)
5812 FIXME("Depth filling not implemented by cpu_blit.\n");
5813 return WINED3DERR_INVALIDCALL;
5816 const struct blit_shader cpu_blit = {
5817 cpu_blit_alloc,
5818 cpu_blit_free,
5819 cpu_blit_set,
5820 cpu_blit_unset,
5821 cpu_blit_supported,
5822 cpu_blit_color_fill,
5823 cpu_blit_depth_fill,
5826 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5827 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5828 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5830 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5831 struct wined3d_device *device = dst_surface->resource.device;
5832 DWORD src_ds_flags, dst_ds_flags;
5833 RECT src_rect, dst_rect;
5834 BOOL scale, convert;
5835 enum wined3d_conversion_type dst_convert_type;
5836 struct wined3d_format dst_conv_fmt;
5838 static const DWORD simple_blit = WINEDDBLT_ASYNC
5839 | WINEDDBLT_COLORFILL
5840 | WINEDDBLT_WAIT
5841 | WINEDDBLT_DEPTHFILL
5842 | WINEDDBLT_DONOTWAIT;
5844 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5845 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
5846 flags, fx, debug_d3dtexturefiltertype(filter));
5847 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5849 if (fx)
5851 TRACE("dwSize %#x.\n", fx->dwSize);
5852 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5853 TRACE("dwROP %#x.\n", fx->dwROP);
5854 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5855 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5856 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5857 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5858 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5859 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5860 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5861 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5862 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5863 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5864 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5865 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5866 TRACE("dwReserved %#x.\n", fx->dwReserved);
5867 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5868 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5869 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5870 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5871 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5872 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5873 fx->ddckDestColorkey.color_space_low_value,
5874 fx->ddckDestColorkey.color_space_high_value);
5875 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5876 fx->ddckSrcColorkey.color_space_low_value,
5877 fx->ddckSrcColorkey.color_space_high_value);
5880 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5882 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5883 return WINEDDERR_SURFACEBUSY;
5886 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5888 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5889 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5890 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5891 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5892 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5894 WARN("The application gave us a bad destination rectangle.\n");
5895 return WINEDDERR_INVALIDRECT;
5898 if (src_surface)
5900 surface_get_rect(src_surface, src_rect_in, &src_rect);
5902 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5903 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5904 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5905 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5906 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5908 WARN("Application gave us bad source rectangle for Blt.\n");
5909 return WINEDDERR_INVALIDRECT;
5912 else
5914 memset(&src_rect, 0, sizeof(src_rect));
5917 if (!fx || !(fx->dwDDFX))
5918 flags &= ~WINEDDBLT_DDFX;
5920 if (flags & WINEDDBLT_WAIT)
5921 flags &= ~WINEDDBLT_WAIT;
5923 if (flags & WINEDDBLT_ASYNC)
5925 static unsigned int once;
5927 if (!once++)
5928 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5929 flags &= ~WINEDDBLT_ASYNC;
5932 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5933 if (flags & WINEDDBLT_DONOTWAIT)
5935 static unsigned int once;
5937 if (!once++)
5938 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5939 flags &= ~WINEDDBLT_DONOTWAIT;
5942 if (!device->d3d_initialized)
5944 WARN("D3D not initialized, using fallback.\n");
5945 goto cpu;
5948 /* We want to avoid invalidating the sysmem location for converted
5949 * surfaces, since otherwise we'd have to convert the data back when
5950 * locking them. */
5951 d3dfmt_get_conv(dst_surface, TRUE, TRUE, &dst_conv_fmt, &dst_convert_type);
5952 if (dst_convert_type != WINED3D_CT_NONE || dst_conv_fmt.convert || dst_surface->flags & SFLAG_CONVERTED)
5954 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5955 goto cpu;
5958 if (flags & ~simple_blit)
5960 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5961 goto fallback;
5964 if (src_surface)
5965 src_swapchain = src_surface->swapchain;
5966 else
5967 src_swapchain = NULL;
5969 dst_swapchain = dst_surface->swapchain;
5971 /* This isn't strictly needed. FBO blits for example could deal with
5972 * cross-swapchain blits by first downloading the source to a texture
5973 * before switching to the destination context. We just have this here to
5974 * not have to deal with the issue, since cross-swapchain blits should be
5975 * rare. */
5976 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5978 FIXME("Using fallback for cross-swapchain blit.\n");
5979 goto fallback;
5982 scale = src_surface
5983 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5984 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5985 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5987 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5988 if (src_surface)
5989 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5990 else
5991 src_ds_flags = 0;
5993 if (src_ds_flags || dst_ds_flags)
5995 if (flags & WINEDDBLT_DEPTHFILL)
5997 float depth;
5999 TRACE("Depth fill.\n");
6001 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
6002 return WINED3DERR_INVALIDCALL;
6004 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
6005 return WINED3D_OK;
6007 else
6009 if (src_ds_flags != dst_ds_flags)
6011 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
6012 return WINED3DERR_INVALIDCALL;
6015 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->draw_binding, &src_rect,
6016 dst_surface, dst_surface->draw_binding, &dst_rect)))
6017 return WINED3D_OK;
6020 else
6022 /* In principle this would apply to depth blits as well, but we don't
6023 * implement those in the CPU blitter at the moment. */
6024 if ((dst_surface->locations & dst_surface->map_binding)
6025 && (!src_surface || (src_surface->locations & src_surface->map_binding)))
6027 if (scale)
6028 TRACE("Not doing sysmem blit because of scaling.\n");
6029 else if (convert)
6030 TRACE("Not doing sysmem blit because of format conversion.\n");
6031 else
6032 goto cpu;
6035 if (flags & WINEDDBLT_COLORFILL)
6037 struct wined3d_color color;
6039 TRACE("Color fill.\n");
6041 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
6042 goto fallback;
6044 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
6045 return WINED3D_OK;
6047 else
6049 TRACE("Color blit.\n");
6051 /* Upload */
6052 if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
6053 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
6055 if (scale)
6056 TRACE("Not doing upload because of scaling.\n");
6057 else if (convert)
6058 TRACE("Not doing upload because of format conversion.\n");
6059 else
6061 POINT dst_point = {dst_rect.left, dst_rect.top};
6063 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
6065 if (!surface_is_offscreen(dst_surface))
6066 surface_load_location(dst_surface, dst_surface->draw_binding);
6067 return WINED3D_OK;
6072 /* Use present for back -> front blits. The idea behind this is
6073 * that present is potentially faster than a blit, in particular
6074 * when FBO blits aren't available. Some ddraw applications like
6075 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
6076 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
6077 * applications can't blit directly to the frontbuffer. */
6078 if (dst_swapchain && dst_swapchain->back_buffers
6079 && dst_surface == dst_swapchain->front_buffer
6080 && src_surface == dst_swapchain->back_buffers[0])
6082 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
6084 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
6086 /* Set the swap effect to COPY, we don't want the backbuffer
6087 * to become undefined. */
6088 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
6089 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
6090 dst_swapchain->desc.swap_effect = swap_effect;
6092 return WINED3D_OK;
6095 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6096 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6097 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6099 TRACE("Using FBO blit.\n");
6101 surface_blt_fbo(device, filter,
6102 src_surface, src_surface->draw_binding, &src_rect,
6103 dst_surface, dst_surface->draw_binding, &dst_rect);
6104 surface_validate_location(dst_surface, dst_surface->draw_binding);
6105 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
6107 return WINED3D_OK;
6110 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6111 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6112 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6114 TRACE("Using arbfp blit.\n");
6116 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
6117 return WINED3D_OK;
6122 fallback:
6123 /* Special cases for render targets. */
6124 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
6125 return WINED3D_OK;
6127 cpu:
6129 /* For the rest call the X11 surface implementation. For render targets
6130 * this should be implemented OpenGL accelerated in surface_blt_special(),
6131 * other blits are rather rare. */
6132 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
6135 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
6136 const struct wined3d_resource_desc *desc, GLenum target, GLint level, DWORD flags)
6138 struct wined3d_device *device = container->resource.device;
6139 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6140 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
6141 UINT multisample_quality = desc->multisample_quality;
6142 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
6143 unsigned int resource_size;
6144 HRESULT hr;
6146 if (multisample_quality > 0)
6148 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
6149 multisample_quality = 0;
6152 /* Quick lockable sanity check.
6153 * TODO: remove this after surfaces, usage and lockability have been debugged properly
6154 * this function is too deep to need to care about things like this.
6155 * Levels need to be checked too, since they all affect what can be done. */
6156 switch (desc->pool)
6158 case WINED3D_POOL_MANAGED:
6159 if (desc->usage & WINED3DUSAGE_DYNAMIC)
6160 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
6161 break;
6163 case WINED3D_POOL_DEFAULT:
6164 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
6165 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
6166 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
6167 break;
6169 case WINED3D_POOL_SCRATCH:
6170 case WINED3D_POOL_SYSTEM_MEM:
6171 break;
6173 default:
6174 FIXME("Unknown pool %#x.\n", desc->pool);
6175 break;
6178 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
6179 FIXME("Trying to create a render target that isn't in the default pool.\n");
6181 /* FIXME: Check that the format is supported by the device. */
6183 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
6184 if (!resource_size)
6185 return WINED3DERR_INVALIDCALL;
6187 if (device->wined3d->flags & WINED3D_NO3D)
6188 surface->surface_ops = &gdi_surface_ops;
6189 else
6190 surface->surface_ops = &surface_ops;
6192 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
6193 desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height, 1,
6194 resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
6196 WARN("Failed to initialize resource, returning %#x.\n", hr);
6197 return hr;
6200 surface_set_container(surface, container);
6201 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
6202 list_init(&surface->renderbuffers);
6203 list_init(&surface->overlays);
6205 /* Flags */
6206 if (target != GL_TEXTURE_RECTANGLE_ARB)
6207 surface->flags |= SFLAG_NORMCOORD;
6208 if (flags & WINED3D_SURFACE_DISCARD)
6209 surface->flags |= SFLAG_DISCARD;
6210 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
6211 surface->flags |= SFLAG_PIN_SYSMEM;
6212 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
6213 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
6215 surface->map_binding = WINED3D_LOCATION_SYSMEM;
6216 surface->texture_target = target;
6217 surface->texture_level = level;
6219 /* Call the private setup routine */
6220 hr = surface->surface_ops->surface_private_setup(surface);
6221 if (FAILED(hr))
6223 ERR("Private setup failed, returning %#x\n", hr);
6224 surface_set_container(surface, NULL);
6225 surface_cleanup(surface);
6226 return hr;
6229 /* Similar to lockable rendertargets above, creating the DIB section
6230 * during surface initialization prevents the sysmem pointer from changing
6231 * after a wined3d_surface_getdc() call. */
6232 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
6233 && SUCCEEDED(surface_create_dib_section(surface)))
6234 surface->map_binding = WINED3D_LOCATION_DIB;
6236 if (surface->map_binding == WINED3D_LOCATION_DIB)
6238 wined3d_resource_free_sysmem(&surface->resource);
6239 surface_validate_location(surface, WINED3D_LOCATION_DIB);
6240 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
6243 return hr;
6246 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
6247 GLenum target, GLint level, DWORD flags, struct wined3d_surface **surface)
6249 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
6250 const struct wined3d_parent_ops *parent_ops;
6251 struct wined3d_surface *object;
6252 void *parent;
6253 HRESULT hr;
6255 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
6256 "multisample_type %#x, multisample_quality %u, target %#x, level %d, flags %#x, surface %p.\n",
6257 container, desc->width, desc->height, debug_d3dformat(desc->format),
6258 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
6259 desc->multisample_type, desc->multisample_quality, target, level, flags, surface);
6261 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
6262 return E_OUTOFMEMORY;
6264 if (FAILED(hr = surface_init(object, container, desc, target, level, flags)))
6266 WARN("Failed to initialize surface, returning %#x.\n", hr);
6267 HeapFree(GetProcessHeap(), 0, object);
6268 return hr;
6271 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
6272 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
6274 WARN("Failed to create surface parent, hr %#x.\n", hr);
6275 surface_set_container(object, NULL);
6276 wined3d_surface_decref(object);
6277 return hr;
6280 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
6282 object->resource.parent = parent;
6283 object->resource.parent_ops = parent_ops;
6284 *surface = object;
6286 return hr;