wined3d: Separate surface locations and flags.
[wine/wine-gecko.git] / dlls / wined3d / surface.c
bloba30c11ca03f116745c9d2f7a4ece84e886aecb11
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 SFLAG_INDIB | SFLAG_INUSERMEM | SFLAG_INSYSMEM | SFLAG_INBUFFER;
42 static void surface_cleanup(struct wined3d_surface *surface)
44 struct wined3d_surface *overlay, *cur;
46 TRACE("surface %p.\n", surface);
48 if (surface->pbo || surface->rb_multisample
49 || surface->rb_resolved || !list_empty(&surface->renderbuffers))
51 struct wined3d_renderbuffer_entry *entry, *entry2;
52 const struct wined3d_gl_info *gl_info;
53 struct wined3d_context *context;
55 context = context_acquire(surface->resource.device, NULL);
56 gl_info = context->gl_info;
58 if (surface->pbo)
60 TRACE("Deleting PBO %u.\n", surface->pbo);
61 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
64 if (surface->rb_multisample)
66 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
67 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
70 if (surface->rb_resolved)
72 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
73 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
76 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
78 TRACE("Deleting renderbuffer %u.\n", entry->id);
79 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
80 HeapFree(GetProcessHeap(), 0, entry);
83 context_release(context);
86 if (surface->flags & SFLAG_DIBSECTION)
88 DeleteDC(surface->hDC);
89 DeleteObject(surface->dib.DIBsection);
90 surface->dib.bitmap_data = NULL;
93 if (surface->overlay_dest)
94 list_remove(&surface->overlay_entry);
96 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
98 list_remove(&overlay->overlay_entry);
99 overlay->overlay_dest = NULL;
102 resource_cleanup(&surface->resource);
105 void surface_update_draw_binding(struct wined3d_surface *surface)
107 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
108 surface->draw_binding = SFLAG_INDRAWABLE;
109 else if (surface->resource.multisample_type)
110 surface->draw_binding = SFLAG_INRB_MULTISAMPLE;
111 else
112 surface->draw_binding = SFLAG_INTEXTURE;
115 void surface_set_swapchain(struct wined3d_surface *surface, struct wined3d_swapchain *swapchain)
117 TRACE("surface %p, swapchain %p.\n", surface, swapchain);
119 if (swapchain)
121 surface->get_drawable_size = get_drawable_size_swapchain;
123 else
125 switch (wined3d_settings.offscreen_rendering_mode)
127 case ORM_FBO:
128 surface->get_drawable_size = get_drawable_size_fbo;
129 break;
131 case ORM_BACKBUFFER:
132 surface->get_drawable_size = get_drawable_size_backbuffer;
133 break;
135 default:
136 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
137 return;
141 surface->swapchain = swapchain;
142 surface_update_draw_binding(surface);
145 void surface_set_container(struct wined3d_surface *surface, struct wined3d_texture *container)
147 TRACE("surface %p, container %p.\n", surface, container);
149 if (!surface->swapchain)
151 switch (wined3d_settings.offscreen_rendering_mode)
153 case ORM_FBO:
154 surface->get_drawable_size = get_drawable_size_fbo;
155 break;
157 case ORM_BACKBUFFER:
158 surface->get_drawable_size = get_drawable_size_backbuffer;
159 break;
161 default:
162 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
163 return;
167 surface->container = container;
168 surface_update_draw_binding(surface);
171 struct blt_info
173 GLenum binding;
174 GLenum bind_target;
175 enum tex_types tex_type;
176 GLfloat coords[4][3];
179 struct float_rect
181 float l;
182 float t;
183 float r;
184 float b;
187 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
189 f->l = ((r->left * 2.0f) / w) - 1.0f;
190 f->t = ((r->top * 2.0f) / h) - 1.0f;
191 f->r = ((r->right * 2.0f) / w) - 1.0f;
192 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
195 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
197 GLfloat (*coords)[3] = info->coords;
198 struct float_rect f;
200 switch (target)
202 default:
203 FIXME("Unsupported texture target %#x\n", target);
204 /* Fall back to GL_TEXTURE_2D */
205 case GL_TEXTURE_2D:
206 info->binding = GL_TEXTURE_BINDING_2D;
207 info->bind_target = GL_TEXTURE_2D;
208 info->tex_type = tex_2d;
209 coords[0][0] = (float)rect->left / w;
210 coords[0][1] = (float)rect->top / h;
211 coords[0][2] = 0.0f;
213 coords[1][0] = (float)rect->right / w;
214 coords[1][1] = (float)rect->top / h;
215 coords[1][2] = 0.0f;
217 coords[2][0] = (float)rect->left / w;
218 coords[2][1] = (float)rect->bottom / h;
219 coords[2][2] = 0.0f;
221 coords[3][0] = (float)rect->right / w;
222 coords[3][1] = (float)rect->bottom / h;
223 coords[3][2] = 0.0f;
224 break;
226 case GL_TEXTURE_RECTANGLE_ARB:
227 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
228 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
229 info->tex_type = tex_rect;
230 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
231 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
232 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
233 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
234 break;
236 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
237 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
238 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
239 info->tex_type = tex_cube;
240 cube_coords_float(rect, w, h, &f);
242 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
243 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
244 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
245 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
246 break;
248 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
249 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
250 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
251 info->tex_type = tex_cube;
252 cube_coords_float(rect, w, h, &f);
254 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
255 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
256 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
257 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
258 break;
260 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
261 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
262 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
263 info->tex_type = tex_cube;
264 cube_coords_float(rect, w, h, &f);
266 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
267 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
268 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
269 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
270 break;
272 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
273 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
274 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
275 info->tex_type = tex_cube;
276 cube_coords_float(rect, w, h, &f);
278 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
279 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
280 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
281 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
282 break;
284 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
285 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
286 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
287 info->tex_type = tex_cube;
288 cube_coords_float(rect, w, h, &f);
290 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
291 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
292 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
293 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
294 break;
296 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
297 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
298 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
299 info->tex_type = tex_cube;
300 cube_coords_float(rect, w, h, &f);
302 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
303 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
304 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
305 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
306 break;
310 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
312 if (rect_in)
313 *rect_out = *rect_in;
314 else
316 rect_out->left = 0;
317 rect_out->top = 0;
318 rect_out->right = surface->resource.width;
319 rect_out->bottom = surface->resource.height;
323 /* Context activation is done by the caller. */
324 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
325 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
327 const struct wined3d_gl_info *gl_info = context->gl_info;
328 struct wined3d_texture *texture = src_surface->container;
329 struct blt_info info;
331 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
333 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
334 checkGLcall("glEnable(bind_target)");
336 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
338 /* Filtering for StretchRect */
339 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
340 wined3d_gl_mag_filter(magLookup, filter));
341 checkGLcall("glTexParameteri");
342 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
343 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
344 checkGLcall("glTexParameteri");
345 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
346 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
347 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
348 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
349 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
350 checkGLcall("glTexEnvi");
352 /* Draw a quad */
353 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
354 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
355 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
357 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
358 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
360 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
361 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
363 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
364 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
365 gl_info->gl_ops.gl.p_glEnd();
367 /* Unbind the texture */
368 context_bind_texture(context, info.bind_target, 0);
370 /* We changed the filtering settings on the texture. Inform the
371 * container about this to get the filters reset properly next draw. */
372 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_POINT;
373 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
374 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_NONE;
375 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
378 /* Works correctly only for <= 4 bpp formats. */
379 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
381 masks[0] = ((1 << format->red_size) - 1) << format->red_offset;
382 masks[1] = ((1 << format->green_size) - 1) << format->green_offset;
383 masks[2] = ((1 << format->blue_size) - 1) << format->blue_offset;
386 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
388 const struct wined3d_format *format = surface->resource.format;
389 SYSTEM_INFO sysInfo;
390 BITMAPINFO *b_info;
391 int extraline = 0;
392 DWORD *masks;
394 TRACE("surface %p.\n", surface);
396 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
398 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
399 return WINED3DERR_INVALIDCALL;
402 switch (format->byte_count)
404 case 2:
405 case 4:
406 /* Allocate extra space to store the RGB bit masks. */
407 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
408 break;
410 case 3:
411 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
412 break;
414 default:
415 /* Allocate extra space for a palette. */
416 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
417 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
418 break;
421 if (!b_info)
422 return E_OUTOFMEMORY;
424 /* Some applications access the surface in via DWORDs, and do not take
425 * the necessary care at the end of the surface. So we need at least
426 * 4 extra bytes at the end of the surface. Check against the page size,
427 * if the last page used for the surface has at least 4 spare bytes we're
428 * safe, otherwise add an extra line to the DIB section. */
429 GetSystemInfo(&sysInfo);
430 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
432 extraline = 1;
433 TRACE("Adding an extra line to the DIB section.\n");
436 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
437 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
438 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
439 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
440 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
441 * wined3d_surface_get_pitch(surface);
442 b_info->bmiHeader.biPlanes = 1;
443 b_info->bmiHeader.biBitCount = format->byte_count * 8;
445 b_info->bmiHeader.biXPelsPerMeter = 0;
446 b_info->bmiHeader.biYPelsPerMeter = 0;
447 b_info->bmiHeader.biClrUsed = 0;
448 b_info->bmiHeader.biClrImportant = 0;
450 /* Get the bit masks */
451 masks = (DWORD *)b_info->bmiColors;
452 switch (surface->resource.format->id)
454 case WINED3DFMT_B8G8R8_UNORM:
455 b_info->bmiHeader.biCompression = BI_RGB;
456 break;
458 case WINED3DFMT_B5G5R5X1_UNORM:
459 case WINED3DFMT_B5G5R5A1_UNORM:
460 case WINED3DFMT_B4G4R4A4_UNORM:
461 case WINED3DFMT_B4G4R4X4_UNORM:
462 case WINED3DFMT_B2G3R3_UNORM:
463 case WINED3DFMT_B2G3R3A8_UNORM:
464 case WINED3DFMT_R10G10B10A2_UNORM:
465 case WINED3DFMT_R8G8B8A8_UNORM:
466 case WINED3DFMT_R8G8B8X8_UNORM:
467 case WINED3DFMT_B10G10R10A2_UNORM:
468 case WINED3DFMT_B5G6R5_UNORM:
469 case WINED3DFMT_R16G16B16A16_UNORM:
470 b_info->bmiHeader.biCompression = BI_BITFIELDS;
471 get_color_masks(format, masks);
472 break;
474 default:
475 /* Don't know palette */
476 b_info->bmiHeader.biCompression = BI_RGB;
477 break;
480 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
481 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
482 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
483 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
485 if (!surface->dib.DIBsection)
487 ERR("Failed to create DIB section.\n");
488 HeapFree(GetProcessHeap(), 0, b_info);
489 return HRESULT_FROM_WIN32(GetLastError());
492 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
493 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
495 HeapFree(GetProcessHeap(), 0, b_info);
497 /* Now allocate a DC. */
498 surface->hDC = CreateCompatibleDC(0);
499 SelectObject(surface->hDC, surface->dib.DIBsection);
500 TRACE("Using wined3d palette %p.\n", surface->palette);
501 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
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 & SFLAG_INBUFFER)
513 data->addr = NULL;
514 data->buffer_object = surface->pbo;
515 return;
517 if (location & SFLAG_INUSERMEM)
519 data->addr = surface->user_memory;
520 data->buffer_object = 0;
521 return;
523 if (location & SFLAG_INDIB)
525 data->addr = surface->dib.bitmap_data;
526 data->buffer_object = 0;
527 return;
529 if (location & SFLAG_INSYSMEM)
531 data->addr = surface->resource.heap_memory;
532 data->buffer_object = 0;
533 return;
536 ERR("Unexpected locations %s.\n", debug_surflocation(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 & SFLAG_INSYSMEM)
586 ERR("Surface without system memory has SFLAG_INSYSMEM set.\n");
589 void surface_prepare_map_memory(struct wined3d_surface *surface)
591 switch (surface->map_binding)
593 case SFLAG_INUSERMEM:
594 if (!surface->user_memory)
595 ERR("Map binding is set to SFLAG_INUSERMEM but surface->user_memory is NULL.\n");
596 break;
598 case SFLAG_INDIB:
599 if (!surface->dib.bitmap_data)
600 ERR("Map binding is set to SFLAG_INDIB but surface->dib.bitmap_data is NULL.\n");
601 break;
603 case SFLAG_INSYSMEM:
604 surface_prepare_system_memory(surface);
605 break;
607 case SFLAG_INBUFFER:
608 surface_prepare_buffer(surface);
609 break;
611 default:
612 ERR("Unexpected map binding %s.\n", debug_surflocation(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, SFLAG_INSYSMEM);
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, SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
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 surface->texture_target = GL_TEXTURE_2D;
675 /* Non-power2 support */
676 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
678 pow2Width = surface->resource.width;
679 pow2Height = surface->resource.height;
681 else
683 /* Find the nearest pow2 match */
684 pow2Width = pow2Height = 1;
685 while (pow2Width < surface->resource.width)
686 pow2Width <<= 1;
687 while (pow2Height < surface->resource.height)
688 pow2Height <<= 1;
690 surface->pow2Width = pow2Width;
691 surface->pow2Height = pow2Height;
693 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
695 /* TODO: Add support for non power two compressed textures. */
696 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
698 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
699 surface, surface->resource.width, surface->resource.height);
700 return WINED3DERR_NOTAVAILABLE;
704 if (pow2Width != surface->resource.width
705 || pow2Height != surface->resource.height)
707 surface->flags |= SFLAG_NONPOW2;
710 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
711 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
713 /* One of three options:
714 * 1: Do the same as we do with NPOT and scale the texture, (any
715 * texture ops would require the texture to be scaled which is
716 * potentially slow)
717 * 2: Set the texture to the maximum size (bad idea).
718 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
719 * 4: Create the surface, but allow it to be used only for DirectDraw
720 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
721 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
722 * the render target. */
723 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
725 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
726 return WINED3DERR_NOTAVAILABLE;
729 /* We should never use this surface in combination with OpenGL! */
730 TRACE("Creating an oversized surface: %ux%u.\n",
731 surface->pow2Width, surface->pow2Height);
734 switch (wined3d_settings.offscreen_rendering_mode)
736 case ORM_FBO:
737 surface->get_drawable_size = get_drawable_size_fbo;
738 break;
740 case ORM_BACKBUFFER:
741 surface->get_drawable_size = get_drawable_size_backbuffer;
742 break;
744 default:
745 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
746 return WINED3DERR_INVALIDCALL;
749 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
750 surface->locations = SFLAG_DISCARDED;
752 if (surface_use_pbo(surface))
753 surface->map_binding = SFLAG_INBUFFER;
755 return WINED3D_OK;
758 static void surface_realize_palette(struct wined3d_surface *surface)
760 struct wined3d_palette *palette = surface->palette;
762 TRACE("surface %p.\n", surface);
764 if (!palette) return;
766 if (surface->resource.format->id == WINED3DFMT_P8_UINT
767 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
769 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
771 /* Make sure the texture is up to date. This call doesn't do
772 * anything if the texture is already up to date. */
773 surface_load_location(surface, SFLAG_INTEXTURE);
775 /* We want to force a palette refresh, so mark the drawable as not being up to date */
776 if (!surface_is_offscreen(surface))
777 surface_invalidate_location(surface, SFLAG_INDRAWABLE);
779 else
781 if (!(surface->locations & surface->map_binding))
783 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
784 surface_prepare_map_memory(surface);
785 surface_load_location(surface, surface->map_binding);
787 surface_invalidate_location(surface, ~surface->map_binding);
791 if (surface->flags & SFLAG_DIBSECTION)
793 RGBQUAD col[256];
794 unsigned int i;
796 TRACE("Updating the DC's palette.\n");
798 for (i = 0; i < 256; ++i)
800 col[i].rgbRed = palette->palents[i].peRed;
801 col[i].rgbGreen = palette->palents[i].peGreen;
802 col[i].rgbBlue = palette->palents[i].peBlue;
803 col[i].rgbReserved = 0;
805 SetDIBColorTable(surface->hDC, 0, 256, col);
808 /* Propagate the changes to the drawable when we have a palette. */
809 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
810 surface_load_location(surface, surface->draw_binding);
813 static void surface_unmap(struct wined3d_surface *surface)
815 struct wined3d_device *device = surface->resource.device;
816 const struct wined3d_gl_info *gl_info;
817 struct wined3d_context *context;
819 TRACE("surface %p.\n", surface);
821 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
823 switch (surface->map_binding)
825 case SFLAG_INUSERMEM:
826 case SFLAG_INDIB:
827 case SFLAG_INSYSMEM:
828 break;
830 case SFLAG_INBUFFER:
831 context = context_acquire(device, NULL);
832 gl_info = context->gl_info;
834 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
835 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
836 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
837 checkGLcall("glUnmapBufferARB");
838 context_release(context);
839 break;
841 default:
842 ERR("Unexpected map binding %s.\n", debug_surflocation(surface->map_binding));
845 if (surface->locations & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
847 TRACE("Not dirtified, nothing to do.\n");
848 return;
851 if (surface->swapchain && surface->swapchain->front_buffer == surface)
852 surface_load_location(surface, surface->draw_binding);
853 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
854 FIXME("Depth / stencil buffer locking is not implemented.\n");
857 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
859 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
860 return FALSE;
861 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
862 return FALSE;
863 return TRUE;
866 static void surface_depth_blt_fbo(const struct wined3d_device *device,
867 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
868 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
870 const struct wined3d_gl_info *gl_info;
871 struct wined3d_context *context;
872 DWORD src_mask, dst_mask;
873 GLbitfield gl_mask;
875 TRACE("device %p\n", device);
876 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
877 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect));
878 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
879 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect));
881 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
882 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
884 if (src_mask != dst_mask)
886 ERR("Incompatible formats %s and %s.\n",
887 debug_d3dformat(src_surface->resource.format->id),
888 debug_d3dformat(dst_surface->resource.format->id));
889 return;
892 if (!src_mask)
894 ERR("Not a depth / stencil format: %s.\n",
895 debug_d3dformat(src_surface->resource.format->id));
896 return;
899 gl_mask = 0;
900 if (src_mask & WINED3DFMT_FLAG_DEPTH)
901 gl_mask |= GL_DEPTH_BUFFER_BIT;
902 if (src_mask & WINED3DFMT_FLAG_STENCIL)
903 gl_mask |= GL_STENCIL_BUFFER_BIT;
905 /* Make sure the locations are up-to-date. Loading the destination
906 * surface isn't required if the entire surface is overwritten. */
907 surface_load_location(src_surface, src_location);
908 if (!surface_is_full_rect(dst_surface, dst_rect))
909 surface_load_location(dst_surface, dst_location);
911 context = context_acquire(device, NULL);
912 if (!context->valid)
914 context_release(context);
915 WARN("Invalid context, skipping blit.\n");
916 return;
919 gl_info = context->gl_info;
921 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
922 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
924 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
925 context_set_draw_buffer(context, GL_NONE);
926 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
927 context_invalidate_state(context, STATE_FRAMEBUFFER);
929 if (gl_mask & GL_DEPTH_BUFFER_BIT)
931 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
932 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
934 if (gl_mask & GL_STENCIL_BUFFER_BIT)
936 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
938 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
939 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
941 gl_info->gl_ops.gl.p_glStencilMask(~0U);
942 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
945 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
946 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
948 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
949 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
950 checkGLcall("glBlitFramebuffer()");
952 if (wined3d_settings.strict_draw_ordering)
953 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
955 context_release(context);
958 /* Blit between surface locations. Onscreen on different swapchains is not supported.
959 * Depth / stencil is not supported. */
960 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
961 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
962 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
964 const struct wined3d_gl_info *gl_info;
965 struct wined3d_context *context;
966 RECT src_rect, dst_rect;
967 GLenum gl_filter;
968 GLenum buffer;
970 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
971 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
972 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
973 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
974 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
976 src_rect = *src_rect_in;
977 dst_rect = *dst_rect_in;
979 switch (filter)
981 case WINED3D_TEXF_LINEAR:
982 gl_filter = GL_LINEAR;
983 break;
985 default:
986 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
987 case WINED3D_TEXF_NONE:
988 case WINED3D_TEXF_POINT:
989 gl_filter = GL_NEAREST;
990 break;
993 /* Resolve the source surface first if needed. */
994 if (src_location == SFLAG_INRB_MULTISAMPLE
995 && (src_surface->resource.format->id != dst_surface->resource.format->id
996 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
997 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
998 src_location = SFLAG_INRB_RESOLVED;
1000 /* Make sure the locations are up-to-date. Loading the destination
1001 * surface isn't required if the entire surface is overwritten. (And is
1002 * in fact harmful if we're being called by surface_load_location() with
1003 * the purpose of loading the destination surface.) */
1004 surface_load_location(src_surface, src_location);
1005 if (!surface_is_full_rect(dst_surface, &dst_rect))
1006 surface_load_location(dst_surface, dst_location);
1008 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1009 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1010 else context = context_acquire(device, NULL);
1012 if (!context->valid)
1014 context_release(context);
1015 WARN("Invalid context, skipping blit.\n");
1016 return;
1019 gl_info = context->gl_info;
1021 if (src_location == SFLAG_INDRAWABLE)
1023 TRACE("Source surface %p is onscreen.\n", src_surface);
1024 buffer = surface_get_gl_buffer(src_surface);
1025 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1027 else
1029 TRACE("Source surface %p is offscreen.\n", src_surface);
1030 buffer = GL_COLOR_ATTACHMENT0;
1033 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1034 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1035 checkGLcall("glReadBuffer()");
1036 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1038 if (dst_location == SFLAG_INDRAWABLE)
1040 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1041 buffer = surface_get_gl_buffer(dst_surface);
1042 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1044 else
1046 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1047 buffer = GL_COLOR_ATTACHMENT0;
1050 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1051 context_set_draw_buffer(context, buffer);
1052 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1053 context_invalidate_state(context, STATE_FRAMEBUFFER);
1055 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1056 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
1057 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
1058 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
1059 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
1061 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
1062 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1064 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1065 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1066 checkGLcall("glBlitFramebuffer()");
1068 if (wined3d_settings.strict_draw_ordering
1069 || (dst_location == SFLAG_INDRAWABLE
1070 && dst_surface->swapchain->front_buffer == dst_surface))
1071 gl_info->gl_ops.gl.p_glFlush();
1073 context_release(context);
1076 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1077 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
1078 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
1080 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1081 return FALSE;
1083 /* Source and/or destination need to be on the GL side */
1084 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
1085 return FALSE;
1087 switch (blit_op)
1089 case WINED3D_BLIT_OP_COLOR_BLIT:
1090 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1091 return FALSE;
1092 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1093 return FALSE;
1094 break;
1096 case WINED3D_BLIT_OP_DEPTH_BLIT:
1097 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1098 return FALSE;
1099 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1100 return FALSE;
1101 break;
1103 default:
1104 return FALSE;
1107 if (!(src_format->id == dst_format->id
1108 || (is_identity_fixup(src_format->color_fixup)
1109 && is_identity_fixup(dst_format->color_fixup))))
1110 return FALSE;
1112 return TRUE;
1115 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1116 DWORD color, struct wined3d_color *float_color)
1118 const struct wined3d_format *format = surface->resource.format;
1119 const struct wined3d_device *device = surface->resource.device;
1121 switch (format->id)
1123 case WINED3DFMT_P8_UINT:
1124 if (surface->palette)
1126 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1127 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1128 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1130 else
1132 float_color->r = 0.0f;
1133 float_color->g = 0.0f;
1134 float_color->b = 0.0f;
1136 float_color->a = swapchain_is_p8(device->swapchains[0]) ? color / 255.0f : 1.0f;
1137 break;
1139 case WINED3DFMT_B5G6R5_UNORM:
1140 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1141 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1142 float_color->b = (color & 0x1f) / 31.0f;
1143 float_color->a = 1.0f;
1144 break;
1146 case WINED3DFMT_B8G8R8_UNORM:
1147 case WINED3DFMT_B8G8R8X8_UNORM:
1148 float_color->r = D3DCOLOR_R(color);
1149 float_color->g = D3DCOLOR_G(color);
1150 float_color->b = D3DCOLOR_B(color);
1151 float_color->a = 1.0f;
1152 break;
1154 case WINED3DFMT_B8G8R8A8_UNORM:
1155 float_color->r = D3DCOLOR_R(color);
1156 float_color->g = D3DCOLOR_G(color);
1157 float_color->b = D3DCOLOR_B(color);
1158 float_color->a = D3DCOLOR_A(color);
1159 break;
1161 default:
1162 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1163 return FALSE;
1166 return TRUE;
1169 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1171 const struct wined3d_format *format = surface->resource.format;
1173 switch (format->id)
1175 case WINED3DFMT_S1_UINT_D15_UNORM:
1176 *float_depth = depth / (float)0x00007fff;
1177 break;
1179 case WINED3DFMT_D16_UNORM:
1180 *float_depth = depth / (float)0x0000ffff;
1181 break;
1183 case WINED3DFMT_D24_UNORM_S8_UINT:
1184 case WINED3DFMT_X8D24_UNORM:
1185 *float_depth = depth / (float)0x00ffffff;
1186 break;
1188 case WINED3DFMT_D32_UNORM:
1189 *float_depth = depth / (float)0xffffffff;
1190 break;
1192 default:
1193 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1194 return FALSE;
1197 return TRUE;
1200 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1202 const struct wined3d_resource *resource = &surface->resource;
1203 struct wined3d_device *device = resource->device;
1204 const struct blit_shader *blitter;
1206 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1207 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1208 if (!blitter)
1210 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1211 return WINED3DERR_INVALIDCALL;
1214 return blitter->depth_fill(device, surface, rect, depth);
1217 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1218 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1220 struct wined3d_device *device = src_surface->resource.device;
1222 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1223 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1224 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1225 return WINED3DERR_INVALIDCALL;
1227 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1229 surface_modify_ds_location(dst_surface, dst_location,
1230 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1232 return WINED3D_OK;
1235 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1236 struct wined3d_surface *render_target)
1238 TRACE("surface %p, render_target %p.\n", surface, render_target);
1240 /* TODO: Check surface sizes, pools, etc. */
1242 if (render_target->resource.multisample_type)
1243 return WINED3DERR_INVALIDCALL;
1245 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1248 /* Context activation is done by the caller. */
1249 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1251 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1252 checkGLcall("glDeleteBuffersARB(1, &surface->pbo)");
1254 surface->pbo = 0;
1255 surface_invalidate_location(surface, SFLAG_INBUFFER);
1258 static void surface_unload(struct wined3d_resource *resource)
1260 struct wined3d_surface *surface = surface_from_resource(resource);
1261 struct wined3d_renderbuffer_entry *entry, *entry2;
1262 struct wined3d_device *device = resource->device;
1263 const struct wined3d_gl_info *gl_info;
1264 struct wined3d_context *context;
1266 TRACE("surface %p.\n", surface);
1268 if (resource->pool == WINED3D_POOL_DEFAULT)
1270 /* Default pool resources are supposed to be destroyed before Reset is called.
1271 * Implicit resources stay however. So this means we have an implicit render target
1272 * or depth stencil. The content may be destroyed, but we still have to tear down
1273 * opengl resources, so we cannot leave early.
1275 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1276 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1277 * or the depth stencil into an FBO the texture or render buffer will be removed
1278 * and all flags get lost */
1279 surface_prepare_system_memory(surface);
1280 memset(surface->resource.heap_memory, 0, surface->resource.size);
1281 surface_validate_location(surface, SFLAG_INSYSMEM);
1282 surface_invalidate_location(surface, ~SFLAG_INSYSMEM);
1284 /* We also get here when the ddraw swapchain is destroyed, for example
1285 * for a mode switch. In this case this surface won't necessarily be
1286 * an implicit surface. We have to mark it lost so that the
1287 * application can restore it after the mode switch. */
1288 surface->flags |= SFLAG_LOST;
1290 else
1292 surface_prepare_map_memory(surface);
1293 surface_load_location(surface, surface->map_binding);
1294 surface_invalidate_location(surface, ~surface->map_binding);
1296 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1298 context = context_acquire(device, NULL);
1299 gl_info = context->gl_info;
1301 /* Destroy PBOs, but load them into real sysmem before */
1302 if (surface->pbo)
1303 surface_remove_pbo(surface, gl_info);
1305 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1306 * all application-created targets the application has to release the surface
1307 * before calling _Reset
1309 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1311 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1312 list_remove(&entry->entry);
1313 HeapFree(GetProcessHeap(), 0, entry);
1315 list_init(&surface->renderbuffers);
1316 surface->current_renderbuffer = NULL;
1318 if (surface->rb_multisample)
1320 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1321 surface->rb_multisample = 0;
1323 if (surface->rb_resolved)
1325 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1326 surface->rb_resolved = 0;
1329 context_release(context);
1331 resource_unload(resource);
1334 static const struct wined3d_resource_ops surface_resource_ops =
1336 surface_unload,
1339 static const struct wined3d_surface_ops surface_ops =
1341 surface_private_setup,
1342 surface_realize_palette,
1343 surface_unmap,
1346 /*****************************************************************************
1347 * Initializes the GDI surface, aka creates the DIB section we render to
1348 * The DIB section creation is done by calling GetDC, which will create the
1349 * section and releasing the dc to allow the app to use it. The dib section
1350 * will stay until the surface is released
1352 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1353 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1354 * avoid confusion in the shared surface code.
1356 * Returns:
1357 * WINED3D_OK on success
1358 * The return values of called methods on failure
1360 *****************************************************************************/
1361 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1363 HRESULT hr;
1365 TRACE("surface %p.\n", surface);
1367 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1369 ERR("Overlays not yet supported by GDI surfaces.\n");
1370 return WINED3DERR_INVALIDCALL;
1373 /* Sysmem textures have memory already allocated - release it,
1374 * this avoids an unnecessary memcpy. */
1375 hr = surface_create_dib_section(surface);
1376 if (FAILED(hr))
1377 return hr;
1378 surface->map_binding = SFLAG_INDIB;
1380 /* We don't mind the nonpow2 stuff in GDI. */
1381 surface->pow2Width = surface->resource.width;
1382 surface->pow2Height = surface->resource.height;
1384 return WINED3D_OK;
1387 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1389 struct wined3d_palette *palette = surface->palette;
1391 TRACE("surface %p.\n", surface);
1393 if (!palette) return;
1395 if (surface->flags & SFLAG_DIBSECTION)
1397 RGBQUAD col[256];
1398 unsigned int i;
1400 TRACE("Updating the DC's palette.\n");
1402 for (i = 0; i < 256; ++i)
1404 col[i].rgbRed = palette->palents[i].peRed;
1405 col[i].rgbGreen = palette->palents[i].peGreen;
1406 col[i].rgbBlue = palette->palents[i].peBlue;
1407 col[i].rgbReserved = 0;
1409 SetDIBColorTable(surface->hDC, 0, 256, col);
1412 /* Update the image because of the palette change. Some games like e.g.
1413 * Red Alert call SetEntries a lot to implement fading. */
1414 /* Tell the swapchain to update the screen. */
1415 if (surface->swapchain && surface == surface->swapchain->front_buffer)
1416 x11_copy_to_screen(surface->swapchain, NULL);
1419 static void gdi_surface_unmap(struct wined3d_surface *surface)
1421 TRACE("surface %p.\n", surface);
1423 /* Tell the swapchain to update the screen. */
1424 if (surface->swapchain && surface == surface->swapchain->front_buffer)
1425 x11_copy_to_screen(surface->swapchain, &surface->lockedRect);
1427 memset(&surface->lockedRect, 0, sizeof(RECT));
1430 static const struct wined3d_surface_ops gdi_surface_ops =
1432 gdi_surface_private_setup,
1433 gdi_surface_realize_palette,
1434 gdi_surface_unmap,
1437 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target, GLint level)
1439 TRACE("surface %p, target %#x.\n", surface, target);
1441 if (surface->texture_target != target)
1443 if (target == GL_TEXTURE_RECTANGLE_ARB)
1445 surface->flags &= ~SFLAG_NORMCOORD;
1447 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
1449 surface->flags |= SFLAG_NORMCOORD;
1452 surface->texture_target = target;
1453 surface->texture_level = level;
1454 surface_force_reload(surface);
1457 /* This call just downloads data, the caller is responsible for binding the
1458 * correct texture. */
1459 /* Context activation is done by the caller. */
1460 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1461 DWORD dst_location)
1463 const struct wined3d_format *format = surface->resource.format;
1464 struct wined3d_bo_address data;
1466 /* Only support read back of converted P8 surfaces. */
1467 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1469 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1470 return;
1473 surface_get_memory(surface, &data, dst_location);
1475 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1477 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
1478 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1480 if (data.buffer_object)
1482 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1483 checkGLcall("glBindBufferARB");
1484 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
1485 checkGLcall("glGetCompressedTexImageARB");
1486 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1487 checkGLcall("glBindBufferARB");
1489 else
1491 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
1492 surface->texture_level, data.addr));
1493 checkGLcall("glGetCompressedTexImageARB");
1496 else
1498 void *mem;
1499 GLenum gl_format = format->glFormat;
1500 GLenum gl_type = format->glType;
1501 int src_pitch = 0;
1502 int dst_pitch = 0;
1504 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
1505 if (format->id == WINED3DFMT_P8_UINT && swapchain_is_p8(surface->resource.device->swapchains[0]))
1507 gl_format = GL_ALPHA;
1508 gl_type = GL_UNSIGNED_BYTE;
1511 if (surface->flags & SFLAG_NONPOW2)
1513 unsigned char alignment = surface->resource.device->surface_alignment;
1514 src_pitch = format->byte_count * surface->pow2Width;
1515 dst_pitch = wined3d_surface_get_pitch(surface);
1516 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1517 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1519 else
1521 mem = data.addr;
1524 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1525 surface, surface->texture_level, gl_format, gl_type, mem);
1527 if (data.buffer_object)
1529 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1530 checkGLcall("glBindBufferARB");
1532 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1533 gl_format, gl_type, NULL);
1534 checkGLcall("glGetTexImage");
1536 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1537 checkGLcall("glBindBufferARB");
1539 else
1541 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1542 gl_format, gl_type, mem);
1543 checkGLcall("glGetTexImage");
1546 if (surface->flags & SFLAG_NONPOW2)
1548 const BYTE *src_data;
1549 BYTE *dst_data;
1550 UINT y;
1552 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1553 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1554 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1556 * We're doing this...
1558 * instead of boxing the texture :
1559 * |<-texture width ->| -->pow2width| /\
1560 * |111111111111111111| | |
1561 * |222 Texture 222222| boxed empty | texture height
1562 * |3333 Data 33333333| | |
1563 * |444444444444444444| | \/
1564 * ----------------------------------- |
1565 * | boxed empty | boxed empty | pow2height
1566 * | | | \/
1567 * -----------------------------------
1570 * we're repacking the data to the expected texture width
1572 * |<-texture width ->| -->pow2width| /\
1573 * |111111111111111111222222222222222| |
1574 * |222333333333333333333444444444444| texture height
1575 * |444444 | |
1576 * | | \/
1577 * | | |
1578 * | empty | pow2height
1579 * | | \/
1580 * -----------------------------------
1582 * == is the same as
1584 * |<-texture width ->| /\
1585 * |111111111111111111|
1586 * |222222222222222222|texture height
1587 * |333333333333333333|
1588 * |444444444444444444| \/
1589 * --------------------
1591 * This also means that any references to surface memory should work with the data as if it were a
1592 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1594 * internally the texture is still stored in a boxed format so any references to textureName will
1595 * get a boxed texture with width pow2width and not a texture of width resource.width.
1597 * Performance should not be an issue, because applications normally do not lock the surfaces when
1598 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
1599 * and doesn't have to be re-read. */
1600 src_data = mem;
1601 dst_data = data.addr;
1602 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1603 for (y = 0; y < surface->resource.height; ++y)
1605 memcpy(dst_data, src_data, dst_pitch);
1606 src_data += src_pitch;
1607 dst_data += dst_pitch;
1610 HeapFree(GetProcessHeap(), 0, mem);
1615 /* This call just uploads data, the caller is responsible for binding the
1616 * correct texture. */
1617 /* Context activation is done by the caller. */
1618 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1619 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1620 BOOL srgb, const struct wined3d_bo_address *data)
1622 UINT update_w = src_rect->right - src_rect->left;
1623 UINT update_h = src_rect->bottom - src_rect->top;
1625 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1626 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1627 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1629 if (surface->resource.map_count)
1631 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
1632 surface->flags |= SFLAG_PIN_SYSMEM;
1635 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1637 update_h *= format->height_scale.numerator;
1638 update_h /= format->height_scale.denominator;
1641 if (data->buffer_object)
1643 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
1644 checkGLcall("glBindBufferARB");
1647 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1649 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1650 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1651 const BYTE *addr = data->addr;
1652 GLenum internal;
1654 addr += (src_rect->top / format->block_height) * src_pitch;
1655 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1657 if (srgb)
1658 internal = format->glGammaInternal;
1659 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
1660 internal = format->rtInternal;
1661 else
1662 internal = format->glInternal;
1664 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
1665 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1666 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1668 if (row_length == src_pitch)
1670 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1671 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1673 else
1675 UINT row, y;
1677 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
1678 * can't use the unpack row length like below. */
1679 for (row = 0, y = dst_point->y; row < row_count; ++row)
1681 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1682 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1683 y += format->block_height;
1684 addr += src_pitch;
1687 checkGLcall("glCompressedTexSubImage2DARB");
1689 else
1691 const BYTE *addr = data->addr;
1693 addr += src_rect->top * src_pitch;
1694 addr += src_rect->left * format->byte_count;
1696 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1697 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1698 update_w, update_h, format->glFormat, format->glType, addr);
1700 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1701 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1702 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1703 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1704 checkGLcall("glTexSubImage2D");
1707 if (data->buffer_object)
1709 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1710 checkGLcall("glBindBufferARB");
1713 if (wined3d_settings.strict_draw_ordering)
1714 gl_info->gl_ops.gl.p_glFlush();
1716 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1718 struct wined3d_device *device = surface->resource.device;
1719 unsigned int i;
1721 for (i = 0; i < device->context_count; ++i)
1723 context_surface_update(device->contexts[i], surface);
1728 static HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck, BOOL use_texturing,
1729 struct wined3d_format *format, enum wined3d_conversion_type *conversion_type)
1731 BOOL colorkey_active = need_alpha_ck && (surface->container->color_key_flags & WINEDDSD_CKSRCBLT);
1732 const struct wined3d_device *device = surface->resource.device;
1733 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1734 BOOL blit_supported = FALSE;
1736 /* Copy the default values from the surface. Below we might perform fixups */
1737 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
1738 *format = *surface->resource.format;
1739 *conversion_type = WINED3D_CT_NONE;
1741 /* Ok, now look if we have to do any conversion */
1742 switch (surface->resource.format->id)
1744 case WINED3DFMT_P8_UINT:
1745 /* Below the call to blit_supported is disabled for Wine 1.2
1746 * because the function isn't operating correctly yet. At the
1747 * moment 8-bit blits are handled in software and if certain GL
1748 * extensions are around, surface conversion is performed at
1749 * upload time. The blit_supported call recognizes it as a
1750 * destination fixup. This type of upload 'fixup' and 8-bit to
1751 * 8-bit blits need to be handled by the blit_shader.
1752 * TODO: get rid of this #if 0. */
1753 #if 0
1754 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1755 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
1756 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
1757 #endif
1758 blit_supported = gl_info->supported[ARB_FRAGMENT_PROGRAM];
1760 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
1761 * texturing. Further also use conversion in case of color keying.
1762 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1763 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1764 * conflicts with this.
1766 if (!((blit_supported && surface->swapchain && surface == surface->swapchain->front_buffer))
1767 || colorkey_active || !use_texturing)
1769 format->glFormat = GL_RGBA;
1770 format->glInternal = GL_RGBA;
1771 format->glType = GL_UNSIGNED_BYTE;
1772 format->conv_byte_count = 4;
1773 if (colorkey_active)
1774 *conversion_type = WINED3D_CT_PALETTED_CK;
1775 else
1776 *conversion_type = WINED3D_CT_PALETTED;
1778 break;
1780 case WINED3DFMT_B2G3R3_UNORM:
1781 /* **********************
1782 GL_UNSIGNED_BYTE_3_3_2
1783 ********************** */
1784 if (colorkey_active) {
1785 /* This texture format will never be used.. So do not care about color keying
1786 up until the point in time it will be needed :-) */
1787 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1789 break;
1791 case WINED3DFMT_B5G6R5_UNORM:
1792 if (colorkey_active)
1794 *conversion_type = WINED3D_CT_CK_565;
1795 format->glFormat = GL_RGBA;
1796 format->glInternal = GL_RGB5_A1;
1797 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
1798 format->conv_byte_count = 2;
1800 break;
1802 case WINED3DFMT_B5G5R5X1_UNORM:
1803 if (colorkey_active)
1805 *conversion_type = WINED3D_CT_CK_5551;
1806 format->glFormat = GL_BGRA;
1807 format->glInternal = GL_RGB5_A1;
1808 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1809 format->conv_byte_count = 2;
1811 break;
1813 case WINED3DFMT_B8G8R8_UNORM:
1814 if (colorkey_active)
1816 *conversion_type = WINED3D_CT_CK_RGB24;
1817 format->glFormat = GL_RGBA;
1818 format->glInternal = GL_RGBA8;
1819 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1820 format->conv_byte_count = 4;
1822 break;
1824 case WINED3DFMT_B8G8R8X8_UNORM:
1825 if (colorkey_active)
1827 *conversion_type = WINED3D_CT_RGB32_888;
1828 format->glFormat = GL_RGBA;
1829 format->glInternal = GL_RGBA8;
1830 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1831 format->conv_byte_count = 4;
1833 break;
1835 case WINED3DFMT_B8G8R8A8_UNORM:
1836 if (colorkey_active)
1838 *conversion_type = WINED3D_CT_CK_ARGB32;
1839 format->conv_byte_count = 4;
1841 break;
1843 default:
1844 break;
1847 if (*conversion_type != WINED3D_CT_NONE)
1849 format->rtInternal = format->glInternal;
1850 format->glGammaInternal = format->glInternal;
1853 return WINED3D_OK;
1856 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
1858 UINT width_mask, height_mask;
1860 if (!rect->left && !rect->top
1861 && rect->right == surface->resource.width
1862 && rect->bottom == surface->resource.height)
1863 return TRUE;
1865 /* This assumes power of two block sizes, but NPOT block sizes would be
1866 * silly anyway. */
1867 width_mask = surface->resource.format->block_width - 1;
1868 height_mask = surface->resource.format->block_height - 1;
1870 if (!(rect->left & width_mask) && !(rect->top & height_mask)
1871 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
1872 return TRUE;
1874 return FALSE;
1877 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1878 struct wined3d_surface *src_surface, const RECT *src_rect)
1880 const struct wined3d_format *src_format;
1881 const struct wined3d_format *dst_format;
1882 const struct wined3d_gl_info *gl_info;
1883 enum wined3d_conversion_type convert;
1884 struct wined3d_context *context;
1885 struct wined3d_bo_address data;
1886 struct wined3d_format format;
1887 UINT update_w, update_h;
1888 UINT dst_w, dst_h;
1889 RECT r, dst_rect;
1890 UINT src_pitch;
1891 POINT p;
1893 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1894 dst_surface, wine_dbgstr_point(dst_point),
1895 src_surface, wine_dbgstr_rect(src_rect));
1897 src_format = src_surface->resource.format;
1898 dst_format = dst_surface->resource.format;
1900 if (src_format->id != dst_format->id)
1902 WARN("Source and destination surfaces should have the same format.\n");
1903 return WINED3DERR_INVALIDCALL;
1906 if (!dst_point)
1908 p.x = 0;
1909 p.y = 0;
1910 dst_point = &p;
1912 else if (dst_point->x < 0 || dst_point->y < 0)
1914 WARN("Invalid destination point.\n");
1915 return WINED3DERR_INVALIDCALL;
1918 if (!src_rect)
1920 r.left = 0;
1921 r.top = 0;
1922 r.right = src_surface->resource.width;
1923 r.bottom = src_surface->resource.height;
1924 src_rect = &r;
1926 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1927 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1929 WARN("Invalid source rectangle.\n");
1930 return WINED3DERR_INVALIDCALL;
1933 dst_w = dst_surface->resource.width;
1934 dst_h = dst_surface->resource.height;
1936 update_w = src_rect->right - src_rect->left;
1937 update_h = src_rect->bottom - src_rect->top;
1939 if (update_w > dst_w || dst_point->x > dst_w - update_w
1940 || update_h > dst_h || dst_point->y > dst_h - update_h)
1942 WARN("Destination out of bounds.\n");
1943 return WINED3DERR_INVALIDCALL;
1946 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
1948 WARN("Source rectangle not block-aligned.\n");
1949 return WINED3DERR_INVALIDCALL;
1952 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1953 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
1955 WARN("Destination rectangle not block-aligned.\n");
1956 return WINED3DERR_INVALIDCALL;
1959 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1960 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
1961 if (convert != WINED3D_CT_NONE || format.convert)
1962 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1964 context = context_acquire(dst_surface->resource.device, NULL);
1965 gl_info = context->gl_info;
1967 /* Only load the surface for partial updates. For newly allocated texture
1968 * the texture wouldn't be the current location, and we'd upload zeroes
1969 * just to overwrite them again. */
1970 if (update_w == dst_w && update_h == dst_h)
1971 surface_prepare_texture(dst_surface, context, FALSE);
1972 else
1973 surface_load_location(dst_surface, SFLAG_INTEXTURE);
1974 wined3d_texture_bind(dst_surface->container, context, FALSE);
1976 surface_get_memory(src_surface, &data, src_surface->locations);
1977 src_pitch = wined3d_surface_get_pitch(src_surface);
1979 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
1981 context_invalidate_active_texture(context);
1983 context_release(context);
1985 surface_validate_location(dst_surface, SFLAG_INTEXTURE);
1986 surface_invalidate_location(dst_surface, ~SFLAG_INTEXTURE);
1988 return WINED3D_OK;
1991 /* This call just allocates the texture, the caller is responsible for binding
1992 * the correct texture. */
1993 /* Context activation is done by the caller. */
1994 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1995 const struct wined3d_format *format, BOOL srgb)
1997 BOOL disable_client_storage = FALSE;
1998 GLsizei width = surface->pow2Width;
1999 GLsizei height = surface->pow2Height;
2000 const BYTE *mem = NULL;
2001 GLenum internal;
2003 if (srgb)
2005 internal = format->glGammaInternal;
2007 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2009 internal = format->rtInternal;
2011 else
2013 internal = format->glInternal;
2016 if (!internal)
2017 FIXME("No GL internal format for format %s.\n", debug_d3dformat(format->id));
2019 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
2021 height *= format->height_scale.numerator;
2022 height /= format->height_scale.denominator;
2025 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",
2026 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2027 internal, width, height, format->glFormat, format->glType);
2029 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2031 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2032 || !surface->resource.heap_memory)
2034 /* In some cases we want to disable client storage.
2035 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2036 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2037 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2038 * heap_memory == NULL: Not defined in the extension. Seems to disable client storage effectively
2040 surface->flags &= ~SFLAG_CLIENT;
2042 else
2044 surface->flags |= SFLAG_CLIENT;
2045 mem = surface->resource.heap_memory;
2047 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2048 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2049 disable_client_storage = TRUE;
2053 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2055 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2056 internal, width, height, 0, surface->resource.size, mem));
2057 checkGLcall("glCompressedTexImage2DARB");
2059 else
2061 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
2062 internal, width, height, 0, format->glFormat, format->glType, mem);
2063 checkGLcall("glTexImage2D");
2066 if (disable_client_storage)
2068 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2069 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2073 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2074 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2075 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2076 /* Context activation is done by the caller. */
2077 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2079 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2080 struct wined3d_renderbuffer_entry *entry;
2081 GLuint renderbuffer = 0;
2082 unsigned int src_width, src_height;
2083 unsigned int width, height;
2085 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2087 width = rt->pow2Width;
2088 height = rt->pow2Height;
2090 else
2092 width = surface->pow2Width;
2093 height = surface->pow2Height;
2096 src_width = surface->pow2Width;
2097 src_height = surface->pow2Height;
2099 /* A depth stencil smaller than the render target is not valid */
2100 if (width > src_width || height > src_height) return;
2102 /* Remove any renderbuffer set if the sizes match */
2103 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2104 || (width == src_width && height == src_height))
2106 surface->current_renderbuffer = NULL;
2107 return;
2110 /* Look if we've already got a renderbuffer of the correct dimensions */
2111 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2113 if (entry->width == width && entry->height == height)
2115 renderbuffer = entry->id;
2116 surface->current_renderbuffer = entry;
2117 break;
2121 if (!renderbuffer)
2123 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2124 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2125 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2126 surface->resource.format->glInternal, width, height);
2128 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2129 entry->width = width;
2130 entry->height = height;
2131 entry->id = renderbuffer;
2132 list_add_head(&surface->renderbuffers, &entry->entry);
2134 surface->current_renderbuffer = entry;
2137 checkGLcall("set_compatible_renderbuffer");
2140 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2142 const struct wined3d_swapchain *swapchain = surface->swapchain;
2144 TRACE("surface %p.\n", surface);
2146 if (!swapchain)
2148 ERR("Surface %p is not on a swapchain.\n", surface);
2149 return GL_NONE;
2152 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2154 if (swapchain->render_to_fbo)
2156 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2157 return GL_COLOR_ATTACHMENT0;
2159 TRACE("Returning GL_BACK\n");
2160 return GL_BACK;
2162 else if (surface == swapchain->front_buffer)
2164 TRACE("Returning GL_FRONT\n");
2165 return GL_FRONT;
2168 FIXME("Higher back buffer, returning GL_BACK\n");
2169 return GL_BACK;
2172 void surface_load(struct wined3d_surface *surface, BOOL srgb)
2174 DWORD location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2175 BOOL ck_changed;
2177 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2179 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
2180 ERR("Not supported on scratch surfaces.\n");
2182 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->container->color_key_flags & WINEDDSD_CKSRCBLT);
2184 /* Reload if either the texture and sysmem have different ideas about the
2185 * color key, or the actual key values changed. */
2186 if (ck_changed || ((surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
2187 && (surface->gl_color_key.color_space_low_value
2188 != surface->container->src_blt_color_key.color_space_low_value
2189 || surface->gl_color_key.color_space_high_value
2190 != surface->container->src_blt_color_key.color_space_high_value)))
2192 TRACE("Reloading because of color keying\n");
2193 /* To perform the color key conversion we need a sysmem copy of
2194 * the surface. Make sure we have it. */
2196 surface_prepare_map_memory(surface);
2197 surface_load_location(surface, surface->map_binding);
2198 surface_invalidate_location(surface, ~surface->map_binding);
2199 /* Switching color keying on / off may change the internal format. */
2200 if (ck_changed)
2201 surface_force_reload(surface);
2203 else if (!(surface->locations & location))
2205 TRACE("Reloading because surface is dirty.\n");
2207 else
2209 TRACE("surface is already in texture\n");
2210 return;
2213 surface_load_location(surface, location);
2214 surface_evict_sysmem(surface);
2217 /* See also float_16_to_32() in wined3d_private.h */
2218 static inline unsigned short float_32_to_16(const float *in)
2220 int exp = 0;
2221 float tmp = fabsf(*in);
2222 unsigned int mantissa;
2223 unsigned short ret;
2225 /* Deal with special numbers */
2226 if (*in == 0.0f)
2227 return 0x0000;
2228 if (isnan(*in))
2229 return 0x7c01;
2230 if (isinf(*in))
2231 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2233 if (tmp < powf(2, 10))
2237 tmp = tmp * 2.0f;
2238 exp--;
2239 } while (tmp < powf(2, 10));
2241 else if (tmp >= powf(2, 11))
2245 tmp /= 2.0f;
2246 exp++;
2247 } while (tmp >= powf(2, 11));
2250 mantissa = (unsigned int)tmp;
2251 if (tmp - mantissa >= 0.5f)
2252 ++mantissa; /* Round to nearest, away from zero. */
2254 exp += 10; /* Normalize the mantissa. */
2255 exp += 15; /* Exponent is encoded with excess 15. */
2257 if (exp > 30) /* too big */
2259 ret = 0x7c00; /* INF */
2261 else if (exp <= 0)
2263 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2264 while (exp <= 0)
2266 mantissa = mantissa >> 1;
2267 ++exp;
2269 ret = mantissa & 0x3ff;
2271 else
2273 ret = (exp << 10) | (mantissa & 0x3ff);
2276 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2277 return ret;
2280 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2282 ULONG refcount;
2284 TRACE("surface %p, swapchain %p, container %p.\n",
2285 surface, surface->swapchain, surface->container);
2287 if (surface->swapchain)
2288 return wined3d_swapchain_incref(surface->swapchain);
2290 if (surface->container)
2291 return wined3d_texture_incref(surface->container);
2293 refcount = InterlockedIncrement(&surface->resource.ref);
2294 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2296 return refcount;
2299 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2301 ULONG refcount;
2303 TRACE("surface %p, swapchain %p, container %p.\n",
2304 surface, surface->swapchain, surface->container);
2306 if (surface->swapchain)
2307 return wined3d_swapchain_decref(surface->swapchain);
2309 if (surface->container)
2310 return wined3d_texture_decref(surface->container);
2312 refcount = InterlockedDecrement(&surface->resource.ref);
2313 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2315 if (!refcount)
2317 surface_cleanup(surface);
2318 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2320 TRACE("Destroyed surface %p.\n", surface);
2321 HeapFree(GetProcessHeap(), 0, surface);
2324 return refcount;
2327 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2329 return resource_set_priority(&surface->resource, priority);
2332 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2334 return resource_get_priority(&surface->resource);
2337 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2339 TRACE("surface %p.\n", surface);
2341 if (!surface->resource.device->d3d_initialized)
2343 ERR("D3D not initialized.\n");
2344 return;
2347 wined3d_texture_preload(surface->container);
2350 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2352 TRACE("surface %p.\n", surface);
2354 return surface->resource.parent;
2357 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2359 TRACE("surface %p.\n", surface);
2361 return &surface->resource;
2364 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2366 TRACE("surface %p, flags %#x.\n", surface, flags);
2368 switch (flags)
2370 case WINEDDGBS_CANBLT:
2371 case WINEDDGBS_ISBLTDONE:
2372 return WINED3D_OK;
2374 default:
2375 return WINED3DERR_INVALIDCALL;
2379 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2381 TRACE("surface %p, flags %#x.\n", surface, flags);
2383 /* XXX: DDERR_INVALIDSURFACETYPE */
2385 switch (flags)
2387 case WINEDDGFS_CANFLIP:
2388 case WINEDDGFS_ISFLIPDONE:
2389 return WINED3D_OK;
2391 default:
2392 return WINED3DERR_INVALIDCALL;
2396 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2398 TRACE("surface %p.\n", surface);
2400 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2401 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2404 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2406 TRACE("surface %p.\n", surface);
2408 surface->flags &= ~SFLAG_LOST;
2409 return WINED3D_OK;
2412 void CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2414 TRACE("surface %p, palette %p.\n", surface, palette);
2416 if (surface->palette == palette)
2418 TRACE("Nop palette change.\n");
2419 return;
2422 surface->palette = palette;
2423 if (palette)
2424 surface->surface_ops->surface_realize_palette(surface);
2427 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
2429 TRACE("surface %p.\n", surface);
2431 return surface->palette;
2434 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2436 const struct wined3d_format *format = surface->resource.format;
2437 DWORD pitch;
2439 TRACE("surface %p.\n", surface);
2441 if (surface->pitch)
2442 return surface->pitch;
2444 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
2446 /* Since compressed formats are block based, pitch means the amount of
2447 * bytes to the next row of block rather than the next row of pixels. */
2448 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
2449 pitch = row_block_count * format->block_byte_count;
2451 else
2453 unsigned char alignment = surface->resource.device->surface_alignment;
2454 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
2455 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2458 TRACE("Returning %u.\n", pitch);
2460 return pitch;
2463 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2465 LONG w, h;
2467 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2469 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2471 WARN("Not an overlay surface.\n");
2472 return WINEDDERR_NOTAOVERLAYSURFACE;
2475 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2476 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2477 surface->overlay_destrect.left = x;
2478 surface->overlay_destrect.top = y;
2479 surface->overlay_destrect.right = x + w;
2480 surface->overlay_destrect.bottom = y + h;
2482 return WINED3D_OK;
2485 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2487 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2489 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2491 TRACE("Not an overlay surface.\n");
2492 return WINEDDERR_NOTAOVERLAYSURFACE;
2495 if (!surface->overlay_dest)
2497 TRACE("Overlay not visible.\n");
2498 *x = 0;
2499 *y = 0;
2500 return WINEDDERR_OVERLAYNOTVISIBLE;
2503 *x = surface->overlay_destrect.left;
2504 *y = surface->overlay_destrect.top;
2506 TRACE("Returning position %d, %d.\n", *x, *y);
2508 return WINED3D_OK;
2511 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2512 DWORD flags, struct wined3d_surface *ref)
2514 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2516 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2518 TRACE("Not an overlay surface.\n");
2519 return WINEDDERR_NOTAOVERLAYSURFACE;
2522 return WINED3D_OK;
2525 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2526 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2528 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2529 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2531 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2533 WARN("Not an overlay surface.\n");
2534 return WINEDDERR_NOTAOVERLAYSURFACE;
2536 else if (!dst_surface)
2538 WARN("Dest surface is NULL.\n");
2539 return WINED3DERR_INVALIDCALL;
2542 if (src_rect)
2544 surface->overlay_srcrect = *src_rect;
2546 else
2548 surface->overlay_srcrect.left = 0;
2549 surface->overlay_srcrect.top = 0;
2550 surface->overlay_srcrect.right = surface->resource.width;
2551 surface->overlay_srcrect.bottom = surface->resource.height;
2554 if (dst_rect)
2556 surface->overlay_destrect = *dst_rect;
2558 else
2560 surface->overlay_destrect.left = 0;
2561 surface->overlay_destrect.top = 0;
2562 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2563 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2566 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2568 surface->overlay_dest = NULL;
2569 list_remove(&surface->overlay_entry);
2572 if (flags & WINEDDOVER_SHOW)
2574 if (surface->overlay_dest != dst_surface)
2576 surface->overlay_dest = dst_surface;
2577 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2580 else if (flags & WINEDDOVER_HIDE)
2582 /* tests show that the rectangles are erased on hide */
2583 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2584 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2585 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2586 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2587 surface->overlay_dest = NULL;
2590 return WINED3D_OK;
2593 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
2594 UINT width, UINT height, enum wined3d_format_id format_id,
2595 enum wined3d_multisample_type multisample_type, UINT multisample_quality,
2596 void *mem, UINT pitch)
2598 struct wined3d_device *device = surface->resource.device;
2599 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2600 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
2601 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height, 1);
2602 BOOL create_dib = FALSE;
2603 HRESULT hr;
2604 DWORD valid_location = 0;
2606 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u.\n",
2607 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type);
2609 if (!resource_size)
2610 return WINED3DERR_INVALIDCALL;
2612 if (surface->resource.map_count || (surface->flags & SFLAG_DCINUSE))
2614 WARN("Surface is mapped or the DC is in use.\n");
2615 return WINED3DERR_INVALIDCALL;
2618 if (device->d3d_initialized)
2619 surface->resource.resource_ops->resource_unload(&surface->resource);
2621 if (surface->flags & SFLAG_DIBSECTION)
2623 DeleteDC(surface->hDC);
2624 DeleteObject(surface->dib.DIBsection);
2625 surface->dib.bitmap_data = NULL;
2626 surface->flags &= ~SFLAG_DIBSECTION;
2627 create_dib = TRUE;
2630 surface->locations = 0;
2631 wined3d_resource_free_sysmem(&surface->resource);
2633 surface->resource.width = width;
2634 surface->resource.height = height;
2635 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2636 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2638 surface->pow2Width = width;
2639 surface->pow2Height = height;
2641 else
2643 surface->pow2Width = surface->pow2Height = 1;
2644 while (surface->pow2Width < width)
2645 surface->pow2Width <<= 1;
2646 while (surface->pow2Height < height)
2647 surface->pow2Height <<= 1;
2650 if (surface->pow2Width != width || surface->pow2Height != height)
2651 surface->flags |= SFLAG_NONPOW2;
2652 else
2653 surface->flags &= ~SFLAG_NONPOW2;
2655 surface->user_memory = mem;
2656 if (surface->user_memory)
2658 surface->map_binding = SFLAG_INUSERMEM;
2659 valid_location = SFLAG_INUSERMEM;
2661 surface->pitch = pitch;
2662 surface->resource.format = format;
2663 surface->resource.multisample_type = multisample_type;
2664 surface->resource.multisample_quality = multisample_quality;
2665 if (surface->pitch)
2666 surface->resource.size = height * surface->pitch;
2667 else
2668 surface->resource.size = resource_size;
2670 /* The format might be changed to a format that needs conversion.
2671 * If the surface didn't use PBOs previously but could now, don't
2672 * change it - whatever made us not use PBOs might come back, e.g.
2673 * color keys. */
2674 if (surface->map_binding == SFLAG_INBUFFER && !surface_use_pbo(surface))
2675 surface->map_binding = create_dib ? SFLAG_INDIB : SFLAG_INSYSMEM;
2677 if (create_dib)
2679 if (FAILED(hr = surface_create_dib_section(surface)))
2681 ERR("Failed to create dib section, hr %#x.\n", hr);
2682 return hr;
2684 if (!valid_location)
2685 valid_location = SFLAG_INDIB;
2688 if (!valid_location)
2690 surface_prepare_system_memory(surface);
2691 valid_location = SFLAG_INSYSMEM;
2694 surface_validate_location(surface, valid_location);
2696 return WINED3D_OK;
2699 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2700 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2702 unsigned short *dst_s;
2703 const float *src_f;
2704 unsigned int x, y;
2706 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2708 for (y = 0; y < h; ++y)
2710 src_f = (const float *)(src + y * pitch_in);
2711 dst_s = (unsigned short *) (dst + y * pitch_out);
2712 for (x = 0; x < w; ++x)
2714 dst_s[x] = float_32_to_16(src_f + x);
2719 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2720 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2722 static const unsigned char convert_5to8[] =
2724 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2725 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2726 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2727 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2729 static const unsigned char convert_6to8[] =
2731 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2732 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2733 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2734 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2735 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2736 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2737 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2738 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2740 unsigned int x, y;
2742 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2744 for (y = 0; y < h; ++y)
2746 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2747 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2748 for (x = 0; x < w; ++x)
2750 WORD pixel = src_line[x];
2751 dst_line[x] = 0xff000000
2752 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2753 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2754 | convert_5to8[(pixel & 0x001f)];
2759 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2760 * in both cases we're just setting the X / Alpha channel to 0xff. */
2761 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2762 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2764 unsigned int x, y;
2766 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2768 for (y = 0; y < h; ++y)
2770 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2771 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2773 for (x = 0; x < w; ++x)
2775 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2780 static inline BYTE cliptobyte(int x)
2782 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2785 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2786 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2788 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2789 unsigned int x, y;
2791 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2793 for (y = 0; y < h; ++y)
2795 const BYTE *src_line = src + y * pitch_in;
2796 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2797 for (x = 0; x < w; ++x)
2799 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2800 * C = Y - 16; D = U - 128; E = V - 128;
2801 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2802 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2803 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2804 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2805 * U and V are shared between the pixels. */
2806 if (!(x & 1)) /* For every even pixel, read new U and V. */
2808 d = (int) src_line[1] - 128;
2809 e = (int) src_line[3] - 128;
2810 r2 = 409 * e + 128;
2811 g2 = - 100 * d - 208 * e + 128;
2812 b2 = 516 * d + 128;
2814 c2 = 298 * ((int) src_line[0] - 16);
2815 dst_line[x] = 0xff000000
2816 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2817 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2818 | cliptobyte((c2 + b2) >> 8); /* blue */
2819 /* Scale RGB values to 0..255 range,
2820 * then clip them if still not in range (may be negative),
2821 * then shift them within DWORD if necessary. */
2822 src_line += 2;
2827 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2828 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2830 unsigned int x, y;
2831 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2833 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2835 for (y = 0; y < h; ++y)
2837 const BYTE *src_line = src + y * pitch_in;
2838 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2839 for (x = 0; x < w; ++x)
2841 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2842 * C = Y - 16; D = U - 128; E = V - 128;
2843 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2844 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2845 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2846 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2847 * U and V are shared between the pixels. */
2848 if (!(x & 1)) /* For every even pixel, read new U and V. */
2850 d = (int) src_line[1] - 128;
2851 e = (int) src_line[3] - 128;
2852 r2 = 409 * e + 128;
2853 g2 = - 100 * d - 208 * e + 128;
2854 b2 = 516 * d + 128;
2856 c2 = 298 * ((int) src_line[0] - 16);
2857 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2858 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2859 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2860 /* Scale RGB values to 0..255 range,
2861 * then clip them if still not in range (may be negative),
2862 * then shift them within DWORD if necessary. */
2863 src_line += 2;
2868 struct d3dfmt_converter_desc
2870 enum wined3d_format_id from, to;
2871 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2874 static const struct d3dfmt_converter_desc converters[] =
2876 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2877 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2878 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2879 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2880 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2881 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2884 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2885 enum wined3d_format_id to)
2887 unsigned int i;
2889 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2891 if (converters[i].from == from && converters[i].to == to)
2892 return &converters[i];
2895 return NULL;
2898 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2900 struct wined3d_map_desc src_map, dst_map;
2901 const struct d3dfmt_converter_desc *conv;
2902 struct wined3d_texture *ret = NULL;
2903 struct wined3d_resource_desc desc;
2904 struct wined3d_surface *dst;
2906 conv = find_converter(source->resource.format->id, to_fmt);
2907 if (!conv)
2909 FIXME("Cannot find a conversion function from format %s to %s.\n",
2910 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2911 return NULL;
2914 /* FIXME: Multisampled conversion? */
2915 wined3d_resource_get_desc(&source->resource, &desc);
2916 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2917 desc.format = to_fmt;
2918 desc.usage = 0;
2919 desc.pool = WINED3D_POOL_SCRATCH;
2920 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2921 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, &wined3d_null_parent_ops, &ret)))
2923 ERR("Failed to create a destination surface for conversion.\n");
2924 return NULL;
2926 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2928 memset(&src_map, 0, sizeof(src_map));
2929 memset(&dst_map, 0, sizeof(dst_map));
2931 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2933 ERR("Failed to lock the source surface.\n");
2934 wined3d_texture_decref(ret);
2935 return NULL;
2937 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2939 ERR("Failed to lock the destination surface.\n");
2940 wined3d_surface_unmap(source);
2941 wined3d_texture_decref(ret);
2942 return NULL;
2945 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2946 source->resource.width, source->resource.height);
2948 wined3d_surface_unmap(dst);
2949 wined3d_surface_unmap(source);
2951 return ret;
2954 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2955 unsigned int bpp, UINT pitch, DWORD color)
2957 BYTE *first;
2958 unsigned int x, y;
2960 /* Do first row */
2962 #define COLORFILL_ROW(type) \
2963 do { \
2964 type *d = (type *)buf; \
2965 for (x = 0; x < width; ++x) \
2966 d[x] = (type)color; \
2967 } while(0)
2969 switch (bpp)
2971 case 1:
2972 COLORFILL_ROW(BYTE);
2973 break;
2975 case 2:
2976 COLORFILL_ROW(WORD);
2977 break;
2979 case 3:
2981 BYTE *d = buf;
2982 for (x = 0; x < width; ++x, d += 3)
2984 d[0] = (color ) & 0xff;
2985 d[1] = (color >> 8) & 0xff;
2986 d[2] = (color >> 16) & 0xff;
2988 break;
2990 case 4:
2991 COLORFILL_ROW(DWORD);
2992 break;
2994 default:
2995 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2996 return WINED3DERR_NOTAVAILABLE;
2999 #undef COLORFILL_ROW
3001 /* Now copy first row. */
3002 first = buf;
3003 for (y = 1; y < height; ++y)
3005 buf += pitch;
3006 memcpy(buf, first, width * bpp);
3009 return WINED3D_OK;
3012 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
3014 return surface_from_resource(resource);
3017 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3019 TRACE("surface %p.\n", surface);
3021 if (!surface->resource.map_count)
3023 WARN("Trying to unmap unmapped surface.\n");
3024 return WINEDDERR_NOTLOCKED;
3026 --surface->resource.map_count;
3028 surface->surface_ops->surface_unmap(surface);
3030 return WINED3D_OK;
3033 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3034 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
3036 const struct wined3d_format *format = surface->resource.format;
3037 struct wined3d_device *device = surface->resource.device;
3038 struct wined3d_context *context;
3039 const struct wined3d_gl_info *gl_info;
3040 BYTE *base_memory;
3042 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
3043 surface, map_desc, wine_dbgstr_rect(rect), flags);
3045 if (surface->resource.map_count)
3047 WARN("Surface is already mapped.\n");
3048 return WINED3DERR_INVALIDCALL;
3051 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
3052 && !surface_check_block_align(surface, rect))
3054 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3055 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3057 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
3058 return WINED3DERR_INVALIDCALL;
3061 ++surface->resource.map_count;
3063 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
3064 WARN("Trying to lock unlockable surface.\n");
3066 /* Performance optimization: Count how often a surface is mapped, if it is
3067 * mapped regularly do not throw away the system memory copy. This avoids
3068 * the need to download the surface from OpenGL all the time. The surface
3069 * is still downloaded if the OpenGL texture is changed. */
3070 if (!(surface->flags & SFLAG_DYNLOCK))
3072 if (++surface->lockCount > MAXLOCKCOUNT)
3074 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3075 surface->flags |= SFLAG_DYNLOCK;
3079 surface_prepare_map_memory(surface);
3080 if (flags & WINED3D_MAP_DISCARD)
3082 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
3083 debug_surflocation(surface->map_binding));
3084 surface_validate_location(surface, surface->map_binding);
3086 else
3088 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
3089 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
3091 surface_load_location(surface, surface->map_binding);
3094 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
3095 surface_invalidate_location(surface, ~surface->map_binding);
3097 switch (surface->map_binding)
3099 case SFLAG_INUSERMEM:
3100 base_memory = surface->user_memory;
3101 break;
3103 case SFLAG_INDIB:
3104 base_memory = surface->dib.bitmap_data;
3105 break;
3107 case SFLAG_INBUFFER:
3108 context = context_acquire(device, NULL);
3109 gl_info = context->gl_info;
3111 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
3112 base_memory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3113 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
3114 checkGLcall("map PBO");
3116 context_release(context);
3117 break;
3119 case SFLAG_INSYSMEM:
3120 base_memory = surface->resource.heap_memory;
3121 break;
3123 default:
3124 ERR("Unexpected map binding %s.\n", debug_surflocation(surface->map_binding));
3125 base_memory = NULL;
3128 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3129 map_desc->row_pitch = surface->resource.width * format->byte_count;
3130 else
3131 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
3132 map_desc->slice_pitch = 0;
3134 if (!rect)
3136 map_desc->data = base_memory;
3137 surface->lockedRect.left = 0;
3138 surface->lockedRect.top = 0;
3139 surface->lockedRect.right = surface->resource.width;
3140 surface->lockedRect.bottom = surface->resource.height;
3142 else
3144 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3146 /* Compressed textures are block based, so calculate the offset of
3147 * the block that contains the top-left pixel of the locked rectangle. */
3148 map_desc->data = base_memory
3149 + ((rect->top / format->block_height) * map_desc->row_pitch)
3150 + ((rect->left / format->block_width) * format->block_byte_count);
3152 else
3154 map_desc->data = base_memory
3155 + (map_desc->row_pitch * rect->top)
3156 + (rect->left * format->byte_count);
3158 surface->lockedRect.left = rect->left;
3159 surface->lockedRect.top = rect->top;
3160 surface->lockedRect.right = rect->right;
3161 surface->lockedRect.bottom = rect->bottom;
3164 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3165 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
3167 return WINED3D_OK;
3170 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3172 HRESULT hr;
3174 TRACE("surface %p, dc %p.\n", surface, dc);
3176 /* Give more detailed info for ddraw. */
3177 if (surface->flags & SFLAG_DCINUSE)
3178 return WINEDDERR_DCALREADYCREATED;
3180 /* Can't GetDC if the surface is locked. */
3181 if (surface->resource.map_count)
3182 return WINED3DERR_INVALIDCALL;
3184 /* Create a DIB section if there isn't a dc yet. */
3185 if (!surface->hDC)
3187 if (surface->flags & SFLAG_CLIENT)
3189 surface_load_location(surface, SFLAG_INSYSMEM);
3190 surface_release_client_storage(surface);
3192 hr = surface_create_dib_section(surface);
3193 if (FAILED(hr))
3194 return WINED3DERR_INVALIDCALL;
3195 if (!(surface->map_binding == SFLAG_INUSERMEM
3196 || surface->flags & SFLAG_PIN_SYSMEM
3197 || surface->pbo))
3198 surface->map_binding = SFLAG_INDIB;
3201 surface_load_location(surface, SFLAG_INDIB);
3202 surface_invalidate_location(surface, ~SFLAG_INDIB);
3204 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3205 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3207 /* GetDC on palettized formats is unsupported in D3D9, and the method
3208 * is missing in D3D8, so this should only be used for DX <=7
3209 * surfaces (with non-device palettes). */
3210 const PALETTEENTRY *pal = NULL;
3212 if (surface->palette)
3214 pal = surface->palette->palents;
3216 else
3218 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3219 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3221 if (dds_primary && dds_primary->palette)
3222 pal = dds_primary->palette->palents;
3225 if (pal)
3227 RGBQUAD col[256];
3228 unsigned int i;
3230 for (i = 0; i < 256; ++i)
3232 col[i].rgbRed = pal[i].peRed;
3233 col[i].rgbGreen = pal[i].peGreen;
3234 col[i].rgbBlue = pal[i].peBlue;
3235 col[i].rgbReserved = 0;
3237 SetDIBColorTable(surface->hDC, 0, 256, col);
3241 surface->flags |= SFLAG_DCINUSE;
3242 surface->resource.map_count++;
3244 *dc = surface->hDC;
3245 TRACE("Returning dc %p.\n", *dc);
3247 return WINED3D_OK;
3250 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3252 TRACE("surface %p, dc %p.\n", surface, dc);
3254 if (!(surface->flags & SFLAG_DCINUSE))
3255 return WINEDDERR_NODC;
3257 if (surface->hDC != dc)
3259 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3260 dc, surface->hDC);
3261 return WINEDDERR_NODC;
3264 surface->resource.map_count--;
3265 surface->flags &= ~SFLAG_DCINUSE;
3267 if (surface->map_binding == SFLAG_INUSERMEM)
3268 surface_load_location(surface, SFLAG_INUSERMEM);
3270 return WINED3D_OK;
3273 static void read_from_framebuffer(struct wined3d_surface *surface, DWORD dst_location)
3275 struct wined3d_device *device = surface->resource.device;
3276 const struct wined3d_gl_info *gl_info;
3277 struct wined3d_context *context;
3278 BYTE *mem;
3279 GLint fmt;
3280 GLint type;
3281 BYTE *row, *top, *bottom;
3282 int i;
3283 BOOL bpp;
3284 BOOL srcIsUpsideDown;
3285 struct wined3d_bo_address data;
3286 UINT pitch = wined3d_surface_get_pitch(surface);
3288 surface_get_memory(surface, &data, dst_location);
3290 context = context_acquire(device, surface);
3291 context_apply_blit_state(context, device);
3292 gl_info = context->gl_info;
3294 /* Select the correct read buffer, and give some debug output.
3295 * There is no need to keep track of the current read buffer or reset it, every part of the code
3296 * that reads sets the read buffer as desired.
3298 if (surface_is_offscreen(surface))
3300 /* Mapping the primary render target which is not on a swapchain.
3301 * Read from the back buffer. */
3302 TRACE("Mapping offscreen render target.\n");
3303 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3304 srcIsUpsideDown = TRUE;
3306 else
3308 /* Onscreen surfaces are always part of a swapchain */
3309 GLenum buffer = surface_get_gl_buffer(surface);
3310 TRACE("Mapping %#x buffer.\n", buffer);
3311 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
3312 checkGLcall("glReadBuffer");
3313 srcIsUpsideDown = FALSE;
3316 switch (surface->resource.format->id)
3318 case WINED3DFMT_P8_UINT:
3320 if (swapchain_is_p8(context->swapchain))
3322 /* In case of P8 render targets the index is stored in the alpha component */
3323 fmt = GL_ALPHA;
3324 type = GL_UNSIGNED_BYTE;
3325 mem = data.addr;
3326 bpp = surface->resource.format->byte_count;
3328 else
3330 /* GL can't return palettized data, so read ARGB pixels into a
3331 * separate block of memory and convert them into palettized format
3332 * in software. Slow, but if the app means to use palettized render
3333 * targets and locks it...
3335 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
3336 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
3337 * for the color channels when palettizing the colors.
3339 fmt = GL_RGB;
3340 type = GL_UNSIGNED_BYTE;
3341 pitch *= 3;
3342 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
3343 if (!mem)
3345 ERR("Out of memory\n");
3346 return;
3348 bpp = surface->resource.format->byte_count * 3;
3351 break;
3353 default:
3354 mem = data.addr;
3355 fmt = surface->resource.format->glFormat;
3356 type = surface->resource.format->glType;
3357 bpp = surface->resource.format->byte_count;
3360 if (data.buffer_object)
3362 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
3363 checkGLcall("glBindBufferARB");
3364 if (mem)
3365 ERR("mem not null for pbo -- unexpected\n");
3368 /* Setup pixel store pack state -- to glReadPixels into the correct place */
3369 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
3370 checkGLcall("glPixelStorei");
3372 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
3373 surface->resource.width, surface->resource.height,
3374 fmt, type, mem);
3375 checkGLcall("glReadPixels");
3377 /* Reset previous pixel store pack state */
3378 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
3379 checkGLcall("glPixelStorei");
3381 if (data.buffer_object && !srcIsUpsideDown)
3383 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
3384 * to get a pointer to it and perform the flipping in software. This is a lot
3385 * faster than calling glReadPixels for each line. In case we want more speed
3386 * we should rerender it flipped in a FBO and read the data back from the FBO. */
3387 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3388 checkGLcall("glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB)");
3391 /* TODO: Merge this with the palettization loop below for P8 targets */
3392 if (!srcIsUpsideDown)
3394 UINT len;
3395 /* glReadPixels returns the image upside down, and there is no way to prevent this.
3396 Flip the lines in software */
3397 len = surface->resource.width * bpp;
3399 row = HeapAlloc(GetProcessHeap(), 0, len);
3400 if (!row)
3402 ERR("Out of memory\n");
3403 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
3404 HeapFree(GetProcessHeap(), 0, mem);
3405 return;
3408 top = mem;
3409 bottom = mem + pitch * (surface->resource.height - 1);
3410 for (i = 0; i < surface->resource.height / 2; i++)
3412 memcpy(row, top, len);
3413 memcpy(top, bottom, len);
3414 memcpy(bottom, row, len);
3415 top += pitch;
3416 bottom -= pitch;
3418 HeapFree(GetProcessHeap(), 0, row);
3421 if (data.buffer_object)
3423 if (!srcIsUpsideDown)
3424 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB));
3426 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
3427 checkGLcall("glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0)");
3430 context_release(context);
3432 /* For P8 textures we need to perform an inverse palette lookup. This is
3433 * done by searching for a palette index which matches the RGB value.
3434 * Note this isn't guaranteed to work when there are multiple entries for
3435 * the same color but we have no choice. In case of P8 render targets,
3436 * the index is stored in the alpha component so no conversion is needed. */
3437 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !swapchain_is_p8(context->swapchain))
3439 const PALETTEENTRY *pal = NULL;
3440 DWORD width = pitch / 3;
3441 int x, y, c;
3443 if (surface->palette)
3445 pal = surface->palette->palents;
3447 else
3449 ERR("Palette is missing, cannot perform inverse palette lookup\n");
3450 HeapFree(GetProcessHeap(), 0, mem);
3451 return;
3454 for (y = 0; y < surface->resource.height; y++)
3456 for (x = 0; x < surface->resource.width; x++)
3458 /* start lines pixels */
3459 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
3460 const BYTE *green = blue + 1;
3461 const BYTE *red = green + 1;
3463 for (c = 0; c < 256; c++)
3465 if (*red == pal[c].peRed
3466 && *green == pal[c].peGreen
3467 && *blue == pal[c].peBlue)
3469 *((BYTE *)data.addr + y * width + x) = c;
3470 break;
3475 HeapFree(GetProcessHeap(), 0, mem);
3479 /* Read the framebuffer contents into a texture. Note that this function
3480 * doesn't do any kind of flipping. Using this on an onscreen surface will
3481 * result in a flipped D3D texture. */
3482 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
3484 struct wined3d_device *device = surface->resource.device;
3485 const struct wined3d_gl_info *gl_info;
3486 struct wined3d_context *context;
3488 context = context_acquire(device, surface);
3489 gl_info = context->gl_info;
3490 device_invalidate_state(device, STATE_FRAMEBUFFER);
3492 surface_prepare_texture(surface, context, srgb);
3493 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3495 TRACE("Reading back offscreen render target %p.\n", surface);
3497 if (surface_is_offscreen(surface))
3498 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3499 else
3500 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
3501 checkGLcall("glReadBuffer");
3503 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
3504 0, 0, 0, 0, surface->resource.width, surface->resource.height);
3505 checkGLcall("glCopyTexSubImage2D");
3507 context_release(context);
3510 /* Context activation is done by the caller. */
3511 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
3512 struct wined3d_context *context, BOOL srgb)
3514 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
3515 enum wined3d_conversion_type convert;
3516 struct wined3d_format format;
3518 if (surface->flags & alloc_flag) return;
3520 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
3521 if (convert != WINED3D_CT_NONE || format.convert)
3522 surface->flags |= SFLAG_CONVERTED;
3523 else surface->flags &= ~SFLAG_CONVERTED;
3525 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3526 surface_allocate_surface(surface, context->gl_info, &format, srgb);
3527 surface->flags |= alloc_flag;
3530 /* Context activation is done by the caller. */
3531 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
3533 struct wined3d_texture *texture = surface->container;
3534 UINT sub_count = texture->level_count * texture->layer_count;
3535 UINT i;
3537 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
3539 for (i = 0; i < sub_count; ++i)
3541 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
3542 surface_prepare_texture_internal(s, context, srgb);
3545 return;
3548 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
3550 if (multisample)
3552 if (surface->rb_multisample)
3553 return;
3555 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
3556 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
3557 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
3558 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
3559 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
3561 else
3563 if (surface->rb_resolved)
3564 return;
3566 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
3567 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
3568 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
3569 surface->pow2Width, surface->pow2Height);
3570 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
3574 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
3576 /* FIXME: Is this really how color keys are supposed to work? I think it
3577 * makes more sense to compare the individual channels. */
3578 return color >= color_key->color_space_low_value
3579 && color <= color_key->color_space_high_value;
3582 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
3584 const struct wined3d_device *device = surface->resource.device;
3585 const struct wined3d_palette *pal = surface->palette;
3586 BOOL index_in_alpha = FALSE;
3587 unsigned int i;
3589 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
3590 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
3591 * is slow. Further RGB->P8 conversion is not possible because palettes can have
3592 * duplicate entries. Store the color key in the unused alpha component to speed the
3593 * download up and to make conversion unneeded. */
3594 index_in_alpha = swapchain_is_p8(device->swapchains[0]);
3596 if (!pal)
3598 FIXME("No palette set.\n");
3599 if (index_in_alpha)
3601 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
3602 * there's no palette at this time. */
3603 for (i = 0; i < 256; i++) table[i][3] = i;
3606 else
3608 TRACE("Using surface palette %p\n", pal);
3609 /* Get the surface's palette */
3610 for (i = 0; i < 256; ++i)
3612 table[i][0] = pal->palents[i].peRed;
3613 table[i][1] = pal->palents[i].peGreen;
3614 table[i][2] = pal->palents[i].peBlue;
3616 /* When index_in_alpha is set the palette index is stored in the
3617 * alpha component. In case of a readback we can then read
3618 * GL_ALPHA. Color keying is handled in surface_blt_special() using a
3619 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
3620 * color key itself is passed to glAlphaFunc in other cases the
3621 * alpha component of pixels that should be masked away is set to 0. */
3622 if (index_in_alpha)
3623 table[i][3] = i;
3624 else if (colorkey && color_in_range(&surface->container->src_blt_color_key, i))
3625 table[i][3] = 0x00;
3626 else if (pal->flags & WINED3D_PALETTE_ALPHA)
3627 table[i][3] = pal->palents[i].peFlags;
3628 else
3629 table[i][3] = 0xff;
3634 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
3635 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
3637 const BYTE *source;
3638 BYTE *dest;
3640 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
3641 src, dst, pitch, width, height, outpitch, conversion_type, surface);
3643 switch (conversion_type)
3645 case WINED3D_CT_NONE:
3647 memcpy(dst, src, pitch * height);
3648 break;
3651 case WINED3D_CT_PALETTED:
3652 case WINED3D_CT_PALETTED_CK:
3654 BYTE table[256][4];
3655 unsigned int x, y;
3657 d3dfmt_p8_init_palette(surface, table, (conversion_type == WINED3D_CT_PALETTED_CK));
3659 for (y = 0; y < height; y++)
3661 source = src + pitch * y;
3662 dest = dst + outpitch * y;
3663 /* This is an 1 bpp format, using the width here is fine */
3664 for (x = 0; x < width; x++) {
3665 BYTE color = *source++;
3666 *dest++ = table[color][0];
3667 *dest++ = table[color][1];
3668 *dest++ = table[color][2];
3669 *dest++ = table[color][3];
3673 break;
3675 case WINED3D_CT_CK_565:
3677 /* Converting the 565 format in 5551 packed to emulate color-keying.
3679 Note : in all these conversion, it would be best to average the averaging
3680 pixels to get the color of the pixel that will be color-keyed to
3681 prevent 'color bleeding'. This will be done later on if ever it is
3682 too visible.
3684 Note2: Nvidia documents say that their driver does not support alpha + color keying
3685 on the same surface and disables color keying in such a case
3687 unsigned int x, y;
3688 const WORD *Source;
3689 WORD *Dest;
3691 TRACE("Color keyed 565\n");
3693 for (y = 0; y < height; y++) {
3694 Source = (const WORD *)(src + y * pitch);
3695 Dest = (WORD *) (dst + y * outpitch);
3696 for (x = 0; x < width; x++ ) {
3697 WORD color = *Source++;
3698 *Dest = ((color & 0xffc0) | ((color & 0x1f) << 1));
3699 if (!color_in_range(&surface->container->src_blt_color_key, color))
3700 *Dest |= 0x0001;
3701 Dest++;
3705 break;
3707 case WINED3D_CT_CK_5551:
3709 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
3710 unsigned int x, y;
3711 const WORD *Source;
3712 WORD *Dest;
3713 TRACE("Color keyed 5551\n");
3714 for (y = 0; y < height; y++) {
3715 Source = (const WORD *)(src + y * pitch);
3716 Dest = (WORD *) (dst + y * outpitch);
3717 for (x = 0; x < width; x++ ) {
3718 WORD color = *Source++;
3719 *Dest = color;
3720 if (!color_in_range(&surface->container->src_blt_color_key, color))
3721 *Dest |= (1 << 15);
3722 else
3723 *Dest &= ~(1 << 15);
3724 Dest++;
3728 break;
3730 case WINED3D_CT_CK_RGB24:
3732 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
3733 unsigned int x, y;
3734 for (y = 0; y < height; y++)
3736 source = src + pitch * y;
3737 dest = dst + outpitch * y;
3738 for (x = 0; x < width; x++) {
3739 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
3740 DWORD dstcolor = color << 8;
3741 if (!color_in_range(&surface->container->src_blt_color_key, color))
3742 dstcolor |= 0xff;
3743 *(DWORD*)dest = dstcolor;
3744 source += 3;
3745 dest += 4;
3749 break;
3751 case WINED3D_CT_RGB32_888:
3753 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
3754 unsigned int x, y;
3755 for (y = 0; y < height; y++)
3757 source = src + pitch * y;
3758 dest = dst + outpitch * y;
3759 for (x = 0; x < width; x++) {
3760 DWORD color = 0xffffff & *(const DWORD*)source;
3761 DWORD dstcolor = color << 8;
3762 if (!color_in_range(&surface->container->src_blt_color_key, color))
3763 dstcolor |= 0xff;
3764 *(DWORD*)dest = dstcolor;
3765 source += 4;
3766 dest += 4;
3770 break;
3772 case WINED3D_CT_CK_ARGB32:
3774 unsigned int x, y;
3775 for (y = 0; y < height; ++y)
3777 source = src + pitch * y;
3778 dest = dst + outpitch * y;
3779 for (x = 0; x < width; ++x)
3781 DWORD color = *(const DWORD *)source;
3782 if (color_in_range(&surface->container->src_blt_color_key, color))
3783 color &= ~0xff000000;
3784 *(DWORD*)dest = color;
3785 source += 4;
3786 dest += 4;
3790 break;
3792 default:
3793 ERR("Unsupported conversion type %#x.\n", conversion_type);
3795 return WINED3D_OK;
3798 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
3800 if (front->container->level_count != 1 || front->container->layer_count != 1
3801 || back->container->level_count != 1 || back->container->layer_count != 1)
3802 ERR("Flip between surfaces %p and %p not supported.\n", front, back);
3804 /* Flip the surface contents */
3805 /* Flip the DC */
3807 HDC tmp;
3808 tmp = front->hDC;
3809 front->hDC = back->hDC;
3810 back->hDC = tmp;
3813 /* Flip the DIBsection */
3815 HBITMAP tmp = front->dib.DIBsection;
3816 front->dib.DIBsection = back->dib.DIBsection;
3817 back->dib.DIBsection = tmp;
3820 /* Flip the surface data */
3822 void* tmp;
3824 tmp = front->dib.bitmap_data;
3825 front->dib.bitmap_data = back->dib.bitmap_data;
3826 back->dib.bitmap_data = tmp;
3828 tmp = front->resource.heap_memory;
3829 front->resource.heap_memory = back->resource.heap_memory;
3830 back->resource.heap_memory = tmp;
3833 /* Flip the PBO */
3835 GLuint tmp_pbo = front->pbo;
3836 front->pbo = back->pbo;
3837 back->pbo = tmp_pbo;
3840 /* Flip the opengl texture */
3842 GLuint tmp;
3844 tmp = back->container->texture_rgb.name;
3845 back->container->texture_rgb.name = front->container->texture_rgb.name;
3846 front->container->texture_rgb.name = tmp;
3848 tmp = back->container->texture_srgb.name;
3849 back->container->texture_srgb.name = front->container->texture_srgb.name;
3850 front->container->texture_srgb.name = tmp;
3852 tmp = back->rb_multisample;
3853 back->rb_multisample = front->rb_multisample;
3854 front->rb_multisample = tmp;
3856 tmp = back->rb_resolved;
3857 back->rb_resolved = front->rb_resolved;
3858 front->rb_resolved = tmp;
3860 resource_unload(&back->resource);
3861 resource_unload(&front->resource);
3865 DWORD tmp_flags = back->flags;
3866 back->flags = front->flags;
3867 front->flags = tmp_flags;
3869 tmp_flags = back->locations;
3870 back->locations = front->locations;
3871 front->locations = tmp_flags;
3875 /* Does a direct frame buffer -> texture copy. Stretching is done with single
3876 * pixel copy calls. */
3877 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3878 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3880 struct wined3d_device *device = dst_surface->resource.device;
3881 const struct wined3d_gl_info *gl_info;
3882 float xrel, yrel;
3883 struct wined3d_context *context;
3884 BOOL upsidedown = FALSE;
3885 RECT dst_rect = *dst_rect_in;
3887 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3888 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3890 if(dst_rect.top > dst_rect.bottom) {
3891 UINT tmp = dst_rect.bottom;
3892 dst_rect.bottom = dst_rect.top;
3893 dst_rect.top = tmp;
3894 upsidedown = TRUE;
3897 context = context_acquire(device, src_surface);
3898 gl_info = context->gl_info;
3899 context_apply_blit_state(context, device);
3900 wined3d_texture_load(dst_surface->container, context, FALSE);
3902 /* Bind the target texture */
3903 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
3904 if (surface_is_offscreen(src_surface))
3906 TRACE("Reading from an offscreen target\n");
3907 upsidedown = !upsidedown;
3908 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3910 else
3912 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3914 checkGLcall("glReadBuffer");
3916 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3917 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3919 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3921 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3923 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3924 ERR("Texture filtering not supported in direct blit.\n");
3926 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3927 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3929 ERR("Texture filtering not supported in direct blit\n");
3932 if (upsidedown
3933 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3934 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3936 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
3937 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3938 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3939 src_rect->left, src_surface->resource.height - src_rect->bottom,
3940 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3942 else
3944 LONG row;
3945 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
3946 /* I have to process this row by row to swap the image,
3947 * otherwise it would be upside down, so stretching in y direction
3948 * doesn't cost extra time
3950 * However, stretching in x direction can be avoided if not necessary
3952 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3953 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3955 /* Well, that stuff works, but it's very slow.
3956 * find a better way instead
3958 LONG col;
3960 for (col = dst_rect.left; col < dst_rect.right; ++col)
3962 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3963 dst_rect.left + col /* x offset */, row /* y offset */,
3964 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3967 else
3969 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3970 dst_rect.left /* x offset */, row /* y offset */,
3971 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3975 checkGLcall("glCopyTexSubImage2D");
3977 context_release(context);
3979 /* The texture is now most up to date - If the surface is a render target
3980 * and has a drawable, this path is never entered. */
3981 surface_validate_location(dst_surface, SFLAG_INTEXTURE);
3982 surface_invalidate_location(dst_surface, ~SFLAG_INTEXTURE);
3985 /* Uses the hardware to stretch and flip the image */
3986 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3987 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3989 struct wined3d_device *device = dst_surface->resource.device;
3990 GLuint src, backup = 0;
3991 float left, right, top, bottom; /* Texture coordinates */
3992 UINT fbwidth = src_surface->resource.width;
3993 UINT fbheight = src_surface->resource.height;
3994 const struct wined3d_gl_info *gl_info;
3995 struct wined3d_context *context;
3996 GLenum drawBuffer = GL_BACK;
3997 GLenum texture_target;
3998 BOOL noBackBufferBackup;
3999 BOOL src_offscreen;
4000 BOOL upsidedown = FALSE;
4001 RECT dst_rect = *dst_rect_in;
4003 TRACE("Using hwstretch blit\n");
4004 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4005 context = context_acquire(device, src_surface);
4006 gl_info = context->gl_info;
4007 context_apply_blit_state(context, device);
4008 wined3d_texture_load(dst_surface->container, context, FALSE);
4010 src_offscreen = surface_is_offscreen(src_surface);
4011 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4012 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
4014 /* Get it a description */
4015 wined3d_texture_load(src_surface->container, context, FALSE);
4018 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4019 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4021 if (context->aux_buffers >= 2)
4023 /* Got more than one aux buffer? Use the 2nd aux buffer */
4024 drawBuffer = GL_AUX1;
4026 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4028 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4029 drawBuffer = GL_AUX0;
4032 if (noBackBufferBackup)
4034 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
4035 checkGLcall("glGenTextures");
4036 context_bind_texture(context, GL_TEXTURE_2D, backup);
4037 texture_target = GL_TEXTURE_2D;
4039 else
4041 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4042 * we are reading from the back buffer, the backup can be used as source texture
4044 texture_target = src_surface->texture_target;
4045 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
4046 gl_info->gl_ops.gl.p_glEnable(texture_target);
4047 checkGLcall("glEnable(texture_target)");
4049 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4050 src_surface->locations &= ~SFLAG_INTEXTURE;
4053 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4054 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4056 if(dst_rect.top > dst_rect.bottom) {
4057 UINT tmp = dst_rect.bottom;
4058 dst_rect.bottom = dst_rect.top;
4059 dst_rect.top = tmp;
4060 upsidedown = TRUE;
4063 if (src_offscreen)
4065 TRACE("Reading from an offscreen target\n");
4066 upsidedown = !upsidedown;
4067 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
4069 else
4071 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
4074 /* TODO: Only back up the part that will be overwritten */
4075 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
4077 checkGLcall("glCopyTexSubImage2D");
4079 /* No issue with overriding these - the sampler is dirty due to blit usage */
4080 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
4081 wined3d_gl_mag_filter(magLookup, filter));
4082 checkGLcall("glTexParameteri");
4083 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
4084 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
4085 checkGLcall("glTexParameteri");
4087 if (!src_surface->swapchain || src_surface == src_surface->swapchain->back_buffers[0])
4089 src = backup ? backup : src_surface->container->texture_rgb.name;
4091 else
4093 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
4094 checkGLcall("glReadBuffer(GL_FRONT)");
4096 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
4097 checkGLcall("glGenTextures(1, &src)");
4098 context_bind_texture(context, GL_TEXTURE_2D, src);
4100 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
4101 * out for power of 2 sizes
4103 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
4104 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
4105 checkGLcall("glTexImage2D");
4106 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
4108 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4109 checkGLcall("glTexParameteri");
4110 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4111 checkGLcall("glTexParameteri");
4113 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
4114 checkGLcall("glReadBuffer(GL_BACK)");
4116 if (texture_target != GL_TEXTURE_2D)
4118 gl_info->gl_ops.gl.p_glDisable(texture_target);
4119 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
4120 texture_target = GL_TEXTURE_2D;
4123 checkGLcall("glEnd and previous");
4125 left = src_rect->left;
4126 right = src_rect->right;
4128 if (!upsidedown)
4130 top = src_surface->resource.height - src_rect->top;
4131 bottom = src_surface->resource.height - src_rect->bottom;
4133 else
4135 top = src_surface->resource.height - src_rect->bottom;
4136 bottom = src_surface->resource.height - src_rect->top;
4139 if (src_surface->flags & SFLAG_NORMCOORD)
4141 left /= src_surface->pow2Width;
4142 right /= src_surface->pow2Width;
4143 top /= src_surface->pow2Height;
4144 bottom /= src_surface->pow2Height;
4147 /* draw the source texture stretched and upside down. The correct surface is bound already */
4148 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
4149 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
4151 context_set_draw_buffer(context, drawBuffer);
4152 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
4154 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
4155 /* bottom left */
4156 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
4157 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
4159 /* top left */
4160 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
4161 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
4163 /* top right */
4164 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
4165 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4167 /* bottom right */
4168 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
4169 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
4170 gl_info->gl_ops.gl.p_glEnd();
4171 checkGLcall("glEnd and previous");
4173 if (texture_target != dst_surface->texture_target)
4175 gl_info->gl_ops.gl.p_glDisable(texture_target);
4176 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
4177 texture_target = dst_surface->texture_target;
4180 /* Now read the stretched and upside down image into the destination texture */
4181 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
4182 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
4184 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
4185 0, 0, /* We blitted the image to the origin */
4186 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4187 checkGLcall("glCopyTexSubImage2D");
4189 if (drawBuffer == GL_BACK)
4191 /* Write the back buffer backup back. */
4192 if (backup)
4194 if (texture_target != GL_TEXTURE_2D)
4196 gl_info->gl_ops.gl.p_glDisable(texture_target);
4197 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
4198 texture_target = GL_TEXTURE_2D;
4200 context_bind_texture(context, GL_TEXTURE_2D, backup);
4202 else
4204 if (texture_target != src_surface->texture_target)
4206 gl_info->gl_ops.gl.p_glDisable(texture_target);
4207 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
4208 texture_target = src_surface->texture_target;
4210 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
4213 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
4214 /* top left */
4215 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
4216 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
4218 /* bottom left */
4219 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
4220 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
4222 /* bottom right */
4223 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
4224 (float)fbheight / (float)src_surface->pow2Height);
4225 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
4227 /* top right */
4228 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
4229 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
4230 gl_info->gl_ops.gl.p_glEnd();
4232 gl_info->gl_ops.gl.p_glDisable(texture_target);
4233 checkGLcall("glDisable(texture_target)");
4235 /* Cleanup */
4236 if (src != src_surface->container->texture_rgb.name && src != backup)
4238 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
4239 checkGLcall("glDeleteTextures(1, &src)");
4241 if (backup)
4243 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
4244 checkGLcall("glDeleteTextures(1, &backup)");
4247 if (wined3d_settings.strict_draw_ordering)
4248 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4250 context_release(context);
4252 /* The texture is now most up to date - If the surface is a render target
4253 * and has a drawable, this path is never entered. */
4254 surface_validate_location(dst_surface, SFLAG_INTEXTURE);
4255 surface_invalidate_location(dst_surface, ~SFLAG_INTEXTURE);
4258 /* Front buffer coordinates are always full screen coordinates, but our GL
4259 * drawable is limited to the window's client area. The sysmem and texture
4260 * copies do have the full screen size. Note that GL has a bottom-left
4261 * origin, while D3D has a top-left origin. */
4262 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
4264 UINT drawable_height;
4266 if (surface->swapchain && surface == surface->swapchain->front_buffer)
4268 POINT offset = {0, 0};
4269 RECT windowsize;
4271 ScreenToClient(window, &offset);
4272 OffsetRect(rect, offset.x, offset.y);
4274 GetClientRect(window, &windowsize);
4275 drawable_height = windowsize.bottom - windowsize.top;
4277 else
4279 drawable_height = surface->resource.height;
4282 rect->top = drawable_height - rect->top;
4283 rect->bottom = drawable_height - rect->bottom;
4286 static void surface_blt_to_drawable(const struct wined3d_device *device,
4287 enum wined3d_texture_filter_type filter, BOOL alpha_test,
4288 struct wined3d_surface *src_surface, const RECT *src_rect_in,
4289 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
4291 const struct wined3d_gl_info *gl_info;
4292 struct wined3d_context *context;
4293 RECT src_rect, dst_rect;
4295 src_rect = *src_rect_in;
4296 dst_rect = *dst_rect_in;
4298 context = context_acquire(device, dst_surface);
4299 gl_info = context->gl_info;
4301 /* Make sure the surface is up-to-date. This should probably use
4302 * surface_load_location() and worry about the destination surface too,
4303 * unless we're overwriting it completely. */
4304 wined3d_texture_load(src_surface->container, context, FALSE);
4306 /* Activate the destination context, set it up for blitting */
4307 context_apply_blit_state(context, device);
4309 if (!surface_is_offscreen(dst_surface))
4310 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
4312 device->blitter->set_shader(device->blit_priv, context, src_surface);
4314 if (alpha_test)
4316 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
4317 checkGLcall("glEnable(GL_ALPHA_TEST)");
4319 /* When the primary render target uses P8, the alpha component
4320 * contains the palette index. Which means that the colorkey is one of
4321 * the palette entries. In other cases pixels that should be masked
4322 * away have alpha set to 0. */
4323 if (swapchain_is_p8(context->swapchain))
4324 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
4325 (float)src_surface->container->src_blt_color_key.color_space_low_value / 256.0f);
4326 else
4327 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
4328 checkGLcall("glAlphaFunc");
4330 else
4332 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4333 checkGLcall("glDisable(GL_ALPHA_TEST)");
4336 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
4338 if (alpha_test)
4340 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4341 checkGLcall("glDisable(GL_ALPHA_TEST)");
4344 /* Leave the opengl state valid for blitting */
4345 device->blitter->unset_shader(context->gl_info);
4347 if (wined3d_settings.strict_draw_ordering
4348 || (dst_surface->swapchain && dst_surface->swapchain->front_buffer == dst_surface))
4349 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4351 context_release(context);
4354 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
4356 struct wined3d_device *device = s->resource.device;
4357 const struct blit_shader *blitter;
4359 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
4360 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
4361 if (!blitter)
4363 FIXME("No blitter is capable of performing the requested color fill operation.\n");
4364 return WINED3DERR_INVALIDCALL;
4367 return blitter->color_fill(device, s, rect, color);
4370 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4371 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
4372 enum wined3d_texture_filter_type filter)
4374 struct wined3d_device *device = dst_surface->resource.device;
4375 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4376 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
4378 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
4379 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4380 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
4382 /* Get the swapchain. One of the surfaces has to be a primary surface */
4383 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4385 WARN("Destination is in sysmem, rejecting gl blt\n");
4386 return WINED3DERR_INVALIDCALL;
4389 dst_swapchain = dst_surface->swapchain;
4391 if (src_surface)
4393 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
4395 WARN("Src is in sysmem, rejecting gl blt\n");
4396 return WINED3DERR_INVALIDCALL;
4399 src_swapchain = src_surface->swapchain;
4401 else
4403 src_swapchain = NULL;
4406 /* Early sort out of cases where no render target is used */
4407 if (!dst_swapchain && !src_swapchain
4408 && src_surface != device->fb.render_targets[0]
4409 && dst_surface != device->fb.render_targets[0])
4411 TRACE("No surface is render target, not using hardware blit.\n");
4412 return WINED3DERR_INVALIDCALL;
4415 /* No destination color keying supported */
4416 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
4418 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
4419 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
4420 return WINED3DERR_INVALIDCALL;
4423 if (dst_swapchain && dst_swapchain == src_swapchain)
4425 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
4426 return WINED3DERR_INVALIDCALL;
4429 if (dst_swapchain && src_swapchain)
4431 FIXME("Implement hardware blit between two different swapchains\n");
4432 return WINED3DERR_INVALIDCALL;
4435 if (dst_swapchain)
4437 /* Handled with regular texture -> swapchain blit */
4438 if (src_surface == device->fb.render_targets[0])
4439 TRACE("Blit from active render target to a swapchain\n");
4441 else if (src_swapchain && dst_surface == device->fb.render_targets[0])
4443 FIXME("Implement blit from a swapchain to the active render target\n");
4444 return WINED3DERR_INVALIDCALL;
4447 if ((src_swapchain || src_surface == device->fb.render_targets[0]) && !dst_swapchain)
4449 /* Blit from render target to texture */
4450 BOOL stretchx;
4452 /* P8 read back is not implemented */
4453 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
4454 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
4456 TRACE("P8 read back not supported by frame buffer to texture blit\n");
4457 return WINED3DERR_INVALIDCALL;
4460 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
4462 TRACE("Color keying not supported by frame buffer to texture blit\n");
4463 return WINED3DERR_INVALIDCALL;
4464 /* Destination color key is checked above */
4467 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
4468 stretchx = TRUE;
4469 else
4470 stretchx = FALSE;
4472 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
4473 * flip the image nor scale it.
4475 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
4476 * -> If the app wants an image width an unscaled width, copy it line per line
4477 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
4478 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
4479 * back buffer. This is slower than reading line per line, thus not used for flipping
4480 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
4481 * pixel by pixel. */
4482 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
4483 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
4485 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
4486 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
4488 else
4490 TRACE("Using hardware stretching to flip / stretch the texture.\n");
4491 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
4494 surface_evict_sysmem(dst_surface);
4496 return WINED3D_OK;
4498 else if (src_surface)
4500 /* Blit from offscreen surface to render target */
4501 struct wined3d_color_key old_blt_key = src_surface->container->src_blt_color_key;
4502 DWORD old_color_key_flags = src_surface->container->color_key_flags;
4504 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4506 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4507 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
4508 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
4510 FIXME("Unsupported blit operation falling back to software\n");
4511 return WINED3DERR_INVALIDCALL;
4514 /* Color keying: Check if we have to do a color keyed blt,
4515 * and if not check if a color key is activated.
4517 * Just modify the color keying parameters in the surface and restore them afterwards
4518 * The surface keeps track of the color key last used to load the opengl surface.
4519 * PreLoad will catch the change to the flags and color key and reload if necessary.
4521 if (flags & WINEDDBLT_KEYSRC)
4523 /* Use color key from surface */
4525 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4527 /* Use color key from DDBltFx */
4528 src_surface->container->color_key_flags |= WINEDDSD_CKSRCBLT;
4529 src_surface->container->src_blt_color_key = DDBltFx->ddckSrcColorkey;
4531 else
4533 /* Do not use color key */
4534 src_surface->container->color_key_flags &= ~WINEDDSD_CKSRCBLT;
4537 surface_blt_to_drawable(device, filter,
4538 flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_ALPHATEST),
4539 src_surface, src_rect, dst_surface, dst_rect);
4541 /* Restore the color key parameters */
4542 src_surface->container->color_key_flags = old_color_key_flags;
4543 src_surface->container->src_blt_color_key = old_blt_key;
4545 surface_validate_location(dst_surface, dst_surface->draw_binding);
4546 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
4548 return WINED3D_OK;
4551 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
4552 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
4553 return WINED3DERR_INVALIDCALL;
4556 /* Context activation is done by the caller. */
4557 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
4558 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
4560 struct wined3d_device *device = surface->resource.device;
4561 const struct wined3d_gl_info *gl_info = context->gl_info;
4562 GLint compare_mode = GL_NONE;
4563 struct blt_info info;
4564 GLint old_binding = 0;
4565 RECT rect;
4567 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4569 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
4570 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
4571 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4572 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
4573 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
4574 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
4575 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
4576 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
4577 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4578 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
4579 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
4581 SetRect(&rect, 0, h, w, 0);
4582 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
4583 context_active_texture(context, context->gl_info, 0);
4584 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
4585 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
4586 if (gl_info->supported[ARB_SHADOW])
4588 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
4589 if (compare_mode != GL_NONE)
4590 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
4593 device->shader_backend->shader_select_depth_blt(device->shader_priv,
4594 gl_info, info.tex_type, &surface->ds_current_size);
4596 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
4597 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
4598 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
4599 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
4600 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
4601 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
4602 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
4603 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
4604 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
4605 gl_info->gl_ops.gl.p_glEnd();
4607 if (compare_mode != GL_NONE)
4608 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
4609 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
4611 gl_info->gl_ops.gl.p_glPopAttrib();
4613 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
4616 void surface_modify_ds_location(struct wined3d_surface *surface,
4617 DWORD location, UINT w, UINT h)
4619 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
4621 if (((surface->locations & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
4622 || (!(surface->locations & SFLAG_INTEXTURE) && (location & SFLAG_INTEXTURE)))
4623 wined3d_texture_set_dirty(surface->container);
4625 surface->ds_current_size.cx = w;
4626 surface->ds_current_size.cy = h;
4627 surface->locations = location;
4630 /* Context activation is done by the caller. */
4631 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4633 const struct wined3d_gl_info *gl_info = context->gl_info;
4634 struct wined3d_device *device = surface->resource.device;
4635 GLsizei w, h;
4637 TRACE("surface %p, new location %#x.\n", surface, location);
4639 /* TODO: Make this work for modes other than FBO */
4640 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4642 if (!(surface->locations & location))
4644 w = surface->ds_current_size.cx;
4645 h = surface->ds_current_size.cy;
4646 surface->ds_current_size.cx = 0;
4647 surface->ds_current_size.cy = 0;
4649 else
4651 w = surface->resource.width;
4652 h = surface->resource.height;
4655 if (surface->ds_current_size.cx == surface->resource.width
4656 && surface->ds_current_size.cy == surface->resource.height)
4658 TRACE("Location (%#x) is already up to date.\n", location);
4659 return;
4662 if (surface->current_renderbuffer)
4664 FIXME("Not supported with fixed up depth stencil.\n");
4665 return;
4668 if (surface->locations & SFLAG_DISCARDED)
4670 TRACE("Surface was discarded, no need copy data.\n");
4671 switch (location)
4673 case SFLAG_INTEXTURE:
4674 surface_prepare_texture(surface, context, FALSE);
4675 break;
4676 case SFLAG_INRB_MULTISAMPLE:
4677 surface_prepare_rb(surface, gl_info, TRUE);
4678 break;
4679 case SFLAG_INDRAWABLE:
4680 /* Nothing to do */
4681 break;
4682 default:
4683 FIXME("Unhandled location %#x\n", location);
4685 surface->locations &= ~SFLAG_DISCARDED;
4686 surface->locations |= location;
4687 surface->ds_current_size.cx = surface->resource.width;
4688 surface->ds_current_size.cy = surface->resource.height;
4689 return;
4692 if (!surface->locations)
4694 FIXME("No up to date depth stencil location.\n");
4695 surface->locations |= location;
4696 surface->ds_current_size.cx = surface->resource.width;
4697 surface->ds_current_size.cy = surface->resource.height;
4698 return;
4701 if (location == SFLAG_INTEXTURE)
4703 GLint old_binding = 0;
4704 GLenum bind_target;
4706 /* The render target is allowed to be smaller than the depth/stencil
4707 * buffer, so the onscreen depth/stencil buffer is potentially smaller
4708 * than the offscreen surface. Don't overwrite the offscreen surface
4709 * with undefined data. */
4710 w = min(w, context->swapchain->desc.backbuffer_width);
4711 h = min(h, context->swapchain->desc.backbuffer_height);
4713 TRACE("Copying onscreen depth buffer to depth texture.\n");
4715 if (!device->depth_blt_texture)
4716 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
4718 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4719 * directly on the FBO texture. That's because we need to flip. */
4720 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4721 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
4722 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4724 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4725 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4727 else
4729 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4730 bind_target = GL_TEXTURE_2D;
4732 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
4733 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
4734 * internal format, because the internal format might include stencil
4735 * data. In principle we should copy stencil data as well, but unless
4736 * the driver supports stencil export it's hard to do, and doesn't
4737 * seem to be needed in practice. If the hardware doesn't support
4738 * writing stencil data, the glCopyTexImage2D() call might trigger
4739 * software fallbacks. */
4740 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
4741 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4742 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4743 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4744 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4745 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4746 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4747 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
4749 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4750 NULL, surface, SFLAG_INTEXTURE);
4751 context_set_draw_buffer(context, GL_NONE);
4753 /* Do the actual blit */
4754 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
4755 checkGLcall("depth_blt");
4757 context_invalidate_state(context, STATE_FRAMEBUFFER);
4759 if (wined3d_settings.strict_draw_ordering)
4760 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4762 else if (location == SFLAG_INDRAWABLE)
4764 TRACE("Copying depth texture to onscreen depth buffer.\n");
4766 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4767 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
4768 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
4769 0, surface->pow2Height - h, w, h, surface->texture_target);
4770 checkGLcall("depth_blt");
4772 context_invalidate_state(context, STATE_FRAMEBUFFER);
4774 if (wined3d_settings.strict_draw_ordering)
4775 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4777 else
4779 ERR("Invalid location (%#x) specified.\n", location);
4782 surface->locations |= location;
4783 surface->ds_current_size.cx = surface->resource.width;
4784 surface->ds_current_size.cy = surface->resource.height;
4787 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
4789 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location));
4791 surface->locations |= location;
4794 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
4796 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location));
4798 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
4799 wined3d_texture_set_dirty(surface->container);
4800 surface->locations &= ~location;
4802 if (!surface->locations)
4803 ERR("Surface %p does not have any up to date location.\n", surface);
4806 static DWORD resource_access_from_location(DWORD location)
4808 switch (location)
4810 case SFLAG_INSYSMEM:
4811 case SFLAG_INUSERMEM:
4812 case SFLAG_INDIB:
4813 case SFLAG_INBUFFER:
4814 return WINED3D_RESOURCE_ACCESS_CPU;
4816 case SFLAG_INDRAWABLE:
4817 case SFLAG_INSRGBTEX:
4818 case SFLAG_INTEXTURE:
4819 case SFLAG_INRB_MULTISAMPLE:
4820 case SFLAG_INRB_RESOLVED:
4821 return WINED3D_RESOURCE_ACCESS_GPU;
4823 default:
4824 FIXME("Unhandled location %#x.\n", location);
4825 return 0;
4829 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
4831 struct wined3d_device *device = surface->resource.device;
4832 struct wined3d_context *context;
4833 const struct wined3d_gl_info *gl_info;
4834 struct wined3d_bo_address dst, src;
4835 UINT size = surface->resource.size;
4837 surface_get_memory(surface, &dst, location);
4838 surface_get_memory(surface, &src, surface->locations);
4840 if (dst.buffer_object)
4842 context = context_acquire(device, NULL);
4843 gl_info = context->gl_info;
4844 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, dst.buffer_object));
4845 GL_EXTCALL(glBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, size, src.addr));
4846 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4847 checkGLcall("Upload PBO");
4848 context_release(context);
4849 return;
4851 if (src.buffer_object)
4853 context = context_acquire(device, NULL);
4854 gl_info = context->gl_info;
4855 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, src.buffer_object));
4856 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_PACK_BUFFER_ARB, 0, size, dst.addr));
4857 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4858 checkGLcall("Download PBO");
4859 context_release(context);
4860 return;
4862 memcpy(dst.addr, src.addr, size);
4865 static void surface_load_sysmem(struct wined3d_surface *surface,
4866 const struct wined3d_gl_info *gl_info, DWORD dst_location)
4868 if (surface->locations & surface_simple_locations)
4870 surface_copy_simple_location(surface, dst_location);
4871 return;
4874 if (surface->locations & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
4875 surface_load_location(surface, SFLAG_INTEXTURE);
4877 /* Download the surface to system memory. */
4878 if (surface->locations & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
4880 struct wined3d_device *device = surface->resource.device;
4881 struct wined3d_context *context;
4883 /* TODO: Use already acquired context when possible. */
4884 context = context_acquire(device, NULL);
4886 wined3d_texture_bind_and_dirtify(surface->container, context, !(surface->locations & SFLAG_INTEXTURE));
4887 surface_download_data(surface, gl_info, dst_location);
4889 context_release(context);
4891 return;
4894 if (surface->locations & SFLAG_INDRAWABLE)
4896 read_from_framebuffer(surface, dst_location);
4897 return;
4900 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
4901 surface, debug_surflocation(surface->locations));
4904 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
4905 const struct wined3d_gl_info *gl_info)
4907 RECT r;
4909 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
4911 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
4912 return WINED3DERR_INVALIDCALL;
4915 surface_get_rect(surface, NULL, &r);
4916 surface_load_location(surface, SFLAG_INTEXTURE);
4917 surface_blt_to_drawable(surface->resource.device,
4918 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
4920 return WINED3D_OK;
4923 static HRESULT surface_load_texture(struct wined3d_surface *surface,
4924 const struct wined3d_gl_info *gl_info, BOOL srgb)
4926 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
4927 struct wined3d_device *device = surface->resource.device;
4928 enum wined3d_conversion_type convert;
4929 struct wined3d_context *context;
4930 UINT width, src_pitch, dst_pitch;
4931 struct wined3d_bo_address data;
4932 struct wined3d_format format;
4933 POINT dst_point = {0, 0};
4934 BYTE *mem = NULL;
4936 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
4937 && surface_is_offscreen(surface)
4938 && (surface->locations & SFLAG_INDRAWABLE))
4940 surface_load_fb_texture(surface, srgb);
4942 return WINED3D_OK;
4945 if (surface->locations & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
4946 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
4947 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4948 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4949 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4951 if (srgb)
4952 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INTEXTURE,
4953 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
4954 else
4955 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INSRGBTEX,
4956 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
4958 return WINED3D_OK;
4961 if (surface->locations & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
4962 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
4963 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4964 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4965 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4967 DWORD src_location = surface->locations & SFLAG_INRB_RESOLVED ?
4968 SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
4969 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
4970 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4972 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
4973 &rect, surface, dst_location, &rect);
4975 return WINED3D_OK;
4978 /* Upload from system memory */
4980 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
4981 TRUE /* We will use textures */, &format, &convert);
4983 if (srgb)
4985 if ((surface->locations & (SFLAG_INTEXTURE | surface->map_binding)) == SFLAG_INTEXTURE)
4987 /* Performance warning... */
4988 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
4989 surface_prepare_map_memory(surface);
4990 surface_load_location(surface, surface->map_binding);
4993 else
4995 if ((surface->locations & (SFLAG_INSRGBTEX | surface->map_binding)) == SFLAG_INSRGBTEX)
4997 /* Performance warning... */
4998 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
4999 surface_prepare_map_memory(surface);
5000 surface_load_location(surface, surface->map_binding);
5004 if (!(surface->locations & surface_simple_locations))
5006 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
5007 /* Lets hope we get it from somewhere... */
5008 surface_prepare_system_memory(surface);
5009 surface_load_location(surface, SFLAG_INSYSMEM);
5012 /* TODO: Use already acquired context when possible. */
5013 context = context_acquire(device, NULL);
5015 surface_prepare_texture(surface, context, srgb);
5016 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
5018 if (surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
5020 surface->flags |= SFLAG_GLCKEY;
5021 surface->gl_color_key = surface->container->src_blt_color_key;
5023 else surface->flags &= ~SFLAG_GLCKEY;
5025 width = surface->resource.width;
5026 src_pitch = wined3d_surface_get_pitch(surface);
5028 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5029 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
5030 * called. */
5031 if ((convert != WINED3D_CT_NONE || format.convert) && surface->pbo)
5033 TRACE("Removing the pbo attached to surface %p.\n", surface);
5035 if (surface->flags & SFLAG_DIBSECTION)
5036 surface->map_binding = SFLAG_INDIB;
5037 else
5038 surface->map_binding = SFLAG_INSYSMEM;
5040 surface_prepare_map_memory(surface);
5041 surface_load_location(surface, surface->map_binding);
5042 surface_remove_pbo(surface, gl_info);
5045 surface_get_memory(surface, &data, surface->locations);
5046 if (format.convert)
5048 /* This code is entered for texture formats which need a fixup. */
5049 UINT height = surface->resource.height;
5051 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
5052 dst_pitch = width * format.conv_byte_count;
5053 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5055 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5057 ERR("Out of memory (%u).\n", dst_pitch * height);
5058 context_release(context);
5059 return E_OUTOFMEMORY;
5061 format.convert(data.addr, mem, src_pitch, src_pitch * height,
5062 dst_pitch, dst_pitch * height, width, height, 1);
5063 format.byte_count = format.conv_byte_count;
5064 src_pitch = dst_pitch;
5065 data.addr = mem;
5067 else if (convert != WINED3D_CT_NONE)
5069 /* This code is only entered for color keying fixups */
5070 UINT height = surface->resource.height;
5072 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
5073 dst_pitch = width * format.conv_byte_count;
5074 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5076 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5078 ERR("Out of memory (%u).\n", dst_pitch * height);
5079 context_release(context);
5080 return E_OUTOFMEMORY;
5082 d3dfmt_convert_surface(data.addr, mem, src_pitch,
5083 width, height, dst_pitch, convert, surface);
5084 format.byte_count = format.conv_byte_count;
5085 src_pitch = dst_pitch;
5086 data.addr = mem;
5089 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
5091 context_release(context);
5093 HeapFree(GetProcessHeap(), 0, mem);
5095 return WINED3D_OK;
5098 static void surface_multisample_resolve(struct wined3d_surface *surface)
5100 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5102 if (!(surface->locations & SFLAG_INRB_MULTISAMPLE))
5103 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
5105 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
5106 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
5109 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
5111 struct wined3d_device *device = surface->resource.device;
5112 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5113 HRESULT hr;
5115 TRACE("surface %p, location %s.\n", surface, debug_surflocation(location));
5117 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5119 if (location == SFLAG_INTEXTURE && surface->locations & SFLAG_INDRAWABLE)
5121 struct wined3d_context *context = context_acquire(device, NULL);
5122 surface_load_ds_location(surface, context, location);
5123 context_release(context);
5124 return WINED3D_OK;
5126 else if (location & surface->locations && surface->draw_binding != SFLAG_INDRAWABLE)
5128 /* Already up to date, nothing to do. */
5129 return WINED3D_OK;
5131 else
5133 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
5134 debug_surflocation(surface->locations), debug_surflocation(location));
5135 return WINED3DERR_INVALIDCALL;
5139 if (surface->locations & location)
5141 TRACE("Location already up to date.\n");
5142 return WINED3D_OK;
5145 if (WARN_ON(d3d_surface))
5147 DWORD required_access = resource_access_from_location(location);
5148 if ((surface->resource.access_flags & required_access) != required_access)
5149 WARN("Operation requires %#x access, but surface only has %#x.\n",
5150 required_access, surface->resource.access_flags);
5153 if (!surface->locations)
5155 ERR("Surface %p does not have any up to date location.\n", surface);
5156 surface->flags |= SFLAG_LOST;
5157 return WINED3DERR_DEVICELOST;
5160 switch (location)
5162 case SFLAG_INDIB:
5163 case SFLAG_INUSERMEM:
5164 case SFLAG_INSYSMEM:
5165 case SFLAG_INBUFFER:
5166 surface_load_sysmem(surface, gl_info, location);
5167 break;
5169 case SFLAG_INDRAWABLE:
5170 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
5171 return hr;
5172 break;
5174 case SFLAG_INRB_RESOLVED:
5175 surface_multisample_resolve(surface);
5176 break;
5178 case SFLAG_INTEXTURE:
5179 case SFLAG_INSRGBTEX:
5180 if (FAILED(hr = surface_load_texture(surface, gl_info, location == SFLAG_INSRGBTEX)))
5181 return hr;
5182 break;
5184 default:
5185 ERR("Don't know how to handle location %#x.\n", location);
5186 break;
5189 surface_validate_location(surface, location);
5191 if (location != SFLAG_INSYSMEM && (surface->locations & SFLAG_INSYSMEM))
5192 surface_evict_sysmem(surface);
5194 return WINED3D_OK;
5197 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
5199 struct wined3d_swapchain *swapchain;
5201 /* Not on a swapchain - must be offscreen */
5202 if (!(swapchain = surface->swapchain))
5203 return TRUE;
5205 /* The front buffer is always onscreen */
5206 if (surface == swapchain->front_buffer) return FALSE;
5208 /* If the swapchain is rendered to an FBO, the backbuffer is
5209 * offscreen, otherwise onscreen */
5210 return swapchain->render_to_fbo;
5213 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
5214 /* Context activation is done by the caller. */
5215 static void ffp_blit_free(struct wined3d_device *device) { }
5217 /* Context activation is done by the caller. */
5218 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5220 const struct wined3d_gl_info *gl_info = context->gl_info;
5222 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
5223 checkGLcall("glEnable(target)");
5225 return WINED3D_OK;
5228 /* Context activation is done by the caller. */
5229 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
5231 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
5232 checkGLcall("glDisable(GL_TEXTURE_2D)");
5233 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
5235 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5236 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5238 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
5240 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
5241 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5245 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5246 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5247 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5249 switch (blit_op)
5251 case WINED3D_BLIT_OP_COLOR_BLIT:
5252 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
5253 return FALSE;
5255 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5257 TRACE("Checking support for fixup:\n");
5258 dump_color_fixup_desc(src_format->color_fixup);
5261 /* We only support identity conversions. */
5262 if (!is_identity_fixup(src_format->color_fixup)
5263 || !is_identity_fixup(dst_format->color_fixup))
5265 TRACE("Fixups are not supported.\n");
5266 return FALSE;
5269 return TRUE;
5271 case WINED3D_BLIT_OP_COLOR_FILL:
5272 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
5273 return FALSE;
5275 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5277 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
5278 return FALSE;
5280 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
5282 TRACE("Color fill not supported\n");
5283 return FALSE;
5286 /* FIXME: We should reject color fills on formats with fixups,
5287 * but this would break P8 color fills for example. */
5289 return TRUE;
5291 case WINED3D_BLIT_OP_DEPTH_FILL:
5292 return TRUE;
5294 default:
5295 TRACE("Unsupported blit_op=%d\n", blit_op);
5296 return FALSE;
5300 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5301 const RECT *dst_rect, const struct wined3d_color *color)
5303 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
5304 struct wined3d_fb_state fb = {&dst_surface, NULL};
5306 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
5308 return WINED3D_OK;
5311 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
5312 struct wined3d_surface *surface, const RECT *rect, float depth)
5314 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
5315 struct wined3d_fb_state fb = {NULL, surface};
5317 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
5319 return WINED3D_OK;
5322 const struct blit_shader ffp_blit = {
5323 ffp_blit_alloc,
5324 ffp_blit_free,
5325 ffp_blit_set,
5326 ffp_blit_unset,
5327 ffp_blit_supported,
5328 ffp_blit_color_fill,
5329 ffp_blit_depth_fill,
5332 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
5334 return WINED3D_OK;
5337 /* Context activation is done by the caller. */
5338 static void cpu_blit_free(struct wined3d_device *device)
5342 /* Context activation is done by the caller. */
5343 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
5345 return WINED3D_OK;
5348 /* Context activation is done by the caller. */
5349 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
5353 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5354 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
5355 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
5357 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
5359 return TRUE;
5362 return FALSE;
5365 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
5366 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
5367 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
5369 UINT row_block_count;
5370 const BYTE *src_row;
5371 BYTE *dst_row;
5372 UINT x, y;
5374 src_row = src_data;
5375 dst_row = dst_data;
5377 row_block_count = (update_w + format->block_width - 1) / format->block_width;
5379 if (!flags)
5381 for (y = 0; y < update_h; y += format->block_height)
5383 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
5384 src_row += src_pitch;
5385 dst_row += dst_pitch;
5388 return WINED3D_OK;
5391 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
5393 src_row += (((update_h / format->block_height) - 1) * src_pitch);
5395 switch (format->id)
5397 case WINED3DFMT_DXT1:
5398 for (y = 0; y < update_h; y += format->block_height)
5400 struct block
5402 WORD color[2];
5403 BYTE control_row[4];
5406 const struct block *s = (const struct block *)src_row;
5407 struct block *d = (struct block *)dst_row;
5409 for (x = 0; x < row_block_count; ++x)
5411 d[x].color[0] = s[x].color[0];
5412 d[x].color[1] = s[x].color[1];
5413 d[x].control_row[0] = s[x].control_row[3];
5414 d[x].control_row[1] = s[x].control_row[2];
5415 d[x].control_row[2] = s[x].control_row[1];
5416 d[x].control_row[3] = s[x].control_row[0];
5418 src_row -= src_pitch;
5419 dst_row += dst_pitch;
5421 return WINED3D_OK;
5423 case WINED3DFMT_DXT3:
5424 for (y = 0; y < update_h; y += format->block_height)
5426 struct block
5428 WORD alpha_row[4];
5429 WORD color[2];
5430 BYTE control_row[4];
5433 const struct block *s = (const struct block *)src_row;
5434 struct block *d = (struct block *)dst_row;
5436 for (x = 0; x < row_block_count; ++x)
5438 d[x].alpha_row[0] = s[x].alpha_row[3];
5439 d[x].alpha_row[1] = s[x].alpha_row[2];
5440 d[x].alpha_row[2] = s[x].alpha_row[1];
5441 d[x].alpha_row[3] = s[x].alpha_row[0];
5442 d[x].color[0] = s[x].color[0];
5443 d[x].color[1] = s[x].color[1];
5444 d[x].control_row[0] = s[x].control_row[3];
5445 d[x].control_row[1] = s[x].control_row[2];
5446 d[x].control_row[2] = s[x].control_row[1];
5447 d[x].control_row[3] = s[x].control_row[0];
5449 src_row -= src_pitch;
5450 dst_row += dst_pitch;
5452 return WINED3D_OK;
5454 default:
5455 FIXME("Compressed flip not implemented for format %s.\n",
5456 debug_d3dformat(format->id));
5457 return E_NOTIMPL;
5461 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
5462 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
5464 return E_NOTIMPL;
5467 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5468 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
5469 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5471 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
5472 const struct wined3d_format *src_format, *dst_format;
5473 struct wined3d_texture *src_texture = NULL;
5474 struct wined3d_map_desc dst_map, src_map;
5475 const BYTE *sbase = NULL;
5476 HRESULT hr = WINED3D_OK;
5477 const BYTE *sbuf;
5478 BYTE *dbuf;
5479 int x, y;
5481 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5482 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5483 flags, fx, debug_d3dtexturefiltertype(filter));
5485 if (src_surface == dst_surface)
5487 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
5488 src_map = dst_map;
5489 src_format = dst_surface->resource.format;
5490 dst_format = src_format;
5492 else
5494 dst_format = dst_surface->resource.format;
5495 if (src_surface)
5497 if (dst_surface->resource.format->id != src_surface->resource.format->id)
5499 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
5501 /* The conv function writes a FIXME */
5502 WARN("Cannot convert source surface format to dest format.\n");
5503 goto release;
5505 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
5507 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
5508 src_format = src_surface->resource.format;
5510 else
5512 src_format = dst_format;
5515 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
5518 bpp = dst_surface->resource.format->byte_count;
5519 srcheight = src_rect->bottom - src_rect->top;
5520 srcwidth = src_rect->right - src_rect->left;
5521 dstheight = dst_rect->bottom - dst_rect->top;
5522 dstwidth = dst_rect->right - dst_rect->left;
5523 width = (dst_rect->right - dst_rect->left) * bpp;
5525 if (src_surface)
5526 sbase = (BYTE *)src_map.data
5527 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
5528 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
5529 if (src_surface != dst_surface)
5530 dbuf = dst_map.data;
5531 else
5532 dbuf = (BYTE *)dst_map.data
5533 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
5534 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
5536 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
5538 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
5540 if (src_surface == dst_surface)
5542 FIXME("Only plain blits supported on compressed surfaces.\n");
5543 hr = E_NOTIMPL;
5544 goto release;
5547 if (srcheight != dstheight || srcwidth != dstwidth)
5549 WARN("Stretching not supported on compressed surfaces.\n");
5550 hr = WINED3DERR_INVALIDCALL;
5551 goto release;
5554 if (!surface_check_block_align(src_surface, src_rect))
5556 WARN("Source rectangle not block-aligned.\n");
5557 hr = WINED3DERR_INVALIDCALL;
5558 goto release;
5561 if (!surface_check_block_align(dst_surface, dst_rect))
5563 WARN("Destination rectangle not block-aligned.\n");
5564 hr = WINED3DERR_INVALIDCALL;
5565 goto release;
5568 hr = surface_cpu_blt_compressed(sbase, dbuf,
5569 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
5570 src_format, flags, fx);
5571 goto release;
5574 /* First, all the 'source-less' blits */
5575 if (flags & WINEDDBLT_COLORFILL)
5577 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
5578 flags &= ~WINEDDBLT_COLORFILL;
5581 if (flags & WINEDDBLT_DEPTHFILL)
5583 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
5585 if (flags & WINEDDBLT_ROP)
5587 /* Catch some degenerate cases here. */
5588 switch (fx->dwROP)
5590 case BLACKNESS:
5591 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
5592 break;
5593 case 0xaa0029: /* No-op */
5594 break;
5595 case WHITENESS:
5596 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
5597 break;
5598 case SRCCOPY: /* Well, we do that below? */
5599 break;
5600 default:
5601 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
5602 goto error;
5604 flags &= ~WINEDDBLT_ROP;
5606 if (flags & WINEDDBLT_DDROPS)
5608 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
5610 /* Now the 'with source' blits. */
5611 if (src_surface)
5613 int sx, xinc, sy, yinc;
5615 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
5616 goto release;
5618 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
5619 && (srcwidth != dstwidth || srcheight != dstheight))
5621 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
5622 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
5625 xinc = (srcwidth << 16) / dstwidth;
5626 yinc = (srcheight << 16) / dstheight;
5628 if (!flags)
5630 /* No effects, we can cheat here. */
5631 if (dstwidth == srcwidth)
5633 if (dstheight == srcheight)
5635 /* No stretching in either direction. This needs to be as
5636 * fast as possible. */
5637 sbuf = sbase;
5639 /* Check for overlapping surfaces. */
5640 if (src_surface != dst_surface || dst_rect->top < src_rect->top
5641 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
5643 /* No overlap, or dst above src, so copy from top downwards. */
5644 for (y = 0; y < dstheight; ++y)
5646 memcpy(dbuf, sbuf, width);
5647 sbuf += src_map.row_pitch;
5648 dbuf += dst_map.row_pitch;
5651 else if (dst_rect->top > src_rect->top)
5653 /* Copy from bottom upwards. */
5654 sbuf += src_map.row_pitch * dstheight;
5655 dbuf += dst_map.row_pitch * dstheight;
5656 for (y = 0; y < dstheight; ++y)
5658 sbuf -= src_map.row_pitch;
5659 dbuf -= dst_map.row_pitch;
5660 memcpy(dbuf, sbuf, width);
5663 else
5665 /* Src and dst overlapping on the same line, use memmove. */
5666 for (y = 0; y < dstheight; ++y)
5668 memmove(dbuf, sbuf, width);
5669 sbuf += src_map.row_pitch;
5670 dbuf += dst_map.row_pitch;
5674 else
5676 /* Stretching in y direction only. */
5677 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5679 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5680 memcpy(dbuf, sbuf, width);
5681 dbuf += dst_map.row_pitch;
5685 else
5687 /* Stretching in X direction. */
5688 int last_sy = -1;
5689 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5691 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5693 if ((sy >> 16) == (last_sy >> 16))
5695 /* This source row is the same as last source row -
5696 * Copy the already stretched row. */
5697 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
5699 else
5701 #define STRETCH_ROW(type) \
5702 do { \
5703 const type *s = (const type *)sbuf; \
5704 type *d = (type *)dbuf; \
5705 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5706 d[x] = s[sx >> 16]; \
5707 } while(0)
5709 switch(bpp)
5711 case 1:
5712 STRETCH_ROW(BYTE);
5713 break;
5714 case 2:
5715 STRETCH_ROW(WORD);
5716 break;
5717 case 4:
5718 STRETCH_ROW(DWORD);
5719 break;
5720 case 3:
5722 const BYTE *s;
5723 BYTE *d = dbuf;
5724 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
5726 DWORD pixel;
5728 s = sbuf + 3 * (sx >> 16);
5729 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5730 d[0] = (pixel ) & 0xff;
5731 d[1] = (pixel >> 8) & 0xff;
5732 d[2] = (pixel >> 16) & 0xff;
5733 d += 3;
5735 break;
5737 default:
5738 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
5739 hr = WINED3DERR_NOTAVAILABLE;
5740 goto error;
5742 #undef STRETCH_ROW
5744 dbuf += dst_map.row_pitch;
5745 last_sy = sy;
5749 else
5751 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
5752 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
5753 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
5754 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
5756 /* The color keying flags are checked for correctness in ddraw */
5757 if (flags & WINEDDBLT_KEYSRC)
5759 keylow = src_surface->container->src_blt_color_key.color_space_low_value;
5760 keyhigh = src_surface->container->src_blt_color_key.color_space_high_value;
5762 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5764 keylow = fx->ddckSrcColorkey.color_space_low_value;
5765 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
5768 if (flags & WINEDDBLT_KEYDEST)
5770 /* Destination color keys are taken from the source surface! */
5771 destkeylow = src_surface->container->dst_blt_color_key.color_space_low_value;
5772 destkeyhigh = src_surface->container->dst_blt_color_key.color_space_high_value;
5774 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
5776 destkeylow = fx->ddckDestColorkey.color_space_low_value;
5777 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
5780 if (bpp == 1)
5782 keymask = 0xff;
5784 else
5786 DWORD masks[3];
5787 get_color_masks(src_format, masks);
5788 keymask = masks[0]
5789 | masks[1]
5790 | masks[2];
5792 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
5795 if (flags & WINEDDBLT_DDFX)
5797 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
5798 LONG tmpxy;
5799 dTopLeft = dbuf;
5800 dTopRight = dbuf + ((dstwidth - 1) * bpp);
5801 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
5802 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
5804 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
5806 /* I don't think we need to do anything about this flag */
5807 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
5809 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
5811 tmp = dTopRight;
5812 dTopRight = dTopLeft;
5813 dTopLeft = tmp;
5814 tmp = dBottomRight;
5815 dBottomRight = dBottomLeft;
5816 dBottomLeft = tmp;
5817 dstxinc = dstxinc * -1;
5819 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
5821 tmp = dTopLeft;
5822 dTopLeft = dBottomLeft;
5823 dBottomLeft = tmp;
5824 tmp = dTopRight;
5825 dTopRight = dBottomRight;
5826 dBottomRight = tmp;
5827 dstyinc = dstyinc * -1;
5829 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
5831 /* I don't think we need to do anything about this flag */
5832 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
5834 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
5836 tmp = dBottomRight;
5837 dBottomRight = dTopLeft;
5838 dTopLeft = tmp;
5839 tmp = dBottomLeft;
5840 dBottomLeft = dTopRight;
5841 dTopRight = tmp;
5842 dstxinc = dstxinc * -1;
5843 dstyinc = dstyinc * -1;
5845 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
5847 tmp = dTopLeft;
5848 dTopLeft = dBottomLeft;
5849 dBottomLeft = dBottomRight;
5850 dBottomRight = dTopRight;
5851 dTopRight = tmp;
5852 tmpxy = dstxinc;
5853 dstxinc = dstyinc;
5854 dstyinc = tmpxy;
5855 dstxinc = dstxinc * -1;
5857 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
5859 tmp = dTopLeft;
5860 dTopLeft = dTopRight;
5861 dTopRight = dBottomRight;
5862 dBottomRight = dBottomLeft;
5863 dBottomLeft = tmp;
5864 tmpxy = dstxinc;
5865 dstxinc = dstyinc;
5866 dstyinc = tmpxy;
5867 dstyinc = dstyinc * -1;
5869 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
5871 /* I don't think we need to do anything about this flag */
5872 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
5874 dbuf = dTopLeft;
5875 flags &= ~(WINEDDBLT_DDFX);
5878 #define COPY_COLORKEY_FX(type) \
5879 do { \
5880 const type *s; \
5881 type *d = (type *)dbuf, *dx, tmp; \
5882 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
5884 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
5885 dx = d; \
5886 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5888 tmp = s[sx >> 16]; \
5889 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
5890 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
5892 dx[0] = tmp; \
5894 dx = (type *)(((BYTE *)dx) + dstxinc); \
5896 d = (type *)(((BYTE *)d) + dstyinc); \
5898 } while(0)
5900 switch (bpp)
5902 case 1:
5903 COPY_COLORKEY_FX(BYTE);
5904 break;
5905 case 2:
5906 COPY_COLORKEY_FX(WORD);
5907 break;
5908 case 4:
5909 COPY_COLORKEY_FX(DWORD);
5910 break;
5911 case 3:
5913 const BYTE *s;
5914 BYTE *d = dbuf, *dx;
5915 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5917 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5918 dx = d;
5919 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
5921 DWORD pixel, dpixel = 0;
5922 s = sbuf + 3 * (sx>>16);
5923 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5924 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
5925 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
5926 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
5928 dx[0] = (pixel ) & 0xff;
5929 dx[1] = (pixel >> 8) & 0xff;
5930 dx[2] = (pixel >> 16) & 0xff;
5932 dx += dstxinc;
5934 d += dstyinc;
5936 break;
5938 default:
5939 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
5940 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
5941 hr = WINED3DERR_NOTAVAILABLE;
5942 goto error;
5943 #undef COPY_COLORKEY_FX
5948 error:
5949 if (flags && FIXME_ON(d3d_surface))
5951 FIXME("\tUnsupported flags: %#x.\n", flags);
5954 release:
5955 wined3d_surface_unmap(dst_surface);
5956 if (src_surface && src_surface != dst_surface)
5957 wined3d_surface_unmap(src_surface);
5958 /* Release the converted surface, if any. */
5959 if (src_texture)
5960 wined3d_texture_decref(src_texture);
5962 return hr;
5965 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5966 const RECT *dst_rect, const struct wined3d_color *color)
5968 static const RECT src_rect;
5969 WINEDDBLTFX BltFx;
5971 memset(&BltFx, 0, sizeof(BltFx));
5972 BltFx.dwSize = sizeof(BltFx);
5973 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
5974 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
5975 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
5978 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
5979 struct wined3d_surface *surface, const RECT *rect, float depth)
5981 FIXME("Depth filling not implemented by cpu_blit.\n");
5982 return WINED3DERR_INVALIDCALL;
5985 const struct blit_shader cpu_blit = {
5986 cpu_blit_alloc,
5987 cpu_blit_free,
5988 cpu_blit_set,
5989 cpu_blit_unset,
5990 cpu_blit_supported,
5991 cpu_blit_color_fill,
5992 cpu_blit_depth_fill,
5995 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5996 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5997 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5999 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
6000 struct wined3d_device *device = dst_surface->resource.device;
6001 DWORD src_ds_flags, dst_ds_flags;
6002 RECT src_rect, dst_rect;
6003 BOOL scale, convert;
6004 enum wined3d_conversion_type dst_convert_type;
6005 struct wined3d_format dst_conv_fmt;
6007 static const DWORD simple_blit = WINEDDBLT_ASYNC
6008 | WINEDDBLT_COLORFILL
6009 | WINEDDBLT_WAIT
6010 | WINEDDBLT_DEPTHFILL
6011 | WINEDDBLT_DONOTWAIT;
6013 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6014 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
6015 flags, fx, debug_d3dtexturefiltertype(filter));
6016 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
6018 if (fx)
6020 TRACE("dwSize %#x.\n", fx->dwSize);
6021 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
6022 TRACE("dwROP %#x.\n", fx->dwROP);
6023 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
6024 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
6025 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
6026 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
6027 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
6028 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
6029 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
6030 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
6031 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
6032 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
6033 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
6034 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
6035 TRACE("dwReserved %#x.\n", fx->dwReserved);
6036 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
6037 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
6038 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
6039 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
6040 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
6041 TRACE("ddckDestColorkey {%#x, %#x}.\n",
6042 fx->ddckDestColorkey.color_space_low_value,
6043 fx->ddckDestColorkey.color_space_high_value);
6044 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
6045 fx->ddckSrcColorkey.color_space_low_value,
6046 fx->ddckSrcColorkey.color_space_high_value);
6049 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
6051 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
6052 return WINEDDERR_SURFACEBUSY;
6055 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
6057 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
6058 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
6059 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
6060 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
6061 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
6063 WARN("The application gave us a bad destination rectangle.\n");
6064 return WINEDDERR_INVALIDRECT;
6067 if (src_surface)
6069 surface_get_rect(src_surface, src_rect_in, &src_rect);
6071 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
6072 || src_rect.left > src_surface->resource.width || src_rect.left < 0
6073 || src_rect.top > src_surface->resource.height || src_rect.top < 0
6074 || src_rect.right > src_surface->resource.width || src_rect.right < 0
6075 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
6077 WARN("Application gave us bad source rectangle for Blt.\n");
6078 return WINEDDERR_INVALIDRECT;
6081 else
6083 memset(&src_rect, 0, sizeof(src_rect));
6086 if (!fx || !(fx->dwDDFX))
6087 flags &= ~WINEDDBLT_DDFX;
6089 if (flags & WINEDDBLT_WAIT)
6090 flags &= ~WINEDDBLT_WAIT;
6092 if (flags & WINEDDBLT_ASYNC)
6094 static unsigned int once;
6096 if (!once++)
6097 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
6098 flags &= ~WINEDDBLT_ASYNC;
6101 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
6102 if (flags & WINEDDBLT_DONOTWAIT)
6104 static unsigned int once;
6106 if (!once++)
6107 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
6108 flags &= ~WINEDDBLT_DONOTWAIT;
6111 if (!device->d3d_initialized)
6113 WARN("D3D not initialized, using fallback.\n");
6114 goto cpu;
6117 /* We want to avoid invalidating the sysmem location for converted
6118 * surfaces, since otherwise we'd have to convert the data back when
6119 * locking them. */
6120 d3dfmt_get_conv(dst_surface, TRUE, TRUE, &dst_conv_fmt, &dst_convert_type);
6121 if (dst_convert_type != WINED3D_CT_NONE || dst_conv_fmt.convert || dst_surface->flags & SFLAG_CONVERTED)
6123 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
6124 goto cpu;
6127 if (flags & ~simple_blit)
6129 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
6130 goto fallback;
6133 if (src_surface)
6134 src_swapchain = src_surface->swapchain;
6135 else
6136 src_swapchain = NULL;
6138 dst_swapchain = dst_surface->swapchain;
6140 /* This isn't strictly needed. FBO blits for example could deal with
6141 * cross-swapchain blits by first downloading the source to a texture
6142 * before switching to the destination context. We just have this here to
6143 * not have to deal with the issue, since cross-swapchain blits should be
6144 * rare. */
6145 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
6147 FIXME("Using fallback for cross-swapchain blit.\n");
6148 goto fallback;
6151 scale = src_surface
6152 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
6153 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
6154 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
6156 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
6157 if (src_surface)
6158 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
6159 else
6160 src_ds_flags = 0;
6162 if (src_ds_flags || dst_ds_flags)
6164 if (flags & WINEDDBLT_DEPTHFILL)
6166 float depth;
6168 TRACE("Depth fill.\n");
6170 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
6171 return WINED3DERR_INVALIDCALL;
6173 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
6174 return WINED3D_OK;
6176 else
6178 if (src_ds_flags != dst_ds_flags)
6180 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
6181 return WINED3DERR_INVALIDCALL;
6184 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->draw_binding, &src_rect,
6185 dst_surface, dst_surface->draw_binding, &dst_rect)))
6186 return WINED3D_OK;
6189 else
6191 /* In principle this would apply to depth blits as well, but we don't
6192 * implement those in the CPU blitter at the moment. */
6193 if ((dst_surface->locations & dst_surface->map_binding)
6194 && (!src_surface || (src_surface->locations & src_surface->map_binding)))
6196 if (scale)
6197 TRACE("Not doing sysmem blit because of scaling.\n");
6198 else if (convert)
6199 TRACE("Not doing sysmem blit because of format conversion.\n");
6200 else
6201 goto cpu;
6204 if (flags & WINEDDBLT_COLORFILL)
6206 struct wined3d_color color;
6208 TRACE("Color fill.\n");
6210 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
6211 goto fallback;
6213 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
6214 return WINED3D_OK;
6216 else
6218 TRACE("Color blit.\n");
6220 /* Upload */
6221 if ((src_surface->locations & SFLAG_INSYSMEM)
6222 && !(dst_surface->locations & SFLAG_INSYSMEM))
6224 if (scale)
6225 TRACE("Not doing upload because of scaling.\n");
6226 else if (convert)
6227 TRACE("Not doing upload because of format conversion.\n");
6228 else
6230 POINT dst_point = {dst_rect.left, dst_rect.top};
6232 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
6234 if (!surface_is_offscreen(dst_surface))
6235 surface_load_location(dst_surface, dst_surface->draw_binding);
6236 return WINED3D_OK;
6241 /* Use present for back -> front blits. The idea behind this is
6242 * that present is potentially faster than a blit, in particular
6243 * when FBO blits aren't available. Some ddraw applications like
6244 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
6245 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
6246 * applications can't blit directly to the frontbuffer. */
6247 if (dst_swapchain && dst_swapchain->back_buffers
6248 && dst_surface == dst_swapchain->front_buffer
6249 && src_surface == dst_swapchain->back_buffers[0])
6251 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
6253 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
6255 /* Set the swap effect to COPY, we don't want the backbuffer
6256 * to become undefined. */
6257 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
6258 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
6259 dst_swapchain->desc.swap_effect = swap_effect;
6261 return WINED3D_OK;
6264 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6265 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6266 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6268 TRACE("Using FBO blit.\n");
6270 surface_blt_fbo(device, filter,
6271 src_surface, src_surface->draw_binding, &src_rect,
6272 dst_surface, dst_surface->draw_binding, &dst_rect);
6273 surface_validate_location(dst_surface, dst_surface->draw_binding);
6274 surface_invalidate_location(dst_surface, ~dst_surface->draw_binding);
6276 return WINED3D_OK;
6279 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6280 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6281 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6283 TRACE("Using arbfp blit.\n");
6285 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
6286 return WINED3D_OK;
6291 fallback:
6292 /* Special cases for render targets. */
6293 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
6294 return WINED3D_OK;
6296 cpu:
6298 /* For the rest call the X11 surface implementation. For render targets
6299 * this should be implemented OpenGL accelerated in surface_blt_special(),
6300 * other blits are rather rare. */
6301 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
6304 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
6305 const struct wined3d_resource_desc *desc, DWORD flags)
6307 struct wined3d_device *device = container->resource.device;
6308 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6309 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
6310 UINT multisample_quality = desc->multisample_quality;
6311 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
6312 unsigned int resource_size;
6313 HRESULT hr;
6315 if (multisample_quality > 0)
6317 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
6318 multisample_quality = 0;
6321 /* Quick lockable sanity check.
6322 * TODO: remove this after surfaces, usage and lockability have been debugged properly
6323 * this function is too deep to need to care about things like this.
6324 * Levels need to be checked too, since they all affect what can be done. */
6325 switch (desc->pool)
6327 case WINED3D_POOL_MANAGED:
6328 if (desc->usage & WINED3DUSAGE_DYNAMIC)
6329 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
6330 break;
6332 case WINED3D_POOL_DEFAULT:
6333 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
6334 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
6335 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
6336 break;
6338 case WINED3D_POOL_SCRATCH:
6339 case WINED3D_POOL_SYSTEM_MEM:
6340 break;
6342 default:
6343 FIXME("Unknown pool %#x.\n", desc->pool);
6344 break;
6347 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
6348 FIXME("Trying to create a render target that isn't in the default pool.\n");
6350 /* FIXME: Check that the format is supported by the device. */
6352 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
6353 if (!resource_size)
6354 return WINED3DERR_INVALIDCALL;
6356 if (device->wined3d->flags & WINED3D_NO3D)
6357 surface->surface_ops = &gdi_surface_ops;
6358 else
6359 surface->surface_ops = &surface_ops;
6361 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
6362 desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height, 1,
6363 resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
6365 WARN("Failed to initialize resource, returning %#x.\n", hr);
6366 return hr;
6369 surface_set_container(surface, container);
6370 surface_validate_location(surface, SFLAG_INSYSMEM);
6371 list_init(&surface->renderbuffers);
6372 list_init(&surface->overlays);
6374 /* Flags */
6375 surface->flags |= SFLAG_NORMCOORD; /* Default to normalized coords. */
6376 if (flags & WINED3D_SURFACE_DISCARD)
6377 surface->flags |= SFLAG_DISCARD;
6378 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
6379 surface->flags |= SFLAG_PIN_SYSMEM;
6380 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
6381 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
6383 /* I'm not sure if this qualifies as a hack or as an optimization. It
6384 * seems reasonable to assume that lockable render targets will get
6385 * locked, so we might as well set SFLAG_DYNLOCK right at surface
6386 * creation. However, the other reason we want to do this is that several
6387 * ddraw applications access surface memory while the surface isn't
6388 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
6389 * future locks prevents these from crashing. */
6390 if (lockable && (desc->usage & WINED3DUSAGE_RENDERTARGET))
6391 surface->flags |= SFLAG_DYNLOCK;
6393 surface->map_binding = SFLAG_INSYSMEM;
6395 /* Call the private setup routine */
6396 hr = surface->surface_ops->surface_private_setup(surface);
6397 if (FAILED(hr))
6399 ERR("Private setup failed, returning %#x\n", hr);
6400 surface_set_container(surface, NULL);
6401 surface_cleanup(surface);
6402 return hr;
6405 /* Similar to lockable rendertargets above, creating the DIB section
6406 * during surface initialization prevents the sysmem pointer from changing
6407 * after a wined3d_surface_getdc() call. */
6408 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
6409 && SUCCEEDED(surface_create_dib_section(surface)))
6410 surface->map_binding = SFLAG_INDIB;
6412 if (surface->map_binding == SFLAG_INDIB)
6414 wined3d_resource_free_sysmem(&surface->resource);
6415 surface_validate_location(surface, SFLAG_INDIB);
6416 surface_invalidate_location(surface, SFLAG_INSYSMEM);
6419 return hr;
6422 HRESULT CDECL wined3d_surface_create(struct wined3d_texture *container,
6423 const struct wined3d_resource_desc *desc, DWORD flags, struct wined3d_surface **surface)
6425 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
6426 const struct wined3d_parent_ops *parent_ops;
6427 struct wined3d_surface *object;
6428 void *parent;
6429 HRESULT hr;
6431 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), "
6432 "pool %s, multisample_type %#x, multisample_quality %u, flags %#x, surface %p.\n",
6433 container, desc->width, desc->height, debug_d3dformat(desc->format),
6434 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
6435 desc->multisample_type, desc->multisample_quality, flags, surface);
6437 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
6438 return E_OUTOFMEMORY;
6440 if (FAILED(hr = surface_init(object, container, desc, flags)))
6442 WARN("Failed to initialize surface, returning %#x.\n", hr);
6443 HeapFree(GetProcessHeap(), 0, object);
6444 return hr;
6447 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
6448 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
6450 WARN("Failed to create surface parent, hr %#x.\n", hr);
6451 surface_set_container(object, NULL);
6452 wined3d_surface_decref(object);
6453 return hr;
6456 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
6458 object->resource.parent = parent;
6459 object->resource.parent_ops = parent_ops;
6460 *surface = object;
6462 return hr;