wined3d: Remove WINED3D_CT_PALETTED_CK.
[wine/wine-gecko.git] / dlls / wined3d / surface.c
blobe0ba37518c36669532573fdfe5acaa02a3433dde
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 *conversion_type = WINED3D_CT_PALETTED;
1730 break;
1732 case WINED3DFMT_B2G3R3_UNORM:
1733 /* **********************
1734 GL_UNSIGNED_BYTE_3_3_2
1735 ********************** */
1736 if (colorkey_active) {
1737 /* This texture format will never be used.. So do not care about color keying
1738 up until the point in time it will be needed :-) */
1739 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1741 break;
1743 case WINED3DFMT_B5G6R5_UNORM:
1744 if (colorkey_active)
1746 *conversion_type = WINED3D_CT_CK_565;
1747 format->glFormat = GL_RGBA;
1748 format->glInternal = GL_RGB5_A1;
1749 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
1750 format->conv_byte_count = 2;
1752 break;
1754 case WINED3DFMT_B5G5R5X1_UNORM:
1755 if (colorkey_active)
1757 *conversion_type = WINED3D_CT_CK_5551;
1758 format->glFormat = GL_BGRA;
1759 format->glInternal = GL_RGB5_A1;
1760 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1761 format->conv_byte_count = 2;
1763 break;
1765 case WINED3DFMT_B8G8R8_UNORM:
1766 if (colorkey_active)
1768 *conversion_type = WINED3D_CT_CK_RGB24;
1769 format->glFormat = GL_RGBA;
1770 format->glInternal = GL_RGBA8;
1771 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1772 format->conv_byte_count = 4;
1774 break;
1776 case WINED3DFMT_B8G8R8X8_UNORM:
1777 if (colorkey_active)
1779 *conversion_type = WINED3D_CT_RGB32_888;
1780 format->glFormat = GL_RGBA;
1781 format->glInternal = GL_RGBA8;
1782 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1783 format->conv_byte_count = 4;
1785 break;
1787 case WINED3DFMT_B8G8R8A8_UNORM:
1788 if (colorkey_active)
1790 *conversion_type = WINED3D_CT_CK_ARGB32;
1791 format->conv_byte_count = 4;
1793 break;
1795 default:
1796 break;
1799 if (*conversion_type != WINED3D_CT_NONE)
1801 format->rtInternal = format->glInternal;
1802 format->glGammaInternal = format->glInternal;
1805 return WINED3D_OK;
1808 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
1810 UINT width_mask, height_mask;
1812 if (!rect->left && !rect->top
1813 && rect->right == surface->resource.width
1814 && rect->bottom == surface->resource.height)
1815 return TRUE;
1817 /* This assumes power of two block sizes, but NPOT block sizes would be
1818 * silly anyway. */
1819 width_mask = surface->resource.format->block_width - 1;
1820 height_mask = surface->resource.format->block_height - 1;
1822 if (!(rect->left & width_mask) && !(rect->top & height_mask)
1823 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
1824 return TRUE;
1826 return FALSE;
1829 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1830 struct wined3d_surface *src_surface, const RECT *src_rect)
1832 const struct wined3d_format *src_format;
1833 const struct wined3d_format *dst_format;
1834 const struct wined3d_gl_info *gl_info;
1835 enum wined3d_conversion_type convert;
1836 struct wined3d_context *context;
1837 struct wined3d_bo_address data;
1838 struct wined3d_format format;
1839 UINT update_w, update_h;
1840 UINT dst_w, dst_h;
1841 RECT r, dst_rect;
1842 UINT src_pitch;
1843 POINT p;
1845 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1846 dst_surface, wine_dbgstr_point(dst_point),
1847 src_surface, wine_dbgstr_rect(src_rect));
1849 src_format = src_surface->resource.format;
1850 dst_format = dst_surface->resource.format;
1852 if (src_format->id != dst_format->id)
1854 WARN("Source and destination surfaces should have the same format.\n");
1855 return WINED3DERR_INVALIDCALL;
1858 if (!dst_point)
1860 p.x = 0;
1861 p.y = 0;
1862 dst_point = &p;
1864 else if (dst_point->x < 0 || dst_point->y < 0)
1866 WARN("Invalid destination point.\n");
1867 return WINED3DERR_INVALIDCALL;
1870 if (!src_rect)
1872 r.left = 0;
1873 r.top = 0;
1874 r.right = src_surface->resource.width;
1875 r.bottom = src_surface->resource.height;
1876 src_rect = &r;
1878 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1879 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1881 WARN("Invalid source rectangle.\n");
1882 return WINED3DERR_INVALIDCALL;
1885 dst_w = dst_surface->resource.width;
1886 dst_h = dst_surface->resource.height;
1888 update_w = src_rect->right - src_rect->left;
1889 update_h = src_rect->bottom - src_rect->top;
1891 if (update_w > dst_w || dst_point->x > dst_w - update_w
1892 || update_h > dst_h || dst_point->y > dst_h - update_h)
1894 WARN("Destination out of bounds.\n");
1895 return WINED3DERR_INVALIDCALL;
1898 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
1900 WARN("Source rectangle not block-aligned.\n");
1901 return WINED3DERR_INVALIDCALL;
1904 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1905 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
1907 WARN("Destination rectangle not block-aligned.\n");
1908 return WINED3DERR_INVALIDCALL;
1911 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1912 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
1913 if (convert != WINED3D_CT_NONE || format.convert)
1914 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1916 context = context_acquire(dst_surface->resource.device, NULL);
1917 gl_info = context->gl_info;
1919 /* Only load the surface for partial updates. For newly allocated texture
1920 * the texture wouldn't be the current location, and we'd upload zeroes
1921 * just to overwrite them again. */
1922 if (update_w == dst_w && update_h == dst_h)
1923 surface_prepare_texture(dst_surface, context, FALSE);
1924 else
1925 surface_load_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1926 wined3d_texture_bind(dst_surface->container, context, FALSE);
1928 surface_get_memory(src_surface, &data, src_surface->locations);
1929 src_pitch = wined3d_surface_get_pitch(src_surface);
1931 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
1933 context_invalidate_active_texture(context);
1935 context_release(context);
1937 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1938 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1940 return WINED3D_OK;
1943 /* This call just allocates the texture, the caller is responsible for binding
1944 * the correct texture. */
1945 /* Context activation is done by the caller. */
1946 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1947 const struct wined3d_format *format, BOOL srgb)
1949 BOOL disable_client_storage = FALSE;
1950 GLsizei width = surface->pow2Width;
1951 GLsizei height = surface->pow2Height;
1952 const BYTE *mem = NULL;
1953 GLenum internal;
1955 if (srgb)
1957 internal = format->glGammaInternal;
1959 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
1961 internal = format->rtInternal;
1963 else
1965 internal = format->glInternal;
1968 if (!internal)
1969 FIXME("No GL internal format for format %s.\n", debug_d3dformat(format->id));
1971 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1973 height *= format->height_scale.numerator;
1974 height /= format->height_scale.denominator;
1977 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",
1978 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
1979 internal, width, height, format->glFormat, format->glType);
1981 if (gl_info->supported[APPLE_CLIENT_STORAGE])
1983 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
1984 || !surface->resource.heap_memory)
1986 /* In some cases we want to disable client storage.
1987 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
1988 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
1989 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
1990 * heap_memory == NULL: Not defined in the extension. Seems to disable client storage effectively
1992 surface->flags &= ~SFLAG_CLIENT;
1994 else
1996 surface->flags |= SFLAG_CLIENT;
1997 mem = surface->resource.heap_memory;
1999 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2000 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2001 disable_client_storage = TRUE;
2005 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2007 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2008 internal, width, height, 0, surface->resource.size, mem));
2009 checkGLcall("glCompressedTexImage2DARB");
2011 else
2013 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
2014 internal, width, height, 0, format->glFormat, format->glType, mem);
2015 checkGLcall("glTexImage2D");
2018 if (disable_client_storage)
2020 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2021 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2025 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2026 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2027 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2028 /* Context activation is done by the caller. */
2029 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2031 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2032 struct wined3d_renderbuffer_entry *entry;
2033 GLuint renderbuffer = 0;
2034 unsigned int src_width, src_height;
2035 unsigned int width, height;
2037 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2039 width = rt->pow2Width;
2040 height = rt->pow2Height;
2042 else
2044 width = surface->pow2Width;
2045 height = surface->pow2Height;
2048 src_width = surface->pow2Width;
2049 src_height = surface->pow2Height;
2051 /* A depth stencil smaller than the render target is not valid */
2052 if (width > src_width || height > src_height) return;
2054 /* Remove any renderbuffer set if the sizes match */
2055 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2056 || (width == src_width && height == src_height))
2058 surface->current_renderbuffer = NULL;
2059 return;
2062 /* Look if we've already got a renderbuffer of the correct dimensions */
2063 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2065 if (entry->width == width && entry->height == height)
2067 renderbuffer = entry->id;
2068 surface->current_renderbuffer = entry;
2069 break;
2073 if (!renderbuffer)
2075 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2076 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2077 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2078 surface->resource.format->glInternal, width, height);
2080 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2081 entry->width = width;
2082 entry->height = height;
2083 entry->id = renderbuffer;
2084 list_add_head(&surface->renderbuffers, &entry->entry);
2086 surface->current_renderbuffer = entry;
2089 checkGLcall("set_compatible_renderbuffer");
2092 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2094 const struct wined3d_swapchain *swapchain = surface->swapchain;
2096 TRACE("surface %p.\n", surface);
2098 if (!swapchain)
2100 ERR("Surface %p is not on a swapchain.\n", surface);
2101 return GL_NONE;
2104 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2106 if (swapchain->render_to_fbo)
2108 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2109 return GL_COLOR_ATTACHMENT0;
2111 TRACE("Returning GL_BACK\n");
2112 return GL_BACK;
2114 else if (surface == swapchain->front_buffer)
2116 TRACE("Returning GL_FRONT\n");
2117 return GL_FRONT;
2120 FIXME("Higher back buffer, returning GL_BACK\n");
2121 return GL_BACK;
2124 void surface_load(struct wined3d_surface *surface, BOOL srgb)
2126 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2127 BOOL ck_changed;
2129 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2131 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
2132 ERR("Not supported on scratch surfaces.\n");
2134 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->container->color_key_flags & WINEDDSD_CKSRCBLT);
2136 /* Reload if either the texture and sysmem have different ideas about the
2137 * color key, or the actual key values changed. */
2138 if (ck_changed || ((surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
2139 && (surface->gl_color_key.color_space_low_value
2140 != surface->container->src_blt_color_key.color_space_low_value
2141 || surface->gl_color_key.color_space_high_value
2142 != surface->container->src_blt_color_key.color_space_high_value)))
2144 TRACE("Reloading because of color keying\n");
2145 /* To perform the color key conversion we need a sysmem copy of
2146 * the surface. Make sure we have it. */
2148 surface_prepare_map_memory(surface);
2149 surface_load_location(surface, surface->map_binding);
2150 surface_invalidate_location(surface, ~surface->map_binding);
2151 /* Switching color keying on / off may change the internal format. */
2152 if (ck_changed)
2153 surface_force_reload(surface);
2155 else if (!(surface->locations & location))
2157 TRACE("Reloading because surface is dirty.\n");
2159 else
2161 TRACE("surface is already in texture\n");
2162 return;
2165 surface_load_location(surface, location);
2166 surface_evict_sysmem(surface);
2169 /* See also float_16_to_32() in wined3d_private.h */
2170 static inline unsigned short float_32_to_16(const float *in)
2172 int exp = 0;
2173 float tmp = fabsf(*in);
2174 unsigned int mantissa;
2175 unsigned short ret;
2177 /* Deal with special numbers */
2178 if (*in == 0.0f)
2179 return 0x0000;
2180 if (isnan(*in))
2181 return 0x7c01;
2182 if (isinf(*in))
2183 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2185 if (tmp < powf(2, 10))
2189 tmp = tmp * 2.0f;
2190 exp--;
2191 } while (tmp < powf(2, 10));
2193 else if (tmp >= powf(2, 11))
2197 tmp /= 2.0f;
2198 exp++;
2199 } while (tmp >= powf(2, 11));
2202 mantissa = (unsigned int)tmp;
2203 if (tmp - mantissa >= 0.5f)
2204 ++mantissa; /* Round to nearest, away from zero. */
2206 exp += 10; /* Normalize the mantissa. */
2207 exp += 15; /* Exponent is encoded with excess 15. */
2209 if (exp > 30) /* too big */
2211 ret = 0x7c00; /* INF */
2213 else if (exp <= 0)
2215 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2216 while (exp <= 0)
2218 mantissa = mantissa >> 1;
2219 ++exp;
2221 ret = mantissa & 0x3ff;
2223 else
2225 ret = (exp << 10) | (mantissa & 0x3ff);
2228 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2229 return ret;
2232 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2234 ULONG refcount;
2236 TRACE("surface %p, swapchain %p, container %p.\n",
2237 surface, surface->swapchain, surface->container);
2239 if (surface->swapchain)
2240 return wined3d_swapchain_incref(surface->swapchain);
2242 if (surface->container)
2243 return wined3d_texture_incref(surface->container);
2245 refcount = InterlockedIncrement(&surface->resource.ref);
2246 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2248 return refcount;
2251 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2253 ULONG refcount;
2255 TRACE("surface %p, swapchain %p, container %p.\n",
2256 surface, surface->swapchain, surface->container);
2258 if (surface->swapchain)
2259 return wined3d_swapchain_decref(surface->swapchain);
2261 if (surface->container)
2262 return wined3d_texture_decref(surface->container);
2264 refcount = InterlockedDecrement(&surface->resource.ref);
2265 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2267 if (!refcount)
2269 surface_cleanup(surface);
2270 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2272 TRACE("Destroyed surface %p.\n", surface);
2273 HeapFree(GetProcessHeap(), 0, surface);
2276 return refcount;
2279 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2281 return resource_set_priority(&surface->resource, priority);
2284 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2286 return resource_get_priority(&surface->resource);
2289 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2291 TRACE("surface %p.\n", surface);
2293 if (!surface->resource.device->d3d_initialized)
2295 ERR("D3D not initialized.\n");
2296 return;
2299 wined3d_texture_preload(surface->container);
2302 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2304 TRACE("surface %p.\n", surface);
2306 return surface->resource.parent;
2309 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2311 TRACE("surface %p.\n", surface);
2313 return &surface->resource;
2316 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2318 TRACE("surface %p, flags %#x.\n", surface, flags);
2320 switch (flags)
2322 case WINEDDGBS_CANBLT:
2323 case WINEDDGBS_ISBLTDONE:
2324 return WINED3D_OK;
2326 default:
2327 return WINED3DERR_INVALIDCALL;
2331 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2333 TRACE("surface %p, flags %#x.\n", surface, flags);
2335 /* XXX: DDERR_INVALIDSURFACETYPE */
2337 switch (flags)
2339 case WINEDDGFS_CANFLIP:
2340 case WINEDDGFS_ISFLIPDONE:
2341 return WINED3D_OK;
2343 default:
2344 return WINED3DERR_INVALIDCALL;
2348 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2350 TRACE("surface %p.\n", surface);
2352 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2353 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2356 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2358 TRACE("surface %p.\n", surface);
2360 surface->flags &= ~SFLAG_LOST;
2361 return WINED3D_OK;
2364 void CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2366 TRACE("surface %p, palette %p.\n", surface, palette);
2368 if (surface->palette == palette)
2370 TRACE("Nop palette change.\n");
2371 return;
2374 surface->palette = palette;
2375 if (palette)
2376 surface->surface_ops->surface_realize_palette(surface);
2379 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2381 unsigned int alignment;
2382 DWORD pitch;
2384 TRACE("surface %p.\n", surface);
2386 if (surface->pitch)
2387 return surface->pitch;
2389 alignment = surface->resource.device->surface_alignment;
2390 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
2391 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2393 TRACE("Returning %u.\n", pitch);
2395 return pitch;
2398 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2400 LONG w, h;
2402 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2404 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2406 WARN("Not an overlay surface.\n");
2407 return WINEDDERR_NOTAOVERLAYSURFACE;
2410 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2411 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2412 surface->overlay_destrect.left = x;
2413 surface->overlay_destrect.top = y;
2414 surface->overlay_destrect.right = x + w;
2415 surface->overlay_destrect.bottom = y + h;
2417 return WINED3D_OK;
2420 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2422 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2424 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2426 TRACE("Not an overlay surface.\n");
2427 return WINEDDERR_NOTAOVERLAYSURFACE;
2430 if (!surface->overlay_dest)
2432 TRACE("Overlay not visible.\n");
2433 *x = 0;
2434 *y = 0;
2435 return WINEDDERR_OVERLAYNOTVISIBLE;
2438 *x = surface->overlay_destrect.left;
2439 *y = surface->overlay_destrect.top;
2441 TRACE("Returning position %d, %d.\n", *x, *y);
2443 return WINED3D_OK;
2446 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2447 DWORD flags, struct wined3d_surface *ref)
2449 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2451 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2453 TRACE("Not an overlay surface.\n");
2454 return WINEDDERR_NOTAOVERLAYSURFACE;
2457 return WINED3D_OK;
2460 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2461 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2463 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2464 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2466 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2468 WARN("Not an overlay surface.\n");
2469 return WINEDDERR_NOTAOVERLAYSURFACE;
2471 else if (!dst_surface)
2473 WARN("Dest surface is NULL.\n");
2474 return WINED3DERR_INVALIDCALL;
2477 if (src_rect)
2479 surface->overlay_srcrect = *src_rect;
2481 else
2483 surface->overlay_srcrect.left = 0;
2484 surface->overlay_srcrect.top = 0;
2485 surface->overlay_srcrect.right = surface->resource.width;
2486 surface->overlay_srcrect.bottom = surface->resource.height;
2489 if (dst_rect)
2491 surface->overlay_destrect = *dst_rect;
2493 else
2495 surface->overlay_destrect.left = 0;
2496 surface->overlay_destrect.top = 0;
2497 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2498 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2501 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2503 surface->overlay_dest = NULL;
2504 list_remove(&surface->overlay_entry);
2507 if (flags & WINEDDOVER_SHOW)
2509 if (surface->overlay_dest != dst_surface)
2511 surface->overlay_dest = dst_surface;
2512 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2515 else if (flags & WINEDDOVER_HIDE)
2517 /* tests show that the rectangles are erased on hide */
2518 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2519 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2520 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2521 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2522 surface->overlay_dest = NULL;
2525 return WINED3D_OK;
2528 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
2529 UINT width, UINT height, enum wined3d_format_id format_id,
2530 enum wined3d_multisample_type multisample_type, UINT multisample_quality,
2531 void *mem, UINT pitch)
2533 struct wined3d_device *device = surface->resource.device;
2534 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2535 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
2536 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height, 1);
2537 BOOL create_dib = FALSE;
2538 HRESULT hr;
2539 DWORD valid_location = 0;
2541 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u, "
2542 "mem %p, pitch %u.\n",
2543 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type, mem, pitch);
2545 if (!resource_size)
2546 return WINED3DERR_INVALIDCALL;
2548 if (surface->resource.map_count || (surface->flags & SFLAG_DCINUSE))
2550 WARN("Surface is mapped or the DC is in use.\n");
2551 return WINED3DERR_INVALIDCALL;
2554 if (device->d3d_initialized)
2555 surface->resource.resource_ops->resource_unload(&surface->resource);
2557 if (surface->flags & SFLAG_DIBSECTION)
2559 DeleteDC(surface->hDC);
2560 DeleteObject(surface->dib.DIBsection);
2561 surface->dib.bitmap_data = NULL;
2562 surface->flags &= ~SFLAG_DIBSECTION;
2563 create_dib = TRUE;
2566 surface->locations = 0;
2567 wined3d_resource_free_sysmem(&surface->resource);
2569 surface->resource.width = width;
2570 surface->resource.height = height;
2571 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2572 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2574 surface->pow2Width = width;
2575 surface->pow2Height = height;
2577 else
2579 surface->pow2Width = surface->pow2Height = 1;
2580 while (surface->pow2Width < width)
2581 surface->pow2Width <<= 1;
2582 while (surface->pow2Height < height)
2583 surface->pow2Height <<= 1;
2586 if (surface->pow2Width != width || surface->pow2Height != height)
2587 surface->flags |= SFLAG_NONPOW2;
2588 else
2589 surface->flags &= ~SFLAG_NONPOW2;
2591 surface->user_memory = mem;
2592 if (surface->user_memory)
2594 surface->map_binding = WINED3D_LOCATION_USER_MEMORY;
2595 valid_location = WINED3D_LOCATION_USER_MEMORY;
2597 surface->pitch = pitch;
2598 surface->resource.format = format;
2599 surface->resource.multisample_type = multisample_type;
2600 surface->resource.multisample_quality = multisample_quality;
2601 if (surface->pitch)
2602 surface->resource.size = height * surface->pitch;
2603 else
2604 surface->resource.size = resource_size;
2606 /* The format might be changed to a format that needs conversion.
2607 * If the surface didn't use PBOs previously but could now, don't
2608 * change it - whatever made us not use PBOs might come back, e.g.
2609 * color keys. */
2610 if (surface->map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2611 surface->map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2613 if (create_dib)
2615 if (FAILED(hr = surface_create_dib_section(surface)))
2617 ERR("Failed to create dib section, hr %#x.\n", hr);
2618 return hr;
2620 if (!valid_location)
2621 valid_location = WINED3D_LOCATION_DIB;
2624 if (!valid_location)
2626 surface_prepare_system_memory(surface);
2627 valid_location = WINED3D_LOCATION_SYSMEM;
2630 surface_validate_location(surface, valid_location);
2632 return WINED3D_OK;
2635 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2636 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2638 unsigned short *dst_s;
2639 const float *src_f;
2640 unsigned int x, y;
2642 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2644 for (y = 0; y < h; ++y)
2646 src_f = (const float *)(src + y * pitch_in);
2647 dst_s = (unsigned short *) (dst + y * pitch_out);
2648 for (x = 0; x < w; ++x)
2650 dst_s[x] = float_32_to_16(src_f + x);
2655 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2656 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2658 static const unsigned char convert_5to8[] =
2660 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2661 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2662 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2663 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2665 static const unsigned char convert_6to8[] =
2667 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2668 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2669 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2670 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2671 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2672 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2673 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2674 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2676 unsigned int x, y;
2678 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2680 for (y = 0; y < h; ++y)
2682 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2683 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2684 for (x = 0; x < w; ++x)
2686 WORD pixel = src_line[x];
2687 dst_line[x] = 0xff000000
2688 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2689 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2690 | convert_5to8[(pixel & 0x001f)];
2695 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2696 * in both cases we're just setting the X / Alpha channel to 0xff. */
2697 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2698 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2700 unsigned int x, y;
2702 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2704 for (y = 0; y < h; ++y)
2706 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2707 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2709 for (x = 0; x < w; ++x)
2711 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2716 static inline BYTE cliptobyte(int x)
2718 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2721 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2722 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2724 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2725 unsigned int x, y;
2727 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2729 for (y = 0; y < h; ++y)
2731 const BYTE *src_line = src + y * pitch_in;
2732 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2733 for (x = 0; x < w; ++x)
2735 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2736 * C = Y - 16; D = U - 128; E = V - 128;
2737 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2738 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2739 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2740 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2741 * U and V are shared between the pixels. */
2742 if (!(x & 1)) /* For every even pixel, read new U and V. */
2744 d = (int) src_line[1] - 128;
2745 e = (int) src_line[3] - 128;
2746 r2 = 409 * e + 128;
2747 g2 = - 100 * d - 208 * e + 128;
2748 b2 = 516 * d + 128;
2750 c2 = 298 * ((int) src_line[0] - 16);
2751 dst_line[x] = 0xff000000
2752 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2753 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2754 | cliptobyte((c2 + b2) >> 8); /* blue */
2755 /* Scale RGB values to 0..255 range,
2756 * then clip them if still not in range (may be negative),
2757 * then shift them within DWORD if necessary. */
2758 src_line += 2;
2763 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2764 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2766 unsigned int x, y;
2767 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2769 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2771 for (y = 0; y < h; ++y)
2773 const BYTE *src_line = src + y * pitch_in;
2774 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2775 for (x = 0; x < w; ++x)
2777 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2778 * C = Y - 16; D = U - 128; E = V - 128;
2779 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2780 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2781 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2782 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2783 * U and V are shared between the pixels. */
2784 if (!(x & 1)) /* For every even pixel, read new U and V. */
2786 d = (int) src_line[1] - 128;
2787 e = (int) src_line[3] - 128;
2788 r2 = 409 * e + 128;
2789 g2 = - 100 * d - 208 * e + 128;
2790 b2 = 516 * d + 128;
2792 c2 = 298 * ((int) src_line[0] - 16);
2793 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2794 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2795 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2796 /* Scale RGB values to 0..255 range,
2797 * then clip them if still not in range (may be negative),
2798 * then shift them within DWORD if necessary. */
2799 src_line += 2;
2804 struct d3dfmt_converter_desc
2806 enum wined3d_format_id from, to;
2807 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2810 static const struct d3dfmt_converter_desc converters[] =
2812 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2813 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2814 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2815 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2816 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2817 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2820 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2821 enum wined3d_format_id to)
2823 unsigned int i;
2825 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2827 if (converters[i].from == from && converters[i].to == to)
2828 return &converters[i];
2831 return NULL;
2834 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2836 struct wined3d_map_desc src_map, dst_map;
2837 const struct d3dfmt_converter_desc *conv;
2838 struct wined3d_texture *ret = NULL;
2839 struct wined3d_resource_desc desc;
2840 struct wined3d_surface *dst;
2842 conv = find_converter(source->resource.format->id, to_fmt);
2843 if (!conv)
2845 FIXME("Cannot find a conversion function from format %s to %s.\n",
2846 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2847 return NULL;
2850 /* FIXME: Multisampled conversion? */
2851 wined3d_resource_get_desc(&source->resource, &desc);
2852 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2853 desc.format = to_fmt;
2854 desc.usage = 0;
2855 desc.pool = WINED3D_POOL_SCRATCH;
2856 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2857 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, &wined3d_null_parent_ops, &ret)))
2859 ERR("Failed to create a destination surface for conversion.\n");
2860 return NULL;
2862 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2864 memset(&src_map, 0, sizeof(src_map));
2865 memset(&dst_map, 0, sizeof(dst_map));
2867 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2869 ERR("Failed to lock the source surface.\n");
2870 wined3d_texture_decref(ret);
2871 return NULL;
2873 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2875 ERR("Failed to lock the destination surface.\n");
2876 wined3d_surface_unmap(source);
2877 wined3d_texture_decref(ret);
2878 return NULL;
2881 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2882 source->resource.width, source->resource.height);
2884 wined3d_surface_unmap(dst);
2885 wined3d_surface_unmap(source);
2887 return ret;
2890 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2891 unsigned int bpp, UINT pitch, DWORD color)
2893 BYTE *first;
2894 unsigned int x, y;
2896 /* Do first row */
2898 #define COLORFILL_ROW(type) \
2899 do { \
2900 type *d = (type *)buf; \
2901 for (x = 0; x < width; ++x) \
2902 d[x] = (type)color; \
2903 } while(0)
2905 switch (bpp)
2907 case 1:
2908 COLORFILL_ROW(BYTE);
2909 break;
2911 case 2:
2912 COLORFILL_ROW(WORD);
2913 break;
2915 case 3:
2917 BYTE *d = buf;
2918 for (x = 0; x < width; ++x, d += 3)
2920 d[0] = (color ) & 0xff;
2921 d[1] = (color >> 8) & 0xff;
2922 d[2] = (color >> 16) & 0xff;
2924 break;
2926 case 4:
2927 COLORFILL_ROW(DWORD);
2928 break;
2930 default:
2931 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2932 return WINED3DERR_NOTAVAILABLE;
2935 #undef COLORFILL_ROW
2937 /* Now copy first row. */
2938 first = buf;
2939 for (y = 1; y < height; ++y)
2941 buf += pitch;
2942 memcpy(buf, first, width * bpp);
2945 return WINED3D_OK;
2948 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2950 return surface_from_resource(resource);
2953 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2955 TRACE("surface %p.\n", surface);
2957 if (!surface->resource.map_count)
2959 WARN("Trying to unmap unmapped surface.\n");
2960 return WINEDDERR_NOTLOCKED;
2962 --surface->resource.map_count;
2964 surface->surface_ops->surface_unmap(surface);
2966 return WINED3D_OK;
2969 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2970 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
2972 const struct wined3d_format *format = surface->resource.format;
2973 struct wined3d_device *device = surface->resource.device;
2974 struct wined3d_context *context;
2975 const struct wined3d_gl_info *gl_info;
2976 BYTE *base_memory;
2978 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
2979 surface, map_desc, wine_dbgstr_rect(rect), flags);
2981 if (surface->resource.map_count)
2983 WARN("Surface is already mapped.\n");
2984 return WINED3DERR_INVALIDCALL;
2987 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
2988 && !surface_check_block_align(surface, rect))
2990 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
2991 wine_dbgstr_rect(rect), format->block_width, format->block_height);
2993 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2994 return WINED3DERR_INVALIDCALL;
2997 ++surface->resource.map_count;
2999 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
3000 WARN("Trying to lock unlockable surface.\n");
3002 /* Performance optimization: Count how often a surface is mapped, if it is
3003 * mapped regularly do not throw away the system memory copy. This avoids
3004 * the need to download the surface from OpenGL all the time. The surface
3005 * is still downloaded if the OpenGL texture is changed. */
3006 if (!(surface->flags & SFLAG_DYNLOCK) && surface->map_binding == WINED3D_LOCATION_SYSMEM)
3008 if (++surface->lockCount > MAXLOCKCOUNT)
3010 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3011 surface->flags |= SFLAG_DYNLOCK;
3015 surface_prepare_map_memory(surface);
3016 if (flags & WINED3D_MAP_DISCARD)
3018 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
3019 wined3d_debug_location(surface->map_binding));
3020 surface_validate_location(surface, surface->map_binding);
3022 else
3024 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
3025 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
3027 surface_load_location(surface, surface->map_binding);
3030 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
3031 surface_invalidate_location(surface, ~surface->map_binding);
3033 switch (surface->map_binding)
3035 case WINED3D_LOCATION_SYSMEM:
3036 base_memory = surface->resource.heap_memory;
3037 break;
3039 case WINED3D_LOCATION_USER_MEMORY:
3040 base_memory = surface->user_memory;
3041 break;
3043 case WINED3D_LOCATION_DIB:
3044 base_memory = surface->dib.bitmap_data;
3045 break;
3047 case WINED3D_LOCATION_BUFFER:
3048 context = context_acquire(device, NULL);
3049 gl_info = context->gl_info;
3051 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
3052 base_memory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3053 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
3054 checkGLcall("map PBO");
3056 context_release(context);
3057 break;
3059 default:
3060 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->map_binding));
3061 base_memory = NULL;
3064 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3065 map_desc->row_pitch = surface->resource.width * format->byte_count;
3066 else
3067 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
3068 map_desc->slice_pitch = 0;
3070 if (!rect)
3072 map_desc->data = base_memory;
3073 surface->lockedRect.left = 0;
3074 surface->lockedRect.top = 0;
3075 surface->lockedRect.right = surface->resource.width;
3076 surface->lockedRect.bottom = surface->resource.height;
3078 else
3080 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3082 /* Compressed textures are block based, so calculate the offset of
3083 * the block that contains the top-left pixel of the locked rectangle. */
3084 map_desc->data = base_memory
3085 + ((rect->top / format->block_height) * map_desc->row_pitch)
3086 + ((rect->left / format->block_width) * format->block_byte_count);
3088 else
3090 map_desc->data = base_memory
3091 + (map_desc->row_pitch * rect->top)
3092 + (rect->left * format->byte_count);
3094 surface->lockedRect.left = rect->left;
3095 surface->lockedRect.top = rect->top;
3096 surface->lockedRect.right = rect->right;
3097 surface->lockedRect.bottom = rect->bottom;
3100 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3101 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
3103 return WINED3D_OK;
3106 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3108 HRESULT hr;
3110 TRACE("surface %p, dc %p.\n", surface, dc);
3112 /* Give more detailed info for ddraw. */
3113 if (surface->flags & SFLAG_DCINUSE)
3114 return WINEDDERR_DCALREADYCREATED;
3116 /* Can't GetDC if the surface is locked. */
3117 if (surface->resource.map_count)
3118 return WINED3DERR_INVALIDCALL;
3120 /* Create a DIB section if there isn't a dc yet. */
3121 if (!surface->hDC)
3123 if (surface->flags & SFLAG_CLIENT)
3125 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
3126 surface_release_client_storage(surface);
3128 hr = surface_create_dib_section(surface);
3129 if (FAILED(hr))
3130 return WINED3DERR_INVALIDCALL;
3131 if (!(surface->map_binding == WINED3D_LOCATION_USER_MEMORY
3132 || surface->flags & SFLAG_PIN_SYSMEM
3133 || surface->pbo))
3134 surface->map_binding = WINED3D_LOCATION_DIB;
3137 surface_load_location(surface, WINED3D_LOCATION_DIB);
3138 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
3140 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3141 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3143 /* GetDC on palettized formats is unsupported in D3D9, and the method
3144 * is missing in D3D8, so this should only be used for DX <=7
3145 * surfaces (with non-device palettes). */
3146 const RGBQUAD *colors = NULL;
3148 if (surface->palette)
3150 colors = surface->palette->colors;
3152 else
3154 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3155 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3157 if (dds_primary && dds_primary->palette)
3158 colors = dds_primary->palette->colors;
3161 if (colors)
3162 SetDIBColorTable(surface->hDC, 0, 256, colors);
3165 surface->flags |= SFLAG_DCINUSE;
3166 surface->resource.map_count++;
3168 *dc = surface->hDC;
3169 TRACE("Returning dc %p.\n", *dc);
3171 return WINED3D_OK;
3174 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3176 TRACE("surface %p, dc %p.\n", surface, dc);
3178 if (!(surface->flags & SFLAG_DCINUSE))
3179 return WINEDDERR_NODC;
3181 if (surface->hDC != dc)
3183 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3184 dc, surface->hDC);
3185 return WINEDDERR_NODC;
3188 surface->resource.map_count--;
3189 surface->flags &= ~SFLAG_DCINUSE;
3191 if (surface->map_binding == WINED3D_LOCATION_USER_MEMORY || (surface->flags & SFLAG_PIN_SYSMEM
3192 && surface->map_binding != WINED3D_LOCATION_DIB))
3194 /* The game Salammbo modifies the surface contents without mapping the surface between
3195 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
3196 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
3197 * Do not only copy the DIB to the map location, but also make sure the map location is
3198 * copied back to the DIB in the next getdc call.
3200 * The same consideration applies to user memory surfaces. */
3201 surface_load_location(surface, surface->map_binding);
3202 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
3205 return WINED3D_OK;
3208 static void read_from_framebuffer(struct wined3d_surface *surface, DWORD dst_location)
3210 struct wined3d_device *device = surface->resource.device;
3211 const struct wined3d_gl_info *gl_info;
3212 struct wined3d_context *context;
3213 BYTE *mem;
3214 GLint fmt;
3215 GLint type;
3216 BYTE *row, *top, *bottom;
3217 int i;
3218 BOOL srcIsUpsideDown;
3219 struct wined3d_bo_address data;
3221 surface_get_memory(surface, &data, dst_location);
3223 context = context_acquire(device, surface);
3224 context_apply_blit_state(context, device);
3225 gl_info = context->gl_info;
3227 /* Select the correct read buffer, and give some debug output.
3228 * There is no need to keep track of the current read buffer or reset it, every part of the code
3229 * that reads sets the read buffer as desired.
3231 if (surface_is_offscreen(surface))
3233 /* Mapping the primary render target which is not on a swapchain.
3234 * Read from the back buffer. */
3235 TRACE("Mapping offscreen render target.\n");
3236 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3237 srcIsUpsideDown = TRUE;
3239 else
3241 /* Onscreen surfaces are always part of a swapchain */
3242 GLenum buffer = surface_get_gl_buffer(surface);
3243 TRACE("Mapping %#x buffer.\n", buffer);
3244 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
3245 checkGLcall("glReadBuffer");
3246 srcIsUpsideDown = FALSE;
3249 switch (surface->resource.format->id)
3251 case WINED3DFMT_P8_UINT:
3252 fmt = GL_ALPHA;
3253 type = GL_UNSIGNED_BYTE;
3254 break;
3256 default:
3257 fmt = surface->resource.format->glFormat;
3258 type = surface->resource.format->glType;
3261 if (data.buffer_object)
3263 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
3264 checkGLcall("glBindBufferARB");
3267 /* Setup pixel store pack state -- to glReadPixels into the correct place */
3268 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
3269 checkGLcall("glPixelStorei");
3271 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
3272 surface->resource.width, surface->resource.height,
3273 fmt, type, data.addr);
3274 checkGLcall("glReadPixels");
3276 /* Reset previous pixel store pack state */
3277 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
3278 checkGLcall("glPixelStorei");
3280 if (!srcIsUpsideDown)
3282 /* glReadPixels returns the image upside down, and there is no way to prevent this.
3283 * Flip the lines in software. */
3284 UINT pitch = wined3d_surface_get_pitch(surface);
3286 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
3287 goto error;
3289 if (data.buffer_object)
3291 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3292 checkGLcall("glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB)");
3294 else
3295 mem = data.addr;
3297 top = mem;
3298 bottom = mem + pitch * (surface->resource.height - 1);
3299 for (i = 0; i < surface->resource.height / 2; i++)
3301 memcpy(row, top, pitch);
3302 memcpy(top, bottom, pitch);
3303 memcpy(bottom, row, pitch);
3304 top += pitch;
3305 bottom -= pitch;
3307 HeapFree(GetProcessHeap(), 0, row);
3309 if (data.buffer_object)
3310 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB));
3313 error:
3314 if (data.buffer_object)
3316 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
3317 checkGLcall("glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0)");
3320 context_release(context);
3323 /* Read the framebuffer contents into a texture. Note that this function
3324 * doesn't do any kind of flipping. Using this on an onscreen surface will
3325 * result in a flipped D3D texture. */
3326 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
3328 struct wined3d_device *device = surface->resource.device;
3329 const struct wined3d_gl_info *gl_info;
3330 struct wined3d_context *context;
3332 context = context_acquire(device, surface);
3333 gl_info = context->gl_info;
3334 device_invalidate_state(device, STATE_FRAMEBUFFER);
3336 surface_prepare_texture(surface, context, srgb);
3337 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3339 TRACE("Reading back offscreen render target %p.\n", surface);
3341 if (surface_is_offscreen(surface))
3342 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3343 else
3344 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
3345 checkGLcall("glReadBuffer");
3347 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
3348 0, 0, 0, 0, surface->resource.width, surface->resource.height);
3349 checkGLcall("glCopyTexSubImage2D");
3351 context_release(context);
3354 /* Context activation is done by the caller. */
3355 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
3356 struct wined3d_context *context, BOOL srgb)
3358 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
3359 enum wined3d_conversion_type convert;
3360 struct wined3d_format format;
3362 if (surface->flags & alloc_flag) return;
3364 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
3365 if (convert != WINED3D_CT_NONE || format.convert)
3366 surface->flags |= SFLAG_CONVERTED;
3367 else surface->flags &= ~SFLAG_CONVERTED;
3369 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3370 surface_allocate_surface(surface, context->gl_info, &format, srgb);
3371 surface->flags |= alloc_flag;
3374 /* Context activation is done by the caller. */
3375 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
3377 struct wined3d_texture *texture = surface->container;
3378 UINT sub_count = texture->level_count * texture->layer_count;
3379 UINT i;
3381 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
3383 for (i = 0; i < sub_count; ++i)
3385 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
3386 surface_prepare_texture_internal(s, context, srgb);
3389 return;
3392 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
3394 if (multisample)
3396 if (surface->rb_multisample)
3397 return;
3399 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
3400 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
3401 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
3402 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
3403 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
3405 else
3407 if (surface->rb_resolved)
3408 return;
3410 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
3411 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
3412 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
3413 surface->pow2Width, surface->pow2Height);
3414 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
3418 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
3420 /* FIXME: Is this really how color keys are supposed to work? I think it
3421 * makes more sense to compare the individual channels. */
3422 return color >= color_key->color_space_low_value
3423 && color <= color_key->color_space_high_value;
3426 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4])
3428 const struct wined3d_palette *pal = surface->palette;
3429 unsigned int i;
3431 if (!pal)
3433 FIXME("No palette set.\n");
3434 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
3435 * there's no palette at this time. */
3436 for (i = 0; i < 256; i++) table[i][3] = i;
3438 else
3440 TRACE("Using surface palette %p\n", pal);
3441 for (i = 0; i < 256; ++i)
3443 table[i][0] = pal->colors[i].rgbRed;
3444 table[i][1] = pal->colors[i].rgbGreen;
3445 table[i][2] = pal->colors[i].rgbBlue;
3446 /* The palette index is stored in the alpha component. In case of a
3447 * readback we can then read GL_ALPHA. Color keying is handled in
3448 * surface_blt_to_drawable() using a GL_ALPHA_TEST using GL_NOT_EQUAL.
3449 * In case of a P8 surface the color key itself is passed to
3450 * glAlphaFunc in other cases the alpha component of pixels that
3451 * should be masked away is set to 0. */
3452 table[i][3] = i;
3457 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
3458 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
3460 const BYTE *source;
3461 BYTE *dest;
3463 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
3464 src, dst, pitch, width, height, outpitch, conversion_type, surface);
3466 switch (conversion_type)
3468 case WINED3D_CT_NONE:
3470 memcpy(dst, src, pitch * height);
3471 break;
3474 case WINED3D_CT_PALETTED:
3476 BYTE table[256][4];
3477 unsigned int x, y;
3479 d3dfmt_p8_init_palette(surface, table);
3481 for (y = 0; y < height; y++)
3483 source = src + pitch * y;
3484 dest = dst + outpitch * y;
3485 /* This is an 1 bpp format, using the width here is fine */
3486 for (x = 0; x < width; x++) {
3487 BYTE color = *source++;
3488 *dest++ = table[color][0];
3489 *dest++ = table[color][1];
3490 *dest++ = table[color][2];
3491 *dest++ = table[color][3];
3495 break;
3497 case WINED3D_CT_CK_565:
3499 /* Converting the 565 format in 5551 packed to emulate color-keying.
3501 Note : in all these conversion, it would be best to average the averaging
3502 pixels to get the color of the pixel that will be color-keyed to
3503 prevent 'color bleeding'. This will be done later on if ever it is
3504 too visible.
3506 Note2: Nvidia documents say that their driver does not support alpha + color keying
3507 on the same surface and disables color keying in such a case
3509 unsigned int x, y;
3510 const WORD *Source;
3511 WORD *Dest;
3513 TRACE("Color keyed 565\n");
3515 for (y = 0; y < height; y++) {
3516 Source = (const WORD *)(src + y * pitch);
3517 Dest = (WORD *) (dst + y * outpitch);
3518 for (x = 0; x < width; x++ ) {
3519 WORD color = *Source++;
3520 *Dest = ((color & 0xffc0) | ((color & 0x1f) << 1));
3521 if (!color_in_range(&surface->container->src_blt_color_key, color))
3522 *Dest |= 0x0001;
3523 Dest++;
3527 break;
3529 case WINED3D_CT_CK_5551:
3531 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
3532 unsigned int x, y;
3533 const WORD *Source;
3534 WORD *Dest;
3535 TRACE("Color keyed 5551\n");
3536 for (y = 0; y < height; y++) {
3537 Source = (const WORD *)(src + y * pitch);
3538 Dest = (WORD *) (dst + y * outpitch);
3539 for (x = 0; x < width; x++ ) {
3540 WORD color = *Source++;
3541 *Dest = color;
3542 if (!color_in_range(&surface->container->src_blt_color_key, color))
3543 *Dest |= (1 << 15);
3544 else
3545 *Dest &= ~(1 << 15);
3546 Dest++;
3550 break;
3552 case WINED3D_CT_CK_RGB24:
3554 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
3555 unsigned int x, y;
3556 for (y = 0; y < height; y++)
3558 source = src + pitch * y;
3559 dest = dst + outpitch * y;
3560 for (x = 0; x < width; x++) {
3561 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
3562 DWORD dstcolor = color << 8;
3563 if (!color_in_range(&surface->container->src_blt_color_key, color))
3564 dstcolor |= 0xff;
3565 *(DWORD*)dest = dstcolor;
3566 source += 3;
3567 dest += 4;
3571 break;
3573 case WINED3D_CT_RGB32_888:
3575 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
3576 unsigned int x, y;
3577 for (y = 0; y < height; y++)
3579 source = src + pitch * y;
3580 dest = dst + outpitch * y;
3581 for (x = 0; x < width; x++) {
3582 DWORD color = 0xffffff & *(const DWORD*)source;
3583 DWORD dstcolor = color << 8;
3584 if (!color_in_range(&surface->container->src_blt_color_key, color))
3585 dstcolor |= 0xff;
3586 *(DWORD*)dest = dstcolor;
3587 source += 4;
3588 dest += 4;
3592 break;
3594 case WINED3D_CT_CK_ARGB32:
3596 unsigned int x, y;
3597 for (y = 0; y < height; ++y)
3599 source = src + pitch * y;
3600 dest = dst + outpitch * y;
3601 for (x = 0; x < width; ++x)
3603 DWORD color = *(const DWORD *)source;
3604 if (color_in_range(&surface->container->src_blt_color_key, color))
3605 color &= ~0xff000000;
3606 *(DWORD*)dest = color;
3607 source += 4;
3608 dest += 4;
3612 break;
3614 default:
3615 ERR("Unsupported conversion type %#x.\n", conversion_type);
3617 return WINED3D_OK;
3620 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
3622 if (front->container->level_count != 1 || front->container->layer_count != 1
3623 || back->container->level_count != 1 || back->container->layer_count != 1)
3624 ERR("Flip between surfaces %p and %p not supported.\n", front, back);
3626 /* Flip the surface contents */
3627 /* Flip the DC */
3629 HDC tmp;
3630 tmp = front->hDC;
3631 front->hDC = back->hDC;
3632 back->hDC = tmp;
3635 /* Flip the DIBsection */
3637 HBITMAP tmp = front->dib.DIBsection;
3638 front->dib.DIBsection = back->dib.DIBsection;
3639 back->dib.DIBsection = tmp;
3642 /* Flip the surface data */
3644 void* tmp;
3646 tmp = front->dib.bitmap_data;
3647 front->dib.bitmap_data = back->dib.bitmap_data;
3648 back->dib.bitmap_data = tmp;
3650 tmp = front->resource.heap_memory;
3651 front->resource.heap_memory = back->resource.heap_memory;
3652 back->resource.heap_memory = tmp;
3655 /* Flip the PBO */
3657 GLuint tmp_pbo = front->pbo;
3658 front->pbo = back->pbo;
3659 back->pbo = tmp_pbo;
3662 /* Flip the opengl texture */
3664 GLuint tmp;
3666 tmp = back->container->texture_rgb.name;
3667 back->container->texture_rgb.name = front->container->texture_rgb.name;
3668 front->container->texture_rgb.name = tmp;
3670 tmp = back->container->texture_srgb.name;
3671 back->container->texture_srgb.name = front->container->texture_srgb.name;
3672 front->container->texture_srgb.name = tmp;
3674 tmp = back->rb_multisample;
3675 back->rb_multisample = front->rb_multisample;
3676 front->rb_multisample = tmp;
3678 tmp = back->rb_resolved;
3679 back->rb_resolved = front->rb_resolved;
3680 front->rb_resolved = tmp;
3682 resource_unload(&back->resource);
3683 resource_unload(&front->resource);
3687 DWORD tmp_flags = back->flags;
3688 back->flags = front->flags;
3689 front->flags = tmp_flags;
3691 tmp_flags = back->locations;
3692 back->locations = front->locations;
3693 front->locations = tmp_flags;
3697 /* Does a direct frame buffer -> texture copy. Stretching is done with single
3698 * pixel copy calls. */
3699 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3700 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3702 struct wined3d_device *device = dst_surface->resource.device;
3703 const struct wined3d_gl_info *gl_info;
3704 float xrel, yrel;
3705 struct wined3d_context *context;
3706 BOOL upsidedown = FALSE;
3707 RECT dst_rect = *dst_rect_in;
3709 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3710 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3712 if(dst_rect.top > dst_rect.bottom) {
3713 UINT tmp = dst_rect.bottom;
3714 dst_rect.bottom = dst_rect.top;
3715 dst_rect.top = tmp;
3716 upsidedown = TRUE;
3719 context = context_acquire(device, src_surface);
3720 gl_info = context->gl_info;
3721 context_apply_blit_state(context, device);
3722 wined3d_texture_load(dst_surface->container, context, FALSE);
3724 /* Bind the target texture */
3725 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
3726 if (surface_is_offscreen(src_surface))
3728 TRACE("Reading from an offscreen target\n");
3729 upsidedown = !upsidedown;
3730 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3732 else
3734 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3736 checkGLcall("glReadBuffer");
3738 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3739 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3741 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3743 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3745 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3746 ERR("Texture filtering not supported in direct blit.\n");
3748 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3749 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3751 ERR("Texture filtering not supported in direct blit\n");
3754 if (upsidedown
3755 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3756 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3758 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
3759 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3760 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3761 src_rect->left, src_surface->resource.height - src_rect->bottom,
3762 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3764 else
3766 LONG row;
3767 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
3768 /* I have to process this row by row to swap the image,
3769 * otherwise it would be upside down, so stretching in y direction
3770 * doesn't cost extra time
3772 * However, stretching in x direction can be avoided if not necessary
3774 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3775 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3777 /* Well, that stuff works, but it's very slow.
3778 * find a better way instead
3780 LONG col;
3782 for (col = dst_rect.left; col < dst_rect.right; ++col)
3784 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3785 dst_rect.left + col /* x offset */, row /* y offset */,
3786 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3789 else
3791 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3792 dst_rect.left /* x offset */, row /* y offset */,
3793 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3797 checkGLcall("glCopyTexSubImage2D");
3799 context_release(context);
3801 /* The texture is now most up to date - If the surface is a render target
3802 * and has a drawable, this path is never entered. */
3803 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3804 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3807 /* Uses the hardware to stretch and flip the image */
3808 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3809 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3811 struct wined3d_device *device = dst_surface->resource.device;
3812 GLuint src, backup = 0;
3813 float left, right, top, bottom; /* Texture coordinates */
3814 UINT fbwidth = src_surface->resource.width;
3815 UINT fbheight = src_surface->resource.height;
3816 const struct wined3d_gl_info *gl_info;
3817 struct wined3d_context *context;
3818 GLenum drawBuffer = GL_BACK;
3819 GLenum texture_target;
3820 BOOL noBackBufferBackup;
3821 BOOL src_offscreen;
3822 BOOL upsidedown = FALSE;
3823 RECT dst_rect = *dst_rect_in;
3825 TRACE("Using hwstretch blit\n");
3826 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3827 context = context_acquire(device, src_surface);
3828 gl_info = context->gl_info;
3829 context_apply_blit_state(context, device);
3830 wined3d_texture_load(dst_surface->container, context, FALSE);
3832 src_offscreen = surface_is_offscreen(src_surface);
3833 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3834 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3836 /* Get it a description */
3837 wined3d_texture_load(src_surface->container, context, FALSE);
3840 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3841 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3843 if (context->aux_buffers >= 2)
3845 /* Got more than one aux buffer? Use the 2nd aux buffer */
3846 drawBuffer = GL_AUX1;
3848 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3850 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3851 drawBuffer = GL_AUX0;
3854 if (noBackBufferBackup)
3856 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3857 checkGLcall("glGenTextures");
3858 context_bind_texture(context, GL_TEXTURE_2D, backup);
3859 texture_target = GL_TEXTURE_2D;
3861 else
3863 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3864 * we are reading from the back buffer, the backup can be used as source texture
3866 texture_target = src_surface->texture_target;
3867 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3868 gl_info->gl_ops.gl.p_glEnable(texture_target);
3869 checkGLcall("glEnable(texture_target)");
3871 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3872 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3875 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3876 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3878 if(dst_rect.top > dst_rect.bottom) {
3879 UINT tmp = dst_rect.bottom;
3880 dst_rect.bottom = dst_rect.top;
3881 dst_rect.top = tmp;
3882 upsidedown = TRUE;
3885 if (src_offscreen)
3887 TRACE("Reading from an offscreen target\n");
3888 upsidedown = !upsidedown;
3889 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3891 else
3893 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3896 /* TODO: Only back up the part that will be overwritten */
3897 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3899 checkGLcall("glCopyTexSubImage2D");
3901 /* No issue with overriding these - the sampler is dirty due to blit usage */
3902 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3903 wined3d_gl_mag_filter(magLookup, filter));
3904 checkGLcall("glTexParameteri");
3905 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3906 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
3907 checkGLcall("glTexParameteri");
3909 if (!src_surface->swapchain || src_surface == src_surface->swapchain->back_buffers[0])
3911 src = backup ? backup : src_surface->container->texture_rgb.name;
3913 else
3915 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3916 checkGLcall("glReadBuffer(GL_FRONT)");
3918 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3919 checkGLcall("glGenTextures(1, &src)");
3920 context_bind_texture(context, GL_TEXTURE_2D, src);
3922 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3923 * out for power of 2 sizes
3925 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3926 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3927 checkGLcall("glTexImage2D");
3928 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3930 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3931 checkGLcall("glTexParameteri");
3932 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3933 checkGLcall("glTexParameteri");
3935 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3936 checkGLcall("glReadBuffer(GL_BACK)");
3938 if (texture_target != GL_TEXTURE_2D)
3940 gl_info->gl_ops.gl.p_glDisable(texture_target);
3941 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3942 texture_target = GL_TEXTURE_2D;
3945 checkGLcall("glEnd and previous");
3947 left = src_rect->left;
3948 right = src_rect->right;
3950 if (!upsidedown)
3952 top = src_surface->resource.height - src_rect->top;
3953 bottom = src_surface->resource.height - src_rect->bottom;
3955 else
3957 top = src_surface->resource.height - src_rect->bottom;
3958 bottom = src_surface->resource.height - src_rect->top;
3961 if (src_surface->flags & SFLAG_NORMCOORD)
3963 left /= src_surface->pow2Width;
3964 right /= src_surface->pow2Width;
3965 top /= src_surface->pow2Height;
3966 bottom /= src_surface->pow2Height;
3969 /* draw the source texture stretched and upside down. The correct surface is bound already */
3970 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3971 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3973 context_set_draw_buffer(context, drawBuffer);
3974 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3976 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3977 /* bottom left */
3978 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3979 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3981 /* top left */
3982 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3983 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3985 /* top right */
3986 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3987 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3989 /* bottom right */
3990 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3991 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3992 gl_info->gl_ops.gl.p_glEnd();
3993 checkGLcall("glEnd and previous");
3995 if (texture_target != dst_surface->texture_target)
3997 gl_info->gl_ops.gl.p_glDisable(texture_target);
3998 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3999 texture_target = dst_surface->texture_target;
4002 /* Now read the stretched and upside down image into the destination texture */
4003 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
4004 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
4006 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
4007 0, 0, /* We blitted the image to the origin */
4008 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4009 checkGLcall("glCopyTexSubImage2D");
4011 if (drawBuffer == GL_BACK)
4013 /* Write the back buffer backup back. */
4014 if (backup)
4016 if (texture_target != GL_TEXTURE_2D)
4018 gl_info->gl_ops.gl.p_glDisable(texture_target);
4019 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
4020 texture_target = GL_TEXTURE_2D;
4022 context_bind_texture(context, GL_TEXTURE_2D, backup);
4024 else
4026 if (texture_target != src_surface->texture_target)
4028 gl_info->gl_ops.gl.p_glDisable(texture_target);
4029 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
4030 texture_target = src_surface->texture_target;
4032 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
4035 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
4036 /* top left */
4037 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
4038 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
4040 /* bottom left */
4041 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
4042 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
4044 /* bottom right */
4045 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
4046 (float)fbheight / (float)src_surface->pow2Height);
4047 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
4049 /* top right */
4050 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
4051 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
4052 gl_info->gl_ops.gl.p_glEnd();
4054 gl_info->gl_ops.gl.p_glDisable(texture_target);
4055 checkGLcall("glDisable(texture_target)");
4057 /* Cleanup */
4058 if (src != src_surface->container->texture_rgb.name && src != backup)
4060 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
4061 checkGLcall("glDeleteTextures(1, &src)");
4063 if (backup)
4065 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
4066 checkGLcall("glDeleteTextures(1, &backup)");
4069 if (wined3d_settings.strict_draw_ordering)
4070 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4072 context_release(context);
4074 /* The texture is now most up to date - If the surface is a render target
4075 * and has a drawable, this path is never entered. */
4076 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
4077 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
4080 /* Front buffer coordinates are always full screen coordinates, but our GL
4081 * drawable is limited to the window's client area. The sysmem and texture
4082 * copies do have the full screen size. Note that GL has a bottom-left
4083 * origin, while D3D has a top-left origin. */
4084 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
4086 UINT drawable_height;
4088 if (surface->swapchain && surface == surface->swapchain->front_buffer)
4090 POINT offset = {0, 0};
4091 RECT windowsize;
4093 ScreenToClient(window, &offset);
4094 OffsetRect(rect, offset.x, offset.y);
4096 GetClientRect(window, &windowsize);
4097 drawable_height = windowsize.bottom - windowsize.top;
4099 else
4101 drawable_height = surface->resource.height;
4104 rect->top = drawable_height - rect->top;
4105 rect->bottom = drawable_height - rect->bottom;
4108 static void surface_blt_to_drawable(const struct wined3d_device *device,
4109 enum wined3d_texture_filter_type filter, BOOL alpha_test,
4110 struct wined3d_surface *src_surface, const RECT *src_rect_in,
4111 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
4113 const struct wined3d_gl_info *gl_info;
4114 struct wined3d_context *context;
4115 RECT src_rect, dst_rect;
4117 src_rect = *src_rect_in;
4118 dst_rect = *dst_rect_in;
4120 context = context_acquire(device, dst_surface);
4121 gl_info = context->gl_info;
4123 /* Make sure the surface is up-to-date. This should probably use
4124 * surface_load_location() and worry about the destination surface too,
4125 * unless we're overwriting it completely. */
4126 wined3d_texture_load(src_surface->container, context, FALSE);
4128 /* Activate the destination context, set it up for blitting */
4129 context_apply_blit_state(context, device);
4131 if (!surface_is_offscreen(dst_surface))
4132 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
4134 device->blitter->set_shader(device->blit_priv, context, src_surface);
4136 if (alpha_test)
4138 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
4139 checkGLcall("glEnable(GL_ALPHA_TEST)");
4141 /* For P8 surfaces, the alpha component contains the palette index.
4142 * Which means that the colorkey is one of the palette entries. In
4143 * other cases pixels that should be masked away have alpha set to 0. */
4144 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
4145 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
4146 (float)src_surface->container->src_blt_color_key.color_space_low_value / 256.0f);
4147 else
4148 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
4149 checkGLcall("glAlphaFunc");
4151 else
4153 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4154 checkGLcall("glDisable(GL_ALPHA_TEST)");
4157 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
4159 if (alpha_test)
4161 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4162 checkGLcall("glDisable(GL_ALPHA_TEST)");
4165 /* Leave the opengl state valid for blitting */
4166 device->blitter->unset_shader(context->gl_info);
4168 if (wined3d_settings.strict_draw_ordering
4169 || (dst_surface->swapchain && dst_surface->swapchain->front_buffer == dst_surface))
4170 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4172 context_release(context);
4175 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
4177 struct wined3d_device *device = s->resource.device;
4178 const struct blit_shader *blitter;
4180 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
4181 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
4182 if (!blitter)
4184 FIXME("No blitter is capable of performing the requested color fill operation.\n");
4185 return WINED3DERR_INVALIDCALL;
4188 return blitter->color_fill(device, s, rect, color);
4191 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4192 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
4193 enum wined3d_texture_filter_type filter)
4195 struct wined3d_device *device = dst_surface->resource.device;
4196 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4197 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
4199 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
4200 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4201 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
4203 /* Get the swapchain. One of the surfaces has to be a primary surface */
4204 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4206 WARN("Destination is in sysmem, rejecting gl blt\n");
4207 return WINED3DERR_INVALIDCALL;
4210 dst_swapchain = dst_surface->swapchain;
4212 if (src_surface)
4214 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4216 WARN("Src is in sysmem, rejecting gl blt\n");
4217 return WINED3DERR_INVALIDCALL;
4220 src_swapchain = src_surface->swapchain;
4222 else
4224 src_swapchain = NULL;
4227 /* Early sort out of cases where no render target is used */
4228 if (!dst_swapchain && !src_swapchain
4229 && src_surface != device->fb.render_targets[0]
4230 && dst_surface != device->fb.render_targets[0])
4232 TRACE("No surface is render target, not using hardware blit.\n");
4233 return WINED3DERR_INVALIDCALL;
4236 /* No destination color keying supported */
4237 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
4239 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
4240 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
4241 return WINED3DERR_INVALIDCALL;
4244 if (dst_swapchain && dst_swapchain == src_swapchain)
4246 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
4247 return WINED3DERR_INVALIDCALL;
4250 if (dst_swapchain && src_swapchain)
4252 FIXME("Implement hardware blit between two different swapchains\n");
4253 return WINED3DERR_INVALIDCALL;
4256 if (dst_swapchain)
4258 /* Handled with regular texture -> swapchain blit */
4259 if (src_surface == device->fb.render_targets[0])
4260 TRACE("Blit from active render target to a swapchain\n");
4262 else if (src_swapchain && dst_surface == device->fb.render_targets[0])
4264 FIXME("Implement blit from a swapchain to the active render target\n");
4265 return WINED3DERR_INVALIDCALL;
4268 if ((src_swapchain || src_surface == device->fb.render_targets[0]) && !dst_swapchain)
4270 /* Blit from render target to texture */
4271 BOOL stretchx;
4273 /* P8 read back is not implemented */
4274 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
4275 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
4277 TRACE("P8 read back not supported by frame buffer to texture blit\n");
4278 return WINED3DERR_INVALIDCALL;
4281 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
4283 TRACE("Color keying not supported by frame buffer to texture blit\n");
4284 return WINED3DERR_INVALIDCALL;
4285 /* Destination color key is checked above */
4288 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
4289 stretchx = TRUE;
4290 else
4291 stretchx = FALSE;
4293 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
4294 * flip the image nor scale it.
4296 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
4297 * -> If the app wants an image width an unscaled width, copy it line per line
4298 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
4299 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
4300 * back buffer. This is slower than reading line per line, thus not used for flipping
4301 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
4302 * pixel by pixel. */
4303 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
4304 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
4306 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
4307 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
4309 else
4311 TRACE("Using hardware stretching to flip / stretch the texture.\n");
4312 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
4315 surface_evict_sysmem(dst_surface);
4317 return WINED3D_OK;
4319 else if (src_surface)
4321 /* Blit from offscreen surface to render target */
4322 struct wined3d_color_key old_blt_key = src_surface->container->src_blt_color_key;
4323 DWORD old_color_key_flags = src_surface->container->color_key_flags;
4325 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4327 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4328 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
4329 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
4331 FIXME("Unsupported blit operation falling back to software\n");
4332 return WINED3DERR_INVALIDCALL;
4335 /* Color keying: Check if we have to do a color keyed blt,
4336 * and if not check if a color key is activated.
4338 * Just modify the color keying parameters in the surface and restore them afterwards
4339 * The surface keeps track of the color key last used to load the opengl surface.
4340 * PreLoad will catch the change to the flags and color key and reload if necessary.
4342 if (flags & WINEDDBLT_KEYSRC)
4344 /* Use color key from surface */
4346 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4348 /* Use color key from DDBltFx */
4349 src_surface->container->color_key_flags |= WINEDDSD_CKSRCBLT;
4350 src_surface->container->src_blt_color_key = DDBltFx->ddckSrcColorkey;
4352 else
4354 /* Do not use color key */
4355 src_surface->container->color_key_flags &= ~WINEDDSD_CKSRCBLT;
4358 surface_blt_to_drawable(device, filter,
4359 flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_ALPHATEST),
4360 src_surface, src_rect, dst_surface, dst_rect);
4362 /* Restore the color key parameters */
4363 src_surface->container->color_key_flags = old_color_key_flags;
4364 src_surface->container->src_blt_color_key = old_blt_key;
4366 surface_validate_location(dst_surface, dst_surface->draw_binding);
4367 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
4369 return WINED3D_OK;
4372 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
4373 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
4374 return WINED3DERR_INVALIDCALL;
4377 /* Context activation is done by the caller. */
4378 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
4379 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
4381 struct wined3d_device *device = surface->resource.device;
4382 const struct wined3d_gl_info *gl_info = context->gl_info;
4383 GLint compare_mode = GL_NONE;
4384 struct blt_info info;
4385 GLint old_binding = 0;
4386 RECT rect;
4388 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4390 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
4391 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
4392 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4393 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
4394 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
4395 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
4396 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
4397 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
4398 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4399 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
4400 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
4402 SetRect(&rect, 0, h, w, 0);
4403 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
4404 context_active_texture(context, context->gl_info, 0);
4405 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
4406 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
4407 if (gl_info->supported[ARB_SHADOW])
4409 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
4410 if (compare_mode != GL_NONE)
4411 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
4414 device->shader_backend->shader_select_depth_blt(device->shader_priv,
4415 gl_info, info.tex_type, &surface->ds_current_size);
4417 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
4418 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
4419 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
4420 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
4421 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
4422 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
4423 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
4424 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
4425 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
4426 gl_info->gl_ops.gl.p_glEnd();
4428 if (compare_mode != GL_NONE)
4429 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
4430 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
4432 gl_info->gl_ops.gl.p_glPopAttrib();
4434 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
4437 void surface_modify_ds_location(struct wined3d_surface *surface,
4438 DWORD location, UINT w, UINT h)
4440 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
4442 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
4443 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
4444 wined3d_texture_set_dirty(surface->container);
4446 surface->ds_current_size.cx = w;
4447 surface->ds_current_size.cy = h;
4448 surface->locations = location;
4451 /* Context activation is done by the caller. */
4452 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4454 const struct wined3d_gl_info *gl_info = context->gl_info;
4455 struct wined3d_device *device = surface->resource.device;
4456 GLsizei w, h;
4458 TRACE("surface %p, new location %#x.\n", surface, location);
4460 /* TODO: Make this work for modes other than FBO */
4461 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4463 if (!(surface->locations & location))
4465 w = surface->ds_current_size.cx;
4466 h = surface->ds_current_size.cy;
4467 surface->ds_current_size.cx = 0;
4468 surface->ds_current_size.cy = 0;
4470 else
4472 w = surface->resource.width;
4473 h = surface->resource.height;
4476 if (surface->ds_current_size.cx == surface->resource.width
4477 && surface->ds_current_size.cy == surface->resource.height)
4479 TRACE("Location (%#x) is already up to date.\n", location);
4480 return;
4483 if (surface->current_renderbuffer)
4485 FIXME("Not supported with fixed up depth stencil.\n");
4486 return;
4489 if (surface->locations & WINED3D_LOCATION_DISCARDED)
4491 TRACE("Surface was discarded, no need copy data.\n");
4492 switch (location)
4494 case WINED3D_LOCATION_TEXTURE_RGB:
4495 surface_prepare_texture(surface, context, FALSE);
4496 break;
4497 case WINED3D_LOCATION_RB_MULTISAMPLE:
4498 surface_prepare_rb(surface, gl_info, TRUE);
4499 break;
4500 case WINED3D_LOCATION_DRAWABLE:
4501 /* Nothing to do */
4502 break;
4503 default:
4504 FIXME("Unhandled location %#x\n", location);
4506 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
4507 surface->locations |= location;
4508 surface->ds_current_size.cx = surface->resource.width;
4509 surface->ds_current_size.cy = surface->resource.height;
4510 return;
4513 if (!surface->locations)
4515 FIXME("No up to date depth stencil location.\n");
4516 surface->locations |= location;
4517 surface->ds_current_size.cx = surface->resource.width;
4518 surface->ds_current_size.cy = surface->resource.height;
4519 return;
4522 if (location == WINED3D_LOCATION_TEXTURE_RGB)
4524 GLint old_binding = 0;
4525 GLenum bind_target;
4527 /* The render target is allowed to be smaller than the depth/stencil
4528 * buffer, so the onscreen depth/stencil buffer is potentially smaller
4529 * than the offscreen surface. Don't overwrite the offscreen surface
4530 * with undefined data. */
4531 w = min(w, context->swapchain->desc.backbuffer_width);
4532 h = min(h, context->swapchain->desc.backbuffer_height);
4534 TRACE("Copying onscreen depth buffer to depth texture.\n");
4536 if (!device->depth_blt_texture)
4537 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
4539 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4540 * directly on the FBO texture. That's because we need to flip. */
4541 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4542 context->swapchain->front_buffer, NULL, WINED3D_LOCATION_DRAWABLE);
4543 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4545 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4546 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4548 else
4550 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4551 bind_target = GL_TEXTURE_2D;
4553 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
4554 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
4555 * internal format, because the internal format might include stencil
4556 * data. In principle we should copy stencil data as well, but unless
4557 * the driver supports stencil export it's hard to do, and doesn't
4558 * seem to be needed in practice. If the hardware doesn't support
4559 * writing stencil data, the glCopyTexImage2D() call might trigger
4560 * software fallbacks. */
4561 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
4562 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4563 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4564 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4565 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4566 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4567 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4568 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
4570 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4571 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
4572 context_set_draw_buffer(context, GL_NONE);
4574 /* Do the actual blit */
4575 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
4576 checkGLcall("depth_blt");
4578 context_invalidate_state(context, STATE_FRAMEBUFFER);
4580 if (wined3d_settings.strict_draw_ordering)
4581 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4583 else if (location == WINED3D_LOCATION_DRAWABLE)
4585 TRACE("Copying depth texture to onscreen depth buffer.\n");
4587 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4588 context->swapchain->front_buffer, NULL, WINED3D_LOCATION_DRAWABLE);
4589 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
4590 0, surface->pow2Height - h, w, h, surface->texture_target);
4591 checkGLcall("depth_blt");
4593 context_invalidate_state(context, STATE_FRAMEBUFFER);
4595 if (wined3d_settings.strict_draw_ordering)
4596 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4598 else
4600 ERR("Invalid location (%#x) specified.\n", location);
4603 surface->locations |= location;
4604 surface->ds_current_size.cx = surface->resource.width;
4605 surface->ds_current_size.cy = surface->resource.height;
4608 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
4610 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4612 surface->locations |= location;
4615 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
4617 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4619 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4620 wined3d_texture_set_dirty(surface->container);
4621 surface->locations &= ~location;
4623 if (!surface->locations)
4624 ERR("Surface %p does not have any up to date location.\n", surface);
4627 static DWORD resource_access_from_location(DWORD location)
4629 switch (location)
4631 case WINED3D_LOCATION_SYSMEM:
4632 case WINED3D_LOCATION_USER_MEMORY:
4633 case WINED3D_LOCATION_DIB:
4634 case WINED3D_LOCATION_BUFFER:
4635 return WINED3D_RESOURCE_ACCESS_CPU;
4637 case WINED3D_LOCATION_DRAWABLE:
4638 case WINED3D_LOCATION_TEXTURE_SRGB:
4639 case WINED3D_LOCATION_TEXTURE_RGB:
4640 case WINED3D_LOCATION_RB_MULTISAMPLE:
4641 case WINED3D_LOCATION_RB_RESOLVED:
4642 return WINED3D_RESOURCE_ACCESS_GPU;
4644 default:
4645 FIXME("Unhandled location %#x.\n", location);
4646 return 0;
4650 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
4652 struct wined3d_device *device = surface->resource.device;
4653 struct wined3d_context *context;
4654 const struct wined3d_gl_info *gl_info;
4655 struct wined3d_bo_address dst, src;
4656 UINT size = surface->resource.size;
4658 surface_get_memory(surface, &dst, location);
4659 surface_get_memory(surface, &src, surface->locations);
4661 if (dst.buffer_object)
4663 context = context_acquire(device, NULL);
4664 gl_info = context->gl_info;
4665 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, dst.buffer_object));
4666 GL_EXTCALL(glBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, size, src.addr));
4667 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4668 checkGLcall("Upload PBO");
4669 context_release(context);
4670 return;
4672 if (src.buffer_object)
4674 context = context_acquire(device, NULL);
4675 gl_info = context->gl_info;
4676 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, src.buffer_object));
4677 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_PACK_BUFFER_ARB, 0, size, dst.addr));
4678 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4679 checkGLcall("Download PBO");
4680 context_release(context);
4681 return;
4683 memcpy(dst.addr, src.addr, size);
4686 static void surface_load_sysmem(struct wined3d_surface *surface,
4687 const struct wined3d_gl_info *gl_info, DWORD dst_location)
4689 if (surface->locations & surface_simple_locations)
4691 surface_copy_simple_location(surface, dst_location);
4692 return;
4695 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
4696 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4698 /* Download the surface to system memory. */
4699 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4701 struct wined3d_device *device = surface->resource.device;
4702 struct wined3d_context *context;
4704 /* TODO: Use already acquired context when possible. */
4705 context = context_acquire(device, NULL);
4707 wined3d_texture_bind_and_dirtify(surface->container, context,
4708 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
4709 surface_download_data(surface, gl_info, dst_location);
4711 context_release(context);
4713 return;
4716 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
4718 read_from_framebuffer(surface, dst_location);
4719 return;
4722 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
4723 surface, wined3d_debug_location(surface->locations));
4726 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
4727 const struct wined3d_gl_info *gl_info)
4729 RECT r;
4731 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
4733 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
4734 return WINED3DERR_INVALIDCALL;
4737 surface_get_rect(surface, NULL, &r);
4738 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4739 surface_blt_to_drawable(surface->resource.device,
4740 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
4742 return WINED3D_OK;
4745 static HRESULT surface_load_texture(struct wined3d_surface *surface,
4746 const struct wined3d_gl_info *gl_info, BOOL srgb)
4748 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
4749 struct wined3d_device *device = surface->resource.device;
4750 enum wined3d_conversion_type convert;
4751 struct wined3d_context *context;
4752 UINT width, src_pitch, dst_pitch;
4753 struct wined3d_bo_address data;
4754 struct wined3d_format format;
4755 POINT dst_point = {0, 0};
4756 BYTE *mem = NULL;
4758 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
4759 && surface_is_offscreen(surface)
4760 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
4762 surface_load_fb_texture(surface, srgb);
4764 return WINED3D_OK;
4767 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
4768 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
4769 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4770 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4771 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4773 if (srgb)
4774 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
4775 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
4776 else
4777 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
4778 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
4780 return WINED3D_OK;
4783 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
4784 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
4785 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4786 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4787 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4789 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
4790 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
4791 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
4792 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4794 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
4795 &rect, surface, dst_location, &rect);
4797 return WINED3D_OK;
4800 /* Upload from system memory */
4802 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
4803 TRUE /* We will use textures */, &format, &convert);
4805 if (srgb)
4807 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->map_binding))
4808 == WINED3D_LOCATION_TEXTURE_RGB)
4810 /* Performance warning... */
4811 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
4812 surface_prepare_map_memory(surface);
4813 surface_load_location(surface, surface->map_binding);
4816 else
4818 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->map_binding))
4819 == WINED3D_LOCATION_TEXTURE_SRGB)
4821 /* Performance warning... */
4822 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
4823 surface_prepare_map_memory(surface);
4824 surface_load_location(surface, surface->map_binding);
4828 if (!(surface->locations & surface_simple_locations))
4830 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
4831 /* Lets hope we get it from somewhere... */
4832 surface_prepare_system_memory(surface);
4833 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
4836 /* TODO: Use already acquired context when possible. */
4837 context = context_acquire(device, NULL);
4839 surface_prepare_texture(surface, context, srgb);
4840 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
4842 if (surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
4844 surface->flags |= SFLAG_GLCKEY;
4845 surface->gl_color_key = surface->container->src_blt_color_key;
4847 else surface->flags &= ~SFLAG_GLCKEY;
4849 width = surface->resource.width;
4850 src_pitch = wined3d_surface_get_pitch(surface);
4852 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
4853 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
4854 * called. */
4855 if ((convert != WINED3D_CT_NONE || format.convert) && surface->pbo)
4857 TRACE("Removing the pbo attached to surface %p.\n", surface);
4859 if (surface->flags & SFLAG_DIBSECTION)
4860 surface->map_binding = WINED3D_LOCATION_DIB;
4861 else
4862 surface->map_binding = WINED3D_LOCATION_SYSMEM;
4864 surface_prepare_map_memory(surface);
4865 surface_load_location(surface, surface->map_binding);
4866 surface_remove_pbo(surface, gl_info);
4869 surface_get_memory(surface, &data, surface->locations);
4870 if (format.convert)
4872 /* This code is entered for texture formats which need a fixup. */
4873 UINT height = surface->resource.height;
4875 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4876 dst_pitch = width * format.conv_byte_count;
4877 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4879 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4881 ERR("Out of memory (%u).\n", dst_pitch * height);
4882 context_release(context);
4883 return E_OUTOFMEMORY;
4885 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4886 dst_pitch, dst_pitch * height, width, height, 1);
4887 format.byte_count = format.conv_byte_count;
4888 src_pitch = dst_pitch;
4889 data.addr = mem;
4891 else if (convert != WINED3D_CT_NONE)
4893 /* This code is only entered for color keying fixups */
4894 UINT height = surface->resource.height;
4896 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4897 dst_pitch = width * format.conv_byte_count;
4898 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4900 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4902 ERR("Out of memory (%u).\n", dst_pitch * height);
4903 context_release(context);
4904 return E_OUTOFMEMORY;
4906 d3dfmt_convert_surface(data.addr, mem, src_pitch,
4907 width, height, dst_pitch, convert, surface);
4908 format.byte_count = format.conv_byte_count;
4909 src_pitch = dst_pitch;
4910 data.addr = mem;
4913 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
4915 context_release(context);
4917 HeapFree(GetProcessHeap(), 0, mem);
4919 return WINED3D_OK;
4922 static void surface_multisample_resolve(struct wined3d_surface *surface)
4924 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4926 if (!(surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE))
4927 ERR("Trying to resolve multisampled surface %p, but location WINED3D_LOCATION_RB_MULTISAMPLE not current.\n",
4928 surface);
4930 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
4931 surface, WINED3D_LOCATION_RB_MULTISAMPLE, &rect, surface, WINED3D_LOCATION_RB_RESOLVED, &rect);
4934 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
4936 struct wined3d_device *device = surface->resource.device;
4937 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4938 HRESULT hr;
4940 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4942 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4944 if (location == WINED3D_LOCATION_TEXTURE_RGB
4945 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4947 struct wined3d_context *context = context_acquire(device, NULL);
4948 surface_load_ds_location(surface, context, location);
4949 context_release(context);
4950 return WINED3D_OK;
4952 else if (location & surface->locations && surface->draw_binding != WINED3D_LOCATION_DRAWABLE)
4954 /* Already up to date, nothing to do. */
4955 return WINED3D_OK;
4957 else
4959 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4960 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4961 return WINED3DERR_INVALIDCALL;
4965 if (surface->locations & location)
4967 TRACE("Location already up to date.\n");
4968 return WINED3D_OK;
4971 if (WARN_ON(d3d_surface))
4973 DWORD required_access = resource_access_from_location(location);
4974 if ((surface->resource.access_flags & required_access) != required_access)
4975 WARN("Operation requires %#x access, but surface only has %#x.\n",
4976 required_access, surface->resource.access_flags);
4979 if (!surface->locations)
4981 ERR("Surface %p does not have any up to date location.\n", surface);
4982 surface->flags |= SFLAG_LOST;
4983 return WINED3DERR_DEVICELOST;
4986 switch (location)
4988 case WINED3D_LOCATION_DIB:
4989 case WINED3D_LOCATION_USER_MEMORY:
4990 case WINED3D_LOCATION_SYSMEM:
4991 case WINED3D_LOCATION_BUFFER:
4992 surface_load_sysmem(surface, gl_info, location);
4993 break;
4995 case WINED3D_LOCATION_DRAWABLE:
4996 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
4997 return hr;
4998 break;
5000 case WINED3D_LOCATION_RB_RESOLVED:
5001 surface_multisample_resolve(surface);
5002 break;
5004 case WINED3D_LOCATION_TEXTURE_RGB:
5005 case WINED3D_LOCATION_TEXTURE_SRGB:
5006 if (FAILED(hr = surface_load_texture(surface, gl_info, location == WINED3D_LOCATION_TEXTURE_SRGB)))
5007 return hr;
5008 break;
5010 default:
5011 ERR("Don't know how to handle location %#x.\n", location);
5012 break;
5015 surface_validate_location(surface, location);
5017 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
5018 surface_evict_sysmem(surface);
5020 return WINED3D_OK;
5023 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
5025 struct wined3d_swapchain *swapchain;
5027 /* Not on a swapchain - must be offscreen */
5028 if (!(swapchain = surface->swapchain))
5029 return TRUE;
5031 /* The front buffer is always onscreen */
5032 if (surface == swapchain->front_buffer) return FALSE;
5034 /* If the swapchain is rendered to an FBO, the backbuffer is
5035 * offscreen, otherwise onscreen */
5036 return swapchain->render_to_fbo;
5039 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
5040 /* Context activation is done by the caller. */
5041 static void ffp_blit_free(struct wined3d_device *device) { }
5043 /* Context activation is done by the caller. */
5044 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5046 const struct wined3d_gl_info *gl_info = context->gl_info;
5048 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
5049 checkGLcall("glEnable(target)");
5051 return WINED3D_OK;
5054 /* Context activation is done by the caller. */
5055 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
5057 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
5058 checkGLcall("glDisable(GL_TEXTURE_2D)");
5059 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
5061 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5062 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5064 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
5066 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
5067 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5071 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5072 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5073 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5075 switch (blit_op)
5077 case WINED3D_BLIT_OP_COLOR_BLIT:
5078 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
5079 return FALSE;
5081 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5083 TRACE("Checking support for fixup:\n");
5084 dump_color_fixup_desc(src_format->color_fixup);
5087 /* We only support identity conversions. */
5088 if (!is_identity_fixup(src_format->color_fixup)
5089 || !is_identity_fixup(dst_format->color_fixup))
5091 TRACE("Fixups are not supported.\n");
5092 return FALSE;
5095 return TRUE;
5097 case WINED3D_BLIT_OP_COLOR_FILL:
5098 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
5099 return FALSE;
5101 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5103 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
5104 return FALSE;
5106 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
5108 TRACE("Color fill not supported\n");
5109 return FALSE;
5112 /* FIXME: We should reject color fills on formats with fixups,
5113 * but this would break P8 color fills for example. */
5115 return TRUE;
5117 case WINED3D_BLIT_OP_DEPTH_FILL:
5118 return TRUE;
5120 default:
5121 TRACE("Unsupported blit_op=%d\n", blit_op);
5122 return FALSE;
5126 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5127 const RECT *dst_rect, const struct wined3d_color *color)
5129 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
5130 struct wined3d_fb_state fb = {&dst_surface, NULL};
5132 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
5134 return WINED3D_OK;
5137 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
5138 struct wined3d_surface *surface, const RECT *rect, float depth)
5140 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
5141 struct wined3d_fb_state fb = {NULL, surface};
5143 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
5145 return WINED3D_OK;
5148 const struct blit_shader ffp_blit = {
5149 ffp_blit_alloc,
5150 ffp_blit_free,
5151 ffp_blit_set,
5152 ffp_blit_unset,
5153 ffp_blit_supported,
5154 ffp_blit_color_fill,
5155 ffp_blit_depth_fill,
5158 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
5160 return WINED3D_OK;
5163 /* Context activation is done by the caller. */
5164 static void cpu_blit_free(struct wined3d_device *device)
5168 /* Context activation is done by the caller. */
5169 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5171 return WINED3D_OK;
5174 /* Context activation is done by the caller. */
5175 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
5179 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5180 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5181 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5183 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
5185 return TRUE;
5188 return FALSE;
5191 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
5192 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
5193 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
5195 UINT row_block_count;
5196 const BYTE *src_row;
5197 BYTE *dst_row;
5198 UINT x, y;
5200 src_row = src_data;
5201 dst_row = dst_data;
5203 row_block_count = (update_w + format->block_width - 1) / format->block_width;
5205 if (!flags)
5207 for (y = 0; y < update_h; y += format->block_height)
5209 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
5210 src_row += src_pitch;
5211 dst_row += dst_pitch;
5214 return WINED3D_OK;
5217 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
5219 src_row += (((update_h / format->block_height) - 1) * src_pitch);
5221 switch (format->id)
5223 case WINED3DFMT_DXT1:
5224 for (y = 0; y < update_h; y += format->block_height)
5226 struct block
5228 WORD color[2];
5229 BYTE control_row[4];
5232 const struct block *s = (const struct block *)src_row;
5233 struct block *d = (struct block *)dst_row;
5235 for (x = 0; x < row_block_count; ++x)
5237 d[x].color[0] = s[x].color[0];
5238 d[x].color[1] = s[x].color[1];
5239 d[x].control_row[0] = s[x].control_row[3];
5240 d[x].control_row[1] = s[x].control_row[2];
5241 d[x].control_row[2] = s[x].control_row[1];
5242 d[x].control_row[3] = s[x].control_row[0];
5244 src_row -= src_pitch;
5245 dst_row += dst_pitch;
5247 return WINED3D_OK;
5249 case WINED3DFMT_DXT2:
5250 case WINED3DFMT_DXT3:
5251 for (y = 0; y < update_h; y += format->block_height)
5253 struct block
5255 WORD alpha_row[4];
5256 WORD color[2];
5257 BYTE control_row[4];
5260 const struct block *s = (const struct block *)src_row;
5261 struct block *d = (struct block *)dst_row;
5263 for (x = 0; x < row_block_count; ++x)
5265 d[x].alpha_row[0] = s[x].alpha_row[3];
5266 d[x].alpha_row[1] = s[x].alpha_row[2];
5267 d[x].alpha_row[2] = s[x].alpha_row[1];
5268 d[x].alpha_row[3] = s[x].alpha_row[0];
5269 d[x].color[0] = s[x].color[0];
5270 d[x].color[1] = s[x].color[1];
5271 d[x].control_row[0] = s[x].control_row[3];
5272 d[x].control_row[1] = s[x].control_row[2];
5273 d[x].control_row[2] = s[x].control_row[1];
5274 d[x].control_row[3] = s[x].control_row[0];
5276 src_row -= src_pitch;
5277 dst_row += dst_pitch;
5279 return WINED3D_OK;
5281 default:
5282 FIXME("Compressed flip not implemented for format %s.\n",
5283 debug_d3dformat(format->id));
5284 return E_NOTIMPL;
5288 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
5289 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
5291 return E_NOTIMPL;
5294 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5295 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
5296 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5298 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
5299 const struct wined3d_format *src_format, *dst_format;
5300 struct wined3d_texture *src_texture = NULL;
5301 struct wined3d_map_desc dst_map, src_map;
5302 const BYTE *sbase = NULL;
5303 HRESULT hr = WINED3D_OK;
5304 const BYTE *sbuf;
5305 BYTE *dbuf;
5306 int x, y;
5308 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5309 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5310 flags, fx, debug_d3dtexturefiltertype(filter));
5312 if (src_surface == dst_surface)
5314 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
5315 src_map = dst_map;
5316 src_format = dst_surface->resource.format;
5317 dst_format = src_format;
5319 else
5321 dst_format = dst_surface->resource.format;
5322 if (src_surface)
5324 if (dst_surface->resource.format->id != src_surface->resource.format->id)
5326 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
5328 /* The conv function writes a FIXME */
5329 WARN("Cannot convert source surface format to dest format.\n");
5330 goto release;
5332 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
5334 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
5335 src_format = src_surface->resource.format;
5337 else
5339 src_format = dst_format;
5342 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
5345 bpp = dst_surface->resource.format->byte_count;
5346 srcheight = src_rect->bottom - src_rect->top;
5347 srcwidth = src_rect->right - src_rect->left;
5348 dstheight = dst_rect->bottom - dst_rect->top;
5349 dstwidth = dst_rect->right - dst_rect->left;
5350 width = (dst_rect->right - dst_rect->left) * bpp;
5352 if (src_surface)
5353 sbase = (BYTE *)src_map.data
5354 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
5355 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
5356 if (src_surface != dst_surface)
5357 dbuf = dst_map.data;
5358 else
5359 dbuf = (BYTE *)dst_map.data
5360 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
5361 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
5363 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
5365 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
5367 if (src_surface == dst_surface)
5369 FIXME("Only plain blits supported on compressed surfaces.\n");
5370 hr = E_NOTIMPL;
5371 goto release;
5374 if (srcheight != dstheight || srcwidth != dstwidth)
5376 WARN("Stretching not supported on compressed surfaces.\n");
5377 hr = WINED3DERR_INVALIDCALL;
5378 goto release;
5381 if (!surface_check_block_align(src_surface, src_rect))
5383 WARN("Source rectangle not block-aligned.\n");
5384 hr = WINED3DERR_INVALIDCALL;
5385 goto release;
5388 if (!surface_check_block_align(dst_surface, dst_rect))
5390 WARN("Destination rectangle not block-aligned.\n");
5391 hr = WINED3DERR_INVALIDCALL;
5392 goto release;
5395 hr = surface_cpu_blt_compressed(sbase, dbuf,
5396 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
5397 src_format, flags, fx);
5398 goto release;
5401 /* First, all the 'source-less' blits */
5402 if (flags & WINEDDBLT_COLORFILL)
5404 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
5405 flags &= ~WINEDDBLT_COLORFILL;
5408 if (flags & WINEDDBLT_DEPTHFILL)
5410 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
5412 if (flags & WINEDDBLT_ROP)
5414 /* Catch some degenerate cases here. */
5415 switch (fx->dwROP)
5417 case BLACKNESS:
5418 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
5419 break;
5420 case 0xaa0029: /* No-op */
5421 break;
5422 case WHITENESS:
5423 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
5424 break;
5425 case SRCCOPY: /* Well, we do that below? */
5426 break;
5427 default:
5428 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
5429 goto error;
5431 flags &= ~WINEDDBLT_ROP;
5433 if (flags & WINEDDBLT_DDROPS)
5435 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
5437 /* Now the 'with source' blits. */
5438 if (src_surface)
5440 int sx, xinc, sy, yinc;
5442 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
5443 goto release;
5445 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
5446 && (srcwidth != dstwidth || srcheight != dstheight))
5448 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
5449 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
5452 xinc = (srcwidth << 16) / dstwidth;
5453 yinc = (srcheight << 16) / dstheight;
5455 if (!flags)
5457 /* No effects, we can cheat here. */
5458 if (dstwidth == srcwidth)
5460 if (dstheight == srcheight)
5462 /* No stretching in either direction. This needs to be as
5463 * fast as possible. */
5464 sbuf = sbase;
5466 /* Check for overlapping surfaces. */
5467 if (src_surface != dst_surface || dst_rect->top < src_rect->top
5468 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
5470 /* No overlap, or dst above src, so copy from top downwards. */
5471 for (y = 0; y < dstheight; ++y)
5473 memcpy(dbuf, sbuf, width);
5474 sbuf += src_map.row_pitch;
5475 dbuf += dst_map.row_pitch;
5478 else if (dst_rect->top > src_rect->top)
5480 /* Copy from bottom upwards. */
5481 sbuf += src_map.row_pitch * dstheight;
5482 dbuf += dst_map.row_pitch * dstheight;
5483 for (y = 0; y < dstheight; ++y)
5485 sbuf -= src_map.row_pitch;
5486 dbuf -= dst_map.row_pitch;
5487 memcpy(dbuf, sbuf, width);
5490 else
5492 /* Src and dst overlapping on the same line, use memmove. */
5493 for (y = 0; y < dstheight; ++y)
5495 memmove(dbuf, sbuf, width);
5496 sbuf += src_map.row_pitch;
5497 dbuf += dst_map.row_pitch;
5501 else
5503 /* Stretching in y direction only. */
5504 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5506 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5507 memcpy(dbuf, sbuf, width);
5508 dbuf += dst_map.row_pitch;
5512 else
5514 /* Stretching in X direction. */
5515 int last_sy = -1;
5516 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5518 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5520 if ((sy >> 16) == (last_sy >> 16))
5522 /* This source row is the same as last source row -
5523 * Copy the already stretched row. */
5524 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
5526 else
5528 #define STRETCH_ROW(type) \
5529 do { \
5530 const type *s = (const type *)sbuf; \
5531 type *d = (type *)dbuf; \
5532 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5533 d[x] = s[sx >> 16]; \
5534 } while(0)
5536 switch(bpp)
5538 case 1:
5539 STRETCH_ROW(BYTE);
5540 break;
5541 case 2:
5542 STRETCH_ROW(WORD);
5543 break;
5544 case 4:
5545 STRETCH_ROW(DWORD);
5546 break;
5547 case 3:
5549 const BYTE *s;
5550 BYTE *d = dbuf;
5551 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
5553 DWORD pixel;
5555 s = sbuf + 3 * (sx >> 16);
5556 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5557 d[0] = (pixel ) & 0xff;
5558 d[1] = (pixel >> 8) & 0xff;
5559 d[2] = (pixel >> 16) & 0xff;
5560 d += 3;
5562 break;
5564 default:
5565 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
5566 hr = WINED3DERR_NOTAVAILABLE;
5567 goto error;
5569 #undef STRETCH_ROW
5571 dbuf += dst_map.row_pitch;
5572 last_sy = sy;
5576 else
5578 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
5579 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
5580 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
5581 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
5583 /* The color keying flags are checked for correctness in ddraw */
5584 if (flags & WINEDDBLT_KEYSRC)
5586 keylow = src_surface->container->src_blt_color_key.color_space_low_value;
5587 keyhigh = src_surface->container->src_blt_color_key.color_space_high_value;
5589 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5591 keylow = fx->ddckSrcColorkey.color_space_low_value;
5592 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
5595 if (flags & WINEDDBLT_KEYDEST)
5597 /* Destination color keys are taken from the source surface! */
5598 destkeylow = src_surface->container->dst_blt_color_key.color_space_low_value;
5599 destkeyhigh = src_surface->container->dst_blt_color_key.color_space_high_value;
5601 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
5603 destkeylow = fx->ddckDestColorkey.color_space_low_value;
5604 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
5607 if (bpp == 1)
5609 keymask = 0xff;
5611 else
5613 DWORD masks[3];
5614 get_color_masks(src_format, masks);
5615 keymask = masks[0]
5616 | masks[1]
5617 | masks[2];
5619 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
5622 if (flags & WINEDDBLT_DDFX)
5624 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
5625 LONG tmpxy;
5626 dTopLeft = dbuf;
5627 dTopRight = dbuf + ((dstwidth - 1) * bpp);
5628 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
5629 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
5631 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
5633 /* I don't think we need to do anything about this flag */
5634 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
5636 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
5638 tmp = dTopRight;
5639 dTopRight = dTopLeft;
5640 dTopLeft = tmp;
5641 tmp = dBottomRight;
5642 dBottomRight = dBottomLeft;
5643 dBottomLeft = tmp;
5644 dstxinc = dstxinc * -1;
5646 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
5648 tmp = dTopLeft;
5649 dTopLeft = dBottomLeft;
5650 dBottomLeft = tmp;
5651 tmp = dTopRight;
5652 dTopRight = dBottomRight;
5653 dBottomRight = tmp;
5654 dstyinc = dstyinc * -1;
5656 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
5658 /* I don't think we need to do anything about this flag */
5659 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
5661 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
5663 tmp = dBottomRight;
5664 dBottomRight = dTopLeft;
5665 dTopLeft = tmp;
5666 tmp = dBottomLeft;
5667 dBottomLeft = dTopRight;
5668 dTopRight = tmp;
5669 dstxinc = dstxinc * -1;
5670 dstyinc = dstyinc * -1;
5672 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
5674 tmp = dTopLeft;
5675 dTopLeft = dBottomLeft;
5676 dBottomLeft = dBottomRight;
5677 dBottomRight = dTopRight;
5678 dTopRight = tmp;
5679 tmpxy = dstxinc;
5680 dstxinc = dstyinc;
5681 dstyinc = tmpxy;
5682 dstxinc = dstxinc * -1;
5684 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
5686 tmp = dTopLeft;
5687 dTopLeft = dTopRight;
5688 dTopRight = dBottomRight;
5689 dBottomRight = dBottomLeft;
5690 dBottomLeft = tmp;
5691 tmpxy = dstxinc;
5692 dstxinc = dstyinc;
5693 dstyinc = tmpxy;
5694 dstyinc = dstyinc * -1;
5696 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
5698 /* I don't think we need to do anything about this flag */
5699 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
5701 dbuf = dTopLeft;
5702 flags &= ~(WINEDDBLT_DDFX);
5705 #define COPY_COLORKEY_FX(type) \
5706 do { \
5707 const type *s; \
5708 type *d = (type *)dbuf, *dx, tmp; \
5709 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
5711 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
5712 dx = d; \
5713 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5715 tmp = s[sx >> 16]; \
5716 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
5717 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
5719 dx[0] = tmp; \
5721 dx = (type *)(((BYTE *)dx) + dstxinc); \
5723 d = (type *)(((BYTE *)d) + dstyinc); \
5725 } while(0)
5727 switch (bpp)
5729 case 1:
5730 COPY_COLORKEY_FX(BYTE);
5731 break;
5732 case 2:
5733 COPY_COLORKEY_FX(WORD);
5734 break;
5735 case 4:
5736 COPY_COLORKEY_FX(DWORD);
5737 break;
5738 case 3:
5740 const BYTE *s;
5741 BYTE *d = dbuf, *dx;
5742 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5744 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5745 dx = d;
5746 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
5748 DWORD pixel, dpixel = 0;
5749 s = sbuf + 3 * (sx>>16);
5750 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5751 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
5752 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
5753 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
5755 dx[0] = (pixel ) & 0xff;
5756 dx[1] = (pixel >> 8) & 0xff;
5757 dx[2] = (pixel >> 16) & 0xff;
5759 dx += dstxinc;
5761 d += dstyinc;
5763 break;
5765 default:
5766 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
5767 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
5768 hr = WINED3DERR_NOTAVAILABLE;
5769 goto error;
5770 #undef COPY_COLORKEY_FX
5775 error:
5776 if (flags && FIXME_ON(d3d_surface))
5778 FIXME("\tUnsupported flags: %#x.\n", flags);
5781 release:
5782 wined3d_surface_unmap(dst_surface);
5783 if (src_surface && src_surface != dst_surface)
5784 wined3d_surface_unmap(src_surface);
5785 /* Release the converted surface, if any. */
5786 if (src_texture)
5787 wined3d_texture_decref(src_texture);
5789 return hr;
5792 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5793 const RECT *dst_rect, const struct wined3d_color *color)
5795 static const RECT src_rect;
5796 WINEDDBLTFX BltFx;
5798 memset(&BltFx, 0, sizeof(BltFx));
5799 BltFx.dwSize = sizeof(BltFx);
5800 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
5801 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
5802 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
5805 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
5806 struct wined3d_surface *surface, const RECT *rect, float depth)
5808 FIXME("Depth filling not implemented by cpu_blit.\n");
5809 return WINED3DERR_INVALIDCALL;
5812 const struct blit_shader cpu_blit = {
5813 cpu_blit_alloc,
5814 cpu_blit_free,
5815 cpu_blit_set,
5816 cpu_blit_unset,
5817 cpu_blit_supported,
5818 cpu_blit_color_fill,
5819 cpu_blit_depth_fill,
5822 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5823 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5824 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5826 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5827 struct wined3d_device *device = dst_surface->resource.device;
5828 DWORD src_ds_flags, dst_ds_flags;
5829 RECT src_rect, dst_rect;
5830 BOOL scale, convert;
5831 enum wined3d_conversion_type dst_convert_type;
5832 struct wined3d_format dst_conv_fmt;
5834 static const DWORD simple_blit = WINEDDBLT_ASYNC
5835 | WINEDDBLT_COLORFILL
5836 | WINEDDBLT_WAIT
5837 | WINEDDBLT_DEPTHFILL
5838 | WINEDDBLT_DONOTWAIT;
5840 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5841 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
5842 flags, fx, debug_d3dtexturefiltertype(filter));
5843 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5845 if (fx)
5847 TRACE("dwSize %#x.\n", fx->dwSize);
5848 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5849 TRACE("dwROP %#x.\n", fx->dwROP);
5850 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5851 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5852 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5853 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5854 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5855 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5856 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5857 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5858 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5859 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5860 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5861 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5862 TRACE("dwReserved %#x.\n", fx->dwReserved);
5863 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5864 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5865 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5866 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5867 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5868 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5869 fx->ddckDestColorkey.color_space_low_value,
5870 fx->ddckDestColorkey.color_space_high_value);
5871 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5872 fx->ddckSrcColorkey.color_space_low_value,
5873 fx->ddckSrcColorkey.color_space_high_value);
5876 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5878 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5879 return WINEDDERR_SURFACEBUSY;
5882 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5884 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5885 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5886 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5887 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5888 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5890 WARN("The application gave us a bad destination rectangle.\n");
5891 return WINEDDERR_INVALIDRECT;
5894 if (src_surface)
5896 surface_get_rect(src_surface, src_rect_in, &src_rect);
5898 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5899 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5900 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5901 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5902 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5904 WARN("Application gave us bad source rectangle for Blt.\n");
5905 return WINEDDERR_INVALIDRECT;
5908 else
5910 memset(&src_rect, 0, sizeof(src_rect));
5913 if (!fx || !(fx->dwDDFX))
5914 flags &= ~WINEDDBLT_DDFX;
5916 if (flags & WINEDDBLT_WAIT)
5917 flags &= ~WINEDDBLT_WAIT;
5919 if (flags & WINEDDBLT_ASYNC)
5921 static unsigned int once;
5923 if (!once++)
5924 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5925 flags &= ~WINEDDBLT_ASYNC;
5928 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5929 if (flags & WINEDDBLT_DONOTWAIT)
5931 static unsigned int once;
5933 if (!once++)
5934 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5935 flags &= ~WINEDDBLT_DONOTWAIT;
5938 if (!device->d3d_initialized)
5940 WARN("D3D not initialized, using fallback.\n");
5941 goto cpu;
5944 /* We want to avoid invalidating the sysmem location for converted
5945 * surfaces, since otherwise we'd have to convert the data back when
5946 * locking them. */
5947 d3dfmt_get_conv(dst_surface, TRUE, TRUE, &dst_conv_fmt, &dst_convert_type);
5948 if (dst_convert_type != WINED3D_CT_NONE || dst_conv_fmt.convert || dst_surface->flags & SFLAG_CONVERTED)
5950 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5951 goto cpu;
5954 if (flags & ~simple_blit)
5956 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5957 goto fallback;
5960 if (src_surface)
5961 src_swapchain = src_surface->swapchain;
5962 else
5963 src_swapchain = NULL;
5965 dst_swapchain = dst_surface->swapchain;
5967 /* This isn't strictly needed. FBO blits for example could deal with
5968 * cross-swapchain blits by first downloading the source to a texture
5969 * before switching to the destination context. We just have this here to
5970 * not have to deal with the issue, since cross-swapchain blits should be
5971 * rare. */
5972 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5974 FIXME("Using fallback for cross-swapchain blit.\n");
5975 goto fallback;
5978 scale = src_surface
5979 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5980 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5981 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5983 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5984 if (src_surface)
5985 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5986 else
5987 src_ds_flags = 0;
5989 if (src_ds_flags || dst_ds_flags)
5991 if (flags & WINEDDBLT_DEPTHFILL)
5993 float depth;
5995 TRACE("Depth fill.\n");
5997 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5998 return WINED3DERR_INVALIDCALL;
6000 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
6001 return WINED3D_OK;
6003 else
6005 if (src_ds_flags != dst_ds_flags)
6007 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
6008 return WINED3DERR_INVALIDCALL;
6011 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->draw_binding, &src_rect,
6012 dst_surface, dst_surface->draw_binding, &dst_rect)))
6013 return WINED3D_OK;
6016 else
6018 /* In principle this would apply to depth blits as well, but we don't
6019 * implement those in the CPU blitter at the moment. */
6020 if ((dst_surface->locations & dst_surface->map_binding)
6021 && (!src_surface || (src_surface->locations & src_surface->map_binding)))
6023 if (scale)
6024 TRACE("Not doing sysmem blit because of scaling.\n");
6025 else if (convert)
6026 TRACE("Not doing sysmem blit because of format conversion.\n");
6027 else
6028 goto cpu;
6031 if (flags & WINEDDBLT_COLORFILL)
6033 struct wined3d_color color;
6035 TRACE("Color fill.\n");
6037 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
6038 goto fallback;
6040 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
6041 return WINED3D_OK;
6043 else
6045 TRACE("Color blit.\n");
6047 /* Upload */
6048 if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
6049 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
6051 if (scale)
6052 TRACE("Not doing upload because of scaling.\n");
6053 else if (convert)
6054 TRACE("Not doing upload because of format conversion.\n");
6055 else
6057 POINT dst_point = {dst_rect.left, dst_rect.top};
6059 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
6061 if (!surface_is_offscreen(dst_surface))
6062 surface_load_location(dst_surface, dst_surface->draw_binding);
6063 return WINED3D_OK;
6068 /* Use present for back -> front blits. The idea behind this is
6069 * that present is potentially faster than a blit, in particular
6070 * when FBO blits aren't available. Some ddraw applications like
6071 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
6072 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
6073 * applications can't blit directly to the frontbuffer. */
6074 if (dst_swapchain && dst_swapchain->back_buffers
6075 && dst_surface == dst_swapchain->front_buffer
6076 && src_surface == dst_swapchain->back_buffers[0])
6078 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
6080 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
6082 /* Set the swap effect to COPY, we don't want the backbuffer
6083 * to become undefined. */
6084 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
6085 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
6086 dst_swapchain->desc.swap_effect = swap_effect;
6088 return WINED3D_OK;
6091 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6092 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6093 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6095 TRACE("Using FBO blit.\n");
6097 surface_blt_fbo(device, filter,
6098 src_surface, src_surface->draw_binding, &src_rect,
6099 dst_surface, dst_surface->draw_binding, &dst_rect);
6100 surface_validate_location(dst_surface, dst_surface->draw_binding);
6101 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
6103 return WINED3D_OK;
6106 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6107 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6108 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6110 TRACE("Using arbfp blit.\n");
6112 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
6113 return WINED3D_OK;
6118 fallback:
6119 /* Special cases for render targets. */
6120 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
6121 return WINED3D_OK;
6123 cpu:
6125 /* For the rest call the X11 surface implementation. For render targets
6126 * this should be implemented OpenGL accelerated in surface_blt_special(),
6127 * other blits are rather rare. */
6128 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
6131 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
6132 const struct wined3d_resource_desc *desc, GLenum target, GLint level, DWORD flags)
6134 struct wined3d_device *device = container->resource.device;
6135 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6136 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
6137 UINT multisample_quality = desc->multisample_quality;
6138 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
6139 unsigned int resource_size;
6140 HRESULT hr;
6142 if (multisample_quality > 0)
6144 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
6145 multisample_quality = 0;
6148 /* Quick lockable sanity check.
6149 * TODO: remove this after surfaces, usage and lockability have been debugged properly
6150 * this function is too deep to need to care about things like this.
6151 * Levels need to be checked too, since they all affect what can be done. */
6152 switch (desc->pool)
6154 case WINED3D_POOL_MANAGED:
6155 if (desc->usage & WINED3DUSAGE_DYNAMIC)
6156 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
6157 break;
6159 case WINED3D_POOL_DEFAULT:
6160 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
6161 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
6162 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
6163 break;
6165 case WINED3D_POOL_SCRATCH:
6166 case WINED3D_POOL_SYSTEM_MEM:
6167 break;
6169 default:
6170 FIXME("Unknown pool %#x.\n", desc->pool);
6171 break;
6174 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
6175 FIXME("Trying to create a render target that isn't in the default pool.\n");
6177 /* FIXME: Check that the format is supported by the device. */
6179 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
6180 if (!resource_size)
6181 return WINED3DERR_INVALIDCALL;
6183 if (device->wined3d->flags & WINED3D_NO3D)
6184 surface->surface_ops = &gdi_surface_ops;
6185 else
6186 surface->surface_ops = &surface_ops;
6188 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
6189 desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height, 1,
6190 resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
6192 WARN("Failed to initialize resource, returning %#x.\n", hr);
6193 return hr;
6196 surface_set_container(surface, container);
6197 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
6198 list_init(&surface->renderbuffers);
6199 list_init(&surface->overlays);
6201 /* Flags */
6202 if (target != GL_TEXTURE_RECTANGLE_ARB)
6203 surface->flags |= SFLAG_NORMCOORD;
6204 if (flags & WINED3D_SURFACE_DISCARD)
6205 surface->flags |= SFLAG_DISCARD;
6206 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
6207 surface->flags |= SFLAG_PIN_SYSMEM;
6208 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
6209 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
6211 surface->map_binding = WINED3D_LOCATION_SYSMEM;
6212 surface->texture_target = target;
6213 surface->texture_level = level;
6215 /* Call the private setup routine */
6216 hr = surface->surface_ops->surface_private_setup(surface);
6217 if (FAILED(hr))
6219 ERR("Private setup failed, returning %#x\n", hr);
6220 surface_set_container(surface, NULL);
6221 surface_cleanup(surface);
6222 return hr;
6225 /* Similar to lockable rendertargets above, creating the DIB section
6226 * during surface initialization prevents the sysmem pointer from changing
6227 * after a wined3d_surface_getdc() call. */
6228 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
6229 && SUCCEEDED(surface_create_dib_section(surface)))
6230 surface->map_binding = WINED3D_LOCATION_DIB;
6232 if (surface->map_binding == WINED3D_LOCATION_DIB)
6234 wined3d_resource_free_sysmem(&surface->resource);
6235 surface_validate_location(surface, WINED3D_LOCATION_DIB);
6236 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
6239 return hr;
6242 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
6243 GLenum target, GLint level, DWORD flags, struct wined3d_surface **surface)
6245 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
6246 const struct wined3d_parent_ops *parent_ops;
6247 struct wined3d_surface *object;
6248 void *parent;
6249 HRESULT hr;
6251 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
6252 "multisample_type %#x, multisample_quality %u, target %#x, level %d, flags %#x, surface %p.\n",
6253 container, desc->width, desc->height, debug_d3dformat(desc->format),
6254 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
6255 desc->multisample_type, desc->multisample_quality, target, level, flags, surface);
6257 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
6258 return E_OUTOFMEMORY;
6260 if (FAILED(hr = surface_init(object, container, desc, target, level, flags)))
6262 WARN("Failed to initialize surface, returning %#x.\n", hr);
6263 HeapFree(GetProcessHeap(), 0, object);
6264 return hr;
6267 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
6268 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
6270 WARN("Failed to create surface parent, hr %#x.\n", hr);
6271 surface_set_container(object, NULL);
6272 wined3d_surface_decref(object);
6273 return hr;
6276 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
6278 object->resource.parent = parent;
6279 object->resource.parent_ops = parent_ops;
6280 *surface = object;
6282 return hr;