wined3d: Set the surface pitch for user-memory surfaces.
[wine/multimedia.git] / dlls / wined3d / surface.c
blob429147e2ed4802b60038f22fec23a2d9e7e7c4d1
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2011, 2013-2014 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d);
37 #define MAXLOCKCOUNT 50 /* After this amount of locks do not free the sysmem copy. */
39 static const DWORD surface_simple_locations =
40 WINED3D_LOCATION_SYSMEM | WINED3D_LOCATION_USER_MEMORY
41 | WINED3D_LOCATION_DIB | WINED3D_LOCATION_BUFFER;
43 static void surface_cleanup(struct wined3d_surface *surface)
45 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->pbo || surface->rb_multisample
50 || surface->rb_resolved || !list_empty(&surface->renderbuffers))
52 struct wined3d_renderbuffer_entry *entry, *entry2;
53 const struct wined3d_gl_info *gl_info;
54 struct wined3d_context *context;
56 context = context_acquire(surface->resource.device, NULL);
57 gl_info = context->gl_info;
59 if (surface->pbo)
61 TRACE("Deleting PBO %u.\n", surface->pbo);
62 GL_EXTCALL(glDeleteBuffers(1, &surface->pbo));
65 if (surface->rb_multisample)
67 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
68 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
71 if (surface->rb_resolved)
73 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
74 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
77 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
79 TRACE("Deleting renderbuffer %u.\n", entry->id);
80 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
81 HeapFree(GetProcessHeap(), 0, entry);
84 context_release(context);
87 if (surface->flags & SFLAG_DIBSECTION)
89 DeleteDC(surface->hDC);
90 DeleteObject(surface->dib.DIBsection);
91 surface->dib.bitmap_data = NULL;
94 if (surface->overlay_dest)
95 list_remove(&surface->overlay_entry);
97 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
99 list_remove(&overlay->overlay_entry);
100 overlay->overlay_dest = NULL;
103 resource_cleanup(&surface->resource);
106 void wined3d_surface_destroy(struct wined3d_surface *surface)
108 TRACE("surface %p.\n", surface);
110 surface_cleanup(surface);
111 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
112 HeapFree(GetProcessHeap(), 0, surface);
115 void surface_get_drawable_size(const struct wined3d_surface *surface, const struct wined3d_context *context,
116 unsigned int *width, unsigned int *height)
118 if (surface->container->swapchain)
120 /* The drawable size of an onscreen drawable is the surface size.
121 * (Actually: The window size, but the surface is created in window
122 * size.) */
123 *width = context->current_rt->resource.width;
124 *height = context->current_rt->resource.height;
126 else if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER)
128 const struct wined3d_swapchain *swapchain = context->swapchain;
130 /* The drawable size of a backbuffer / aux buffer offscreen target is
131 * the size of the current context's drawable, which is the size of
132 * the back buffer of the swapchain the active context belongs to. */
133 *width = swapchain->desc.backbuffer_width;
134 *height = swapchain->desc.backbuffer_height;
136 else
138 /* The drawable size of an FBO target is the OpenGL texture size,
139 * which is the power of two size. */
140 *width = context->current_rt->pow2Width;
141 *height = context->current_rt->pow2Height;
145 struct blt_info
147 GLenum binding;
148 GLenum bind_target;
149 enum tex_types tex_type;
150 GLfloat coords[4][3];
153 struct float_rect
155 float l;
156 float t;
157 float r;
158 float b;
161 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
163 f->l = ((r->left * 2.0f) / w) - 1.0f;
164 f->t = ((r->top * 2.0f) / h) - 1.0f;
165 f->r = ((r->right * 2.0f) / w) - 1.0f;
166 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
169 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
171 GLfloat (*coords)[3] = info->coords;
172 struct float_rect f;
174 switch (target)
176 default:
177 FIXME("Unsupported texture target %#x\n", target);
178 /* Fall back to GL_TEXTURE_2D */
179 case GL_TEXTURE_2D:
180 info->binding = GL_TEXTURE_BINDING_2D;
181 info->bind_target = GL_TEXTURE_2D;
182 info->tex_type = tex_2d;
183 coords[0][0] = (float)rect->left / w;
184 coords[0][1] = (float)rect->top / h;
185 coords[0][2] = 0.0f;
187 coords[1][0] = (float)rect->right / w;
188 coords[1][1] = (float)rect->top / h;
189 coords[1][2] = 0.0f;
191 coords[2][0] = (float)rect->left / w;
192 coords[2][1] = (float)rect->bottom / h;
193 coords[2][2] = 0.0f;
195 coords[3][0] = (float)rect->right / w;
196 coords[3][1] = (float)rect->bottom / h;
197 coords[3][2] = 0.0f;
198 break;
200 case GL_TEXTURE_RECTANGLE_ARB:
201 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
202 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
203 info->tex_type = tex_rect;
204 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
205 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
206 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
207 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
208 break;
210 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
211 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
212 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
213 info->tex_type = tex_cube;
214 cube_coords_float(rect, w, h, &f);
216 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
217 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
218 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
219 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
220 break;
222 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
223 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
224 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
225 info->tex_type = tex_cube;
226 cube_coords_float(rect, w, h, &f);
228 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
229 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
230 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
231 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
232 break;
234 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
235 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
236 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
237 info->tex_type = tex_cube;
238 cube_coords_float(rect, w, h, &f);
240 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
241 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
242 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
243 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
244 break;
246 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
247 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
248 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
249 info->tex_type = tex_cube;
250 cube_coords_float(rect, w, h, &f);
252 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
253 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
254 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
255 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
256 break;
258 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
259 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
260 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
261 info->tex_type = tex_cube;
262 cube_coords_float(rect, w, h, &f);
264 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
265 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
266 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
267 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
268 break;
270 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
271 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
272 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
273 info->tex_type = tex_cube;
274 cube_coords_float(rect, w, h, &f);
276 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
277 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
278 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
279 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
280 break;
284 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
286 if (rect_in)
287 *rect_out = *rect_in;
288 else
290 rect_out->left = 0;
291 rect_out->top = 0;
292 rect_out->right = surface->resource.width;
293 rect_out->bottom = surface->resource.height;
297 /* Context activation is done by the caller. */
298 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
299 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
301 const struct wined3d_gl_info *gl_info = context->gl_info;
302 struct wined3d_texture *texture = src_surface->container;
303 struct blt_info info;
305 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
307 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
308 checkGLcall("glEnable(bind_target)");
310 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
312 /* Filtering for StretchRect */
313 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
314 checkGLcall("glTexParameteri");
315 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
316 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
317 checkGLcall("glTexParameteri");
318 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
319 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
320 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
321 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
322 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
323 checkGLcall("glTexEnvi");
325 /* Draw a quad */
326 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
327 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
328 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
330 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
331 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
333 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
334 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
336 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
337 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
338 gl_info->gl_ops.gl.p_glEnd();
340 /* Unbind the texture */
341 context_bind_texture(context, info.bind_target, 0);
343 /* We changed the filtering settings on the texture. Inform the
344 * container about this to get the filters reset properly next draw. */
345 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
346 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
347 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
348 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
351 /* Works correctly only for <= 4 bpp formats. */
352 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
354 masks[0] = ((1 << format->red_size) - 1) << format->red_offset;
355 masks[1] = ((1 << format->green_size) - 1) << format->green_offset;
356 masks[2] = ((1 << format->blue_size) - 1) << format->blue_offset;
359 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
361 const struct wined3d_format *format = surface->resource.format;
362 SYSTEM_INFO sysInfo;
363 BITMAPINFO *b_info;
364 int extraline = 0;
365 DWORD *masks;
367 TRACE("surface %p.\n", surface);
369 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
371 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
372 return WINED3DERR_INVALIDCALL;
375 switch (format->byte_count)
377 case 2:
378 case 4:
379 /* Allocate extra space to store the RGB bit masks. */
380 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
381 break;
383 case 3:
384 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
385 break;
387 default:
388 /* Allocate extra space for a palette. */
389 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
390 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
391 break;
394 if (!b_info)
395 return E_OUTOFMEMORY;
397 /* Some applications access the surface in via DWORDs, and do not take
398 * the necessary care at the end of the surface. So we need at least
399 * 4 extra bytes at the end of the surface. Check against the page size,
400 * if the last page used for the surface has at least 4 spare bytes we're
401 * safe, otherwise add an extra line to the DIB section. */
402 GetSystemInfo(&sysInfo);
403 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
405 extraline = 1;
406 TRACE("Adding an extra line to the DIB section.\n");
409 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
410 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
411 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
412 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
413 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
414 * wined3d_surface_get_pitch(surface);
415 b_info->bmiHeader.biPlanes = 1;
416 b_info->bmiHeader.biBitCount = format->byte_count * 8;
418 b_info->bmiHeader.biXPelsPerMeter = 0;
419 b_info->bmiHeader.biYPelsPerMeter = 0;
420 b_info->bmiHeader.biClrUsed = 0;
421 b_info->bmiHeader.biClrImportant = 0;
423 /* Get the bit masks */
424 masks = (DWORD *)b_info->bmiColors;
425 switch (surface->resource.format->id)
427 case WINED3DFMT_B8G8R8_UNORM:
428 b_info->bmiHeader.biCompression = BI_RGB;
429 break;
431 case WINED3DFMT_B5G5R5X1_UNORM:
432 case WINED3DFMT_B5G5R5A1_UNORM:
433 case WINED3DFMT_B4G4R4A4_UNORM:
434 case WINED3DFMT_B4G4R4X4_UNORM:
435 case WINED3DFMT_B2G3R3_UNORM:
436 case WINED3DFMT_B2G3R3A8_UNORM:
437 case WINED3DFMT_R10G10B10A2_UNORM:
438 case WINED3DFMT_R8G8B8A8_UNORM:
439 case WINED3DFMT_R8G8B8X8_UNORM:
440 case WINED3DFMT_B10G10R10A2_UNORM:
441 case WINED3DFMT_B5G6R5_UNORM:
442 case WINED3DFMT_R16G16B16A16_UNORM:
443 b_info->bmiHeader.biCompression = BI_BITFIELDS;
444 get_color_masks(format, masks);
445 break;
447 default:
448 /* Don't know palette */
449 b_info->bmiHeader.biCompression = BI_RGB;
450 break;
453 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
454 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
455 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
456 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
458 if (!surface->dib.DIBsection)
460 ERR("Failed to create DIB section.\n");
461 HeapFree(GetProcessHeap(), 0, b_info);
462 return HRESULT_FROM_WIN32(GetLastError());
465 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
466 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
468 HeapFree(GetProcessHeap(), 0, b_info);
470 /* Now allocate a DC. */
471 surface->hDC = CreateCompatibleDC(0);
472 SelectObject(surface->hDC, surface->dib.DIBsection);
474 surface->flags |= SFLAG_DIBSECTION;
476 return WINED3D_OK;
479 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
480 DWORD location)
482 if (location & WINED3D_LOCATION_BUFFER)
484 data->addr = NULL;
485 data->buffer_object = surface->pbo;
486 return;
488 if (location & WINED3D_LOCATION_USER_MEMORY)
490 data->addr = surface->user_memory;
491 data->buffer_object = 0;
492 return;
494 if (location & WINED3D_LOCATION_DIB)
496 data->addr = surface->dib.bitmap_data;
497 data->buffer_object = 0;
498 return;
500 if (location & WINED3D_LOCATION_SYSMEM)
502 data->addr = surface->resource.heap_memory;
503 data->buffer_object = 0;
504 return;
507 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
508 data->addr = NULL;
509 data->buffer_object = 0;
512 static void surface_prepare_buffer(struct wined3d_surface *surface)
514 struct wined3d_context *context;
515 GLenum error;
516 const struct wined3d_gl_info *gl_info;
518 if (surface->pbo)
519 return;
521 context = context_acquire(surface->resource.device, NULL);
522 gl_info = context->gl_info;
524 GL_EXTCALL(glGenBuffers(1, &surface->pbo));
525 error = gl_info->gl_ops.gl.p_glGetError();
526 if (!surface->pbo || error != GL_NO_ERROR)
527 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
529 TRACE("Binding PBO %u.\n", surface->pbo);
531 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
532 checkGLcall("glBindBuffer");
534 GL_EXTCALL(glBufferData(GL_PIXEL_UNPACK_BUFFER, surface->resource.size + 4,
535 NULL, GL_STREAM_DRAW));
536 checkGLcall("glBufferData");
538 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
539 checkGLcall("glBindBuffer");
541 context_release(context);
544 static void surface_prepare_system_memory(struct wined3d_surface *surface)
546 TRACE("surface %p.\n", surface);
548 if (surface->resource.heap_memory)
549 return;
551 /* Whatever surface we have, make sure that there is memory allocated
552 * for the downloaded copy, or a PBO to map. */
553 if (!wined3d_resource_allocate_sysmem(&surface->resource))
554 ERR("Failed to allocate system memory.\n");
556 if (surface->locations & WINED3D_LOCATION_SYSMEM)
557 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
560 void surface_prepare_map_memory(struct wined3d_surface *surface)
562 switch (surface->resource.map_binding)
564 case WINED3D_LOCATION_SYSMEM:
565 surface_prepare_system_memory(surface);
566 break;
568 case WINED3D_LOCATION_USER_MEMORY:
569 if (!surface->user_memory)
570 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
571 break;
573 case WINED3D_LOCATION_DIB:
574 if (!surface->dib.bitmap_data)
575 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
576 break;
578 case WINED3D_LOCATION_BUFFER:
579 surface_prepare_buffer(surface);
580 break;
582 default:
583 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
587 static void surface_evict_sysmem(struct wined3d_surface *surface)
589 /* In some conditions the surface memory must not be freed:
590 * WINED3D_TEXTURE_CONVERTED: Converting the data back would take too long
591 * WINED3D_TEXTURE_DYNAMIC_MAP: Avoid freeing the data for performance
592 * SFLAG_CLIENT: OpenGL uses our memory as backup */
593 if (surface->resource.map_count || surface->flags & SFLAG_CLIENT
594 || surface->container->flags & (WINED3D_TEXTURE_CONVERTED | WINED3D_TEXTURE_PIN_SYSMEM
595 | WINED3D_TEXTURE_DYNAMIC_MAP))
596 return;
598 wined3d_resource_free_sysmem(&surface->resource);
599 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
602 static void surface_release_client_storage(struct wined3d_surface *surface)
604 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
605 const struct wined3d_gl_info *gl_info = context->gl_info;
607 if (surface->container->texture_rgb.name)
609 wined3d_texture_bind_and_dirtify(surface->container, context, FALSE);
610 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
611 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
613 if (surface->container->texture_srgb.name)
615 wined3d_texture_bind_and_dirtify(surface->container, context, TRUE);
616 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
617 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
619 wined3d_texture_force_reload(surface->container);
621 context_release(context);
624 static BOOL surface_use_pbo(const struct wined3d_surface *surface)
626 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
627 struct wined3d_texture *texture = surface->container;
629 return texture->resource.pool == WINED3D_POOL_DEFAULT
630 && surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU
631 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
632 && !texture->resource.format->convert
633 && !(texture->flags & WINED3D_TEXTURE_PIN_SYSMEM)
634 && !(surface->flags & SFLAG_NONPOW2);
637 static HRESULT surface_private_setup(struct wined3d_surface *surface)
639 /* TODO: Check against the maximum texture sizes supported by the video card. */
640 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
641 unsigned int pow2Width, pow2Height;
643 TRACE("surface %p.\n", surface);
645 /* Non-power2 support */
646 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
647 || gl_info->supported[ARB_TEXTURE_RECTANGLE])
649 pow2Width = surface->resource.width;
650 pow2Height = surface->resource.height;
652 else
654 /* Find the nearest pow2 match */
655 pow2Width = pow2Height = 1;
656 while (pow2Width < surface->resource.width)
657 pow2Width <<= 1;
658 while (pow2Height < surface->resource.height)
659 pow2Height <<= 1;
661 surface->pow2Width = pow2Width;
662 surface->pow2Height = pow2Height;
664 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
666 /* TODO: Add support for non power two compressed textures. */
667 if (surface->resource.format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE))
669 FIXME("(%p) Compressed or height scaled non-power-two textures are not supported w(%d) h(%d)\n",
670 surface, surface->resource.width, surface->resource.height);
671 return WINED3DERR_NOTAVAILABLE;
675 if (pow2Width != surface->resource.width
676 || pow2Height != surface->resource.height)
678 surface->flags |= SFLAG_NONPOW2;
681 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
682 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
684 /* One of three options:
685 * 1: Do the same as we do with NPOT and scale the texture, (any
686 * texture ops would require the texture to be scaled which is
687 * potentially slow)
688 * 2: Set the texture to the maximum size (bad idea).
689 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
690 * 4: Create the surface, but allow it to be used only for DirectDraw
691 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
692 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
693 * the render target. */
694 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
696 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
697 return WINED3DERR_NOTAVAILABLE;
700 /* We should never use this surface in combination with OpenGL! */
701 TRACE("Creating an oversized surface: %ux%u.\n",
702 surface->pow2Width, surface->pow2Height);
705 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
706 surface->locations = WINED3D_LOCATION_DISCARDED;
708 if (surface_use_pbo(surface))
709 surface->resource.map_binding = WINED3D_LOCATION_BUFFER;
711 return WINED3D_OK;
714 static void surface_unmap(struct wined3d_surface *surface)
716 struct wined3d_device *device = surface->resource.device;
717 const struct wined3d_gl_info *gl_info;
718 struct wined3d_context *context;
720 TRACE("surface %p.\n", surface);
722 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
724 switch (surface->resource.map_binding)
726 case WINED3D_LOCATION_SYSMEM:
727 case WINED3D_LOCATION_USER_MEMORY:
728 case WINED3D_LOCATION_DIB:
729 break;
731 case WINED3D_LOCATION_BUFFER:
732 context = context_acquire(device, NULL);
733 gl_info = context->gl_info;
735 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
736 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
737 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
738 checkGLcall("glUnmapBuffer");
739 context_release(context);
740 break;
742 default:
743 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
746 if (surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_TEXTURE_RGB))
748 TRACE("Not dirtified, nothing to do.\n");
749 return;
752 if (surface->container->swapchain && surface->container->swapchain->front_buffer == surface->container)
753 surface_load_location(surface, surface->container->resource.draw_binding);
754 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
755 FIXME("Depth / stencil buffer locking is not implemented.\n");
758 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
760 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
761 return FALSE;
762 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
763 return FALSE;
764 return TRUE;
767 static void surface_depth_blt_fbo(const struct wined3d_device *device,
768 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
769 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
771 const struct wined3d_gl_info *gl_info;
772 struct wined3d_context *context;
773 DWORD src_mask, dst_mask;
774 GLbitfield gl_mask;
776 TRACE("device %p\n", device);
777 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
778 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
779 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
780 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
782 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
783 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
785 if (src_mask != dst_mask)
787 ERR("Incompatible formats %s and %s.\n",
788 debug_d3dformat(src_surface->resource.format->id),
789 debug_d3dformat(dst_surface->resource.format->id));
790 return;
793 if (!src_mask)
795 ERR("Not a depth / stencil format: %s.\n",
796 debug_d3dformat(src_surface->resource.format->id));
797 return;
800 gl_mask = 0;
801 if (src_mask & WINED3DFMT_FLAG_DEPTH)
802 gl_mask |= GL_DEPTH_BUFFER_BIT;
803 if (src_mask & WINED3DFMT_FLAG_STENCIL)
804 gl_mask |= GL_STENCIL_BUFFER_BIT;
806 /* Make sure the locations are up-to-date. Loading the destination
807 * surface isn't required if the entire surface is overwritten. */
808 surface_load_location(src_surface, src_location);
809 if (!surface_is_full_rect(dst_surface, dst_rect))
810 surface_load_location(dst_surface, dst_location);
812 context = context_acquire(device, NULL);
813 if (!context->valid)
815 context_release(context);
816 WARN("Invalid context, skipping blit.\n");
817 return;
820 gl_info = context->gl_info;
822 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
823 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
825 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
826 context_set_draw_buffer(context, GL_NONE);
827 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
828 context_invalidate_state(context, STATE_FRAMEBUFFER);
830 if (gl_mask & GL_DEPTH_BUFFER_BIT)
832 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
833 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
835 if (gl_mask & GL_STENCIL_BUFFER_BIT)
837 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
839 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
840 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
842 gl_info->gl_ops.gl.p_glStencilMask(~0U);
843 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
846 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
847 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
849 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
850 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
851 checkGLcall("glBlitFramebuffer()");
853 if (wined3d_settings.strict_draw_ordering)
854 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
856 context_release(context);
859 /* Blit between surface locations. Onscreen on different swapchains is not supported.
860 * Depth / stencil is not supported. */
861 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
862 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
863 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
865 const struct wined3d_gl_info *gl_info;
866 struct wined3d_context *context;
867 RECT src_rect, dst_rect;
868 GLenum gl_filter;
869 GLenum buffer;
871 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
872 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
873 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
874 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
875 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
877 src_rect = *src_rect_in;
878 dst_rect = *dst_rect_in;
880 switch (filter)
882 case WINED3D_TEXF_LINEAR:
883 gl_filter = GL_LINEAR;
884 break;
886 default:
887 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
888 case WINED3D_TEXF_NONE:
889 case WINED3D_TEXF_POINT:
890 gl_filter = GL_NEAREST;
891 break;
894 /* Resolve the source surface first if needed. */
895 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
896 && (src_surface->resource.format->id != dst_surface->resource.format->id
897 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
898 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
899 src_location = WINED3D_LOCATION_RB_RESOLVED;
901 /* Make sure the locations are up-to-date. Loading the destination
902 * surface isn't required if the entire surface is overwritten. (And is
903 * in fact harmful if we're being called by surface_load_location() with
904 * the purpose of loading the destination surface.) */
905 surface_load_location(src_surface, src_location);
906 if (!surface_is_full_rect(dst_surface, &dst_rect))
907 surface_load_location(dst_surface, dst_location);
909 if (src_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, src_surface);
910 else if (dst_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, dst_surface);
911 else context = context_acquire(device, NULL);
913 if (!context->valid)
915 context_release(context);
916 WARN("Invalid context, skipping blit.\n");
917 return;
920 gl_info = context->gl_info;
922 if (src_location == WINED3D_LOCATION_DRAWABLE)
924 TRACE("Source surface %p is onscreen.\n", src_surface);
925 buffer = surface_get_gl_buffer(src_surface);
926 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
928 else
930 TRACE("Source surface %p is offscreen.\n", src_surface);
931 buffer = GL_COLOR_ATTACHMENT0;
934 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
935 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
936 checkGLcall("glReadBuffer()");
937 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
939 if (dst_location == WINED3D_LOCATION_DRAWABLE)
941 TRACE("Destination surface %p is onscreen.\n", dst_surface);
942 buffer = surface_get_gl_buffer(dst_surface);
943 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
945 else
947 TRACE("Destination surface %p is offscreen.\n", dst_surface);
948 buffer = GL_COLOR_ATTACHMENT0;
951 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
952 context_set_draw_buffer(context, buffer);
953 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
954 context_invalidate_state(context, STATE_FRAMEBUFFER);
956 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
957 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
958 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
959 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
960 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
962 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
963 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
965 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
966 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
967 checkGLcall("glBlitFramebuffer()");
969 if (wined3d_settings.strict_draw_ordering
970 || (dst_location == WINED3D_LOCATION_DRAWABLE
971 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
972 gl_info->gl_ops.gl.p_glFlush();
974 context_release(context);
977 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
978 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
979 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
981 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
982 return FALSE;
984 /* Source and/or destination need to be on the GL side */
985 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
986 return FALSE;
988 switch (blit_op)
990 case WINED3D_BLIT_OP_COLOR_BLIT:
991 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
992 return FALSE;
993 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
994 return FALSE;
995 break;
997 case WINED3D_BLIT_OP_DEPTH_BLIT:
998 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
999 return FALSE;
1000 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1001 return FALSE;
1002 break;
1004 default:
1005 return FALSE;
1008 if (!(src_format->id == dst_format->id
1009 || (is_identity_fixup(src_format->color_fixup)
1010 && is_identity_fixup(dst_format->color_fixup))))
1011 return FALSE;
1013 return TRUE;
1016 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1017 DWORD color, struct wined3d_color *float_color)
1019 const struct wined3d_format *format = surface->resource.format;
1020 const struct wined3d_palette *palette;
1022 switch (format->id)
1024 case WINED3DFMT_P8_UINT:
1025 palette = surface->container->swapchain ? surface->container->swapchain->palette : NULL;
1027 if (palette)
1029 float_color->r = palette->colors[color].rgbRed / 255.0f;
1030 float_color->g = palette->colors[color].rgbGreen / 255.0f;
1031 float_color->b = palette->colors[color].rgbBlue / 255.0f;
1033 else
1035 float_color->r = 0.0f;
1036 float_color->g = 0.0f;
1037 float_color->b = 0.0f;
1039 float_color->a = color / 255.0f;
1040 break;
1042 case WINED3DFMT_B5G6R5_UNORM:
1043 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1044 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1045 float_color->b = (color & 0x1f) / 31.0f;
1046 float_color->a = 1.0f;
1047 break;
1049 case WINED3DFMT_B8G8R8_UNORM:
1050 case WINED3DFMT_B8G8R8X8_UNORM:
1051 float_color->r = D3DCOLOR_R(color);
1052 float_color->g = D3DCOLOR_G(color);
1053 float_color->b = D3DCOLOR_B(color);
1054 float_color->a = 1.0f;
1055 break;
1057 case WINED3DFMT_B8G8R8A8_UNORM:
1058 float_color->r = D3DCOLOR_R(color);
1059 float_color->g = D3DCOLOR_G(color);
1060 float_color->b = D3DCOLOR_B(color);
1061 float_color->a = D3DCOLOR_A(color);
1062 break;
1064 default:
1065 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1066 return FALSE;
1069 return TRUE;
1072 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1074 const struct wined3d_format *format = surface->resource.format;
1076 switch (format->id)
1078 case WINED3DFMT_S1_UINT_D15_UNORM:
1079 *float_depth = depth / (float)0x00007fff;
1080 break;
1082 case WINED3DFMT_D16_UNORM:
1083 *float_depth = depth / (float)0x0000ffff;
1084 break;
1086 case WINED3DFMT_D24_UNORM_S8_UINT:
1087 case WINED3DFMT_X8D24_UNORM:
1088 *float_depth = depth / (float)0x00ffffff;
1089 break;
1091 case WINED3DFMT_D32_UNORM:
1092 *float_depth = depth / (float)0xffffffff;
1093 break;
1095 default:
1096 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1097 return FALSE;
1100 return TRUE;
1103 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1105 const struct wined3d_resource *resource = &surface->container->resource;
1106 struct wined3d_device *device = resource->device;
1107 const struct blit_shader *blitter;
1109 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1110 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1111 if (!blitter)
1113 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1114 return WINED3DERR_INVALIDCALL;
1117 return blitter->depth_fill(device, surface, rect, depth);
1120 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1121 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1123 struct wined3d_device *device = src_surface->resource.device;
1125 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1126 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1127 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1128 return WINED3DERR_INVALIDCALL;
1130 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1132 surface_modify_ds_location(dst_surface, dst_location,
1133 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1135 return WINED3D_OK;
1138 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1139 struct wined3d_surface *render_target)
1141 TRACE("surface %p, render_target %p.\n", surface, render_target);
1143 /* TODO: Check surface sizes, pools, etc. */
1145 if (render_target->resource.multisample_type)
1146 return WINED3DERR_INVALIDCALL;
1148 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1151 /* Context activation is done by the caller. */
1152 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1154 GL_EXTCALL(glDeleteBuffers(1, &surface->pbo));
1155 checkGLcall("glDeleteBuffers(1, &surface->pbo)");
1157 surface->pbo = 0;
1158 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1161 static ULONG surface_resource_incref(struct wined3d_resource *resource)
1163 return wined3d_surface_incref(surface_from_resource(resource));
1166 static ULONG surface_resource_decref(struct wined3d_resource *resource)
1168 return wined3d_surface_decref(surface_from_resource(resource));
1171 static void surface_unload(struct wined3d_resource *resource)
1173 struct wined3d_surface *surface = surface_from_resource(resource);
1174 struct wined3d_renderbuffer_entry *entry, *entry2;
1175 struct wined3d_device *device = resource->device;
1176 const struct wined3d_gl_info *gl_info;
1177 struct wined3d_context *context;
1179 TRACE("surface %p.\n", surface);
1181 if (resource->pool == WINED3D_POOL_DEFAULT)
1183 /* Default pool resources are supposed to be destroyed before Reset is called.
1184 * Implicit resources stay however. So this means we have an implicit render target
1185 * or depth stencil. The content may be destroyed, but we still have to tear down
1186 * opengl resources, so we cannot leave early.
1188 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1189 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1190 * or the depth stencil into an FBO the texture or render buffer will be removed
1191 * and all flags get lost */
1192 surface_prepare_system_memory(surface);
1193 memset(surface->resource.heap_memory, 0, surface->resource.size);
1194 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1195 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1197 /* We also get here when the ddraw swapchain is destroyed, for example
1198 * for a mode switch. In this case this surface won't necessarily be
1199 * an implicit surface. We have to mark it lost so that the
1200 * application can restore it after the mode switch. */
1201 surface->flags |= SFLAG_LOST;
1203 else
1205 surface_prepare_map_memory(surface);
1206 surface_load_location(surface, surface->resource.map_binding);
1207 surface_invalidate_location(surface, ~surface->resource.map_binding);
1210 context = context_acquire(device, NULL);
1211 gl_info = context->gl_info;
1213 /* Destroy PBOs, but load them into real sysmem before */
1214 if (surface->pbo)
1215 surface_remove_pbo(surface, gl_info);
1217 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1218 * all application-created targets the application has to release the surface
1219 * before calling _Reset
1221 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1223 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1224 list_remove(&entry->entry);
1225 HeapFree(GetProcessHeap(), 0, entry);
1227 list_init(&surface->renderbuffers);
1228 surface->current_renderbuffer = NULL;
1230 if (surface->rb_multisample)
1232 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1233 surface->rb_multisample = 0;
1235 if (surface->rb_resolved)
1237 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1238 surface->rb_resolved = 0;
1241 context_release(context);
1243 resource_unload(resource);
1246 static const struct wined3d_resource_ops surface_resource_ops =
1248 surface_resource_incref,
1249 surface_resource_decref,
1250 surface_unload,
1253 static const struct wined3d_surface_ops surface_ops =
1255 surface_private_setup,
1256 surface_unmap,
1259 /*****************************************************************************
1260 * Initializes the GDI surface, aka creates the DIB section we render to
1261 * The DIB section creation is done by calling GetDC, which will create the
1262 * section and releasing the dc to allow the app to use it. The dib section
1263 * will stay until the surface is released
1265 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1266 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1267 * avoid confusion in the shared surface code.
1269 * Returns:
1270 * WINED3D_OK on success
1271 * The return values of called methods on failure
1273 *****************************************************************************/
1274 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1276 HRESULT hr;
1278 TRACE("surface %p.\n", surface);
1280 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1282 ERR("Overlays not yet supported by GDI surfaces.\n");
1283 return WINED3DERR_INVALIDCALL;
1286 /* Sysmem textures have memory already allocated - release it,
1287 * this avoids an unnecessary memcpy. */
1288 hr = surface_create_dib_section(surface);
1289 if (FAILED(hr))
1290 return hr;
1291 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1293 /* We don't mind the nonpow2 stuff in GDI. */
1294 surface->pow2Width = surface->resource.width;
1295 surface->pow2Height = surface->resource.height;
1297 return WINED3D_OK;
1300 static void gdi_surface_unmap(struct wined3d_surface *surface)
1302 TRACE("surface %p.\n", surface);
1304 /* Tell the swapchain to update the screen. */
1305 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
1306 x11_copy_to_screen(surface->container->swapchain, &surface->lockedRect);
1308 memset(&surface->lockedRect, 0, sizeof(RECT));
1311 static const struct wined3d_surface_ops gdi_surface_ops =
1313 gdi_surface_private_setup,
1314 gdi_surface_unmap,
1317 /* This call just downloads data, the caller is responsible for binding the
1318 * correct texture. */
1319 /* Context activation is done by the caller. */
1320 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1321 DWORD dst_location)
1323 const struct wined3d_format *format = surface->resource.format;
1324 struct wined3d_bo_address data;
1326 /* Only support read back of converted P8 surfaces. */
1327 if (surface->container->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1329 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1330 return;
1333 surface_get_memory(surface, &data, dst_location);
1335 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1337 TRACE("(%p) : Calling glGetCompressedTexImage level %d, format %#x, type %#x, data %p.\n",
1338 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1340 if (data.buffer_object)
1342 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1343 checkGLcall("glBindBuffer");
1344 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, NULL));
1345 checkGLcall("glGetCompressedTexImage");
1346 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1347 checkGLcall("glBindBuffer");
1349 else
1351 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target,
1352 surface->texture_level, data.addr));
1353 checkGLcall("glGetCompressedTexImage");
1356 else
1358 void *mem;
1359 GLenum gl_format = format->glFormat;
1360 GLenum gl_type = format->glType;
1361 int src_pitch = 0;
1362 int dst_pitch = 0;
1364 if (surface->flags & SFLAG_NONPOW2)
1366 unsigned char alignment = surface->resource.device->surface_alignment;
1367 src_pitch = format->byte_count * surface->pow2Width;
1368 dst_pitch = wined3d_surface_get_pitch(surface);
1369 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1370 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1372 else
1374 mem = data.addr;
1377 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1378 surface, surface->texture_level, gl_format, gl_type, mem);
1380 if (data.buffer_object)
1382 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1383 checkGLcall("glBindBuffer");
1385 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1386 gl_format, gl_type, NULL);
1387 checkGLcall("glGetTexImage");
1389 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1390 checkGLcall("glBindBuffer");
1392 else
1394 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1395 gl_format, gl_type, mem);
1396 checkGLcall("glGetTexImage");
1399 if (surface->flags & SFLAG_NONPOW2)
1401 const BYTE *src_data;
1402 BYTE *dst_data;
1403 UINT y;
1405 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1406 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1407 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1409 * We're doing this...
1411 * instead of boxing the texture :
1412 * |<-texture width ->| -->pow2width| /\
1413 * |111111111111111111| | |
1414 * |222 Texture 222222| boxed empty | texture height
1415 * |3333 Data 33333333| | |
1416 * |444444444444444444| | \/
1417 * ----------------------------------- |
1418 * | boxed empty | boxed empty | pow2height
1419 * | | | \/
1420 * -----------------------------------
1423 * we're repacking the data to the expected texture width
1425 * |<-texture width ->| -->pow2width| /\
1426 * |111111111111111111222222222222222| |
1427 * |222333333333333333333444444444444| texture height
1428 * |444444 | |
1429 * | | \/
1430 * | | |
1431 * | empty | pow2height
1432 * | | \/
1433 * -----------------------------------
1435 * == is the same as
1437 * |<-texture width ->| /\
1438 * |111111111111111111|
1439 * |222222222222222222|texture height
1440 * |333333333333333333|
1441 * |444444444444444444| \/
1442 * --------------------
1444 * This also means that any references to surface memory should work with the data as if it were a
1445 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1447 * internally the texture is still stored in a boxed format so any references to textureName will
1448 * get a boxed texture with width pow2width and not a texture of width resource.width.
1450 * Performance should not be an issue, because applications normally do not lock the surfaces when
1451 * rendering. If an app does, the WINED3D_TEXTURE_DYNAMIC_MAP flag will kick in and the memory copy
1452 * won't be released, and doesn't have to be re-read. */
1453 src_data = mem;
1454 dst_data = data.addr;
1455 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1456 for (y = 0; y < surface->resource.height; ++y)
1458 memcpy(dst_data, src_data, dst_pitch);
1459 src_data += src_pitch;
1460 dst_data += dst_pitch;
1463 HeapFree(GetProcessHeap(), 0, mem);
1468 /* This call just uploads data, the caller is responsible for binding the
1469 * correct texture. */
1470 /* Context activation is done by the caller. */
1471 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1472 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1473 BOOL srgb, const struct wined3d_const_bo_address *data)
1475 UINT update_w = src_rect->right - src_rect->left;
1476 UINT update_h = src_rect->bottom - src_rect->top;
1478 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1479 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1480 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1482 if (surface->resource.map_count)
1484 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
1485 surface->container->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
1488 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1490 update_h *= format->height_scale.numerator;
1491 update_h /= format->height_scale.denominator;
1494 if (data->buffer_object)
1496 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
1497 checkGLcall("glBindBuffer");
1500 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1502 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1503 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1504 const BYTE *addr = data->addr;
1505 GLenum internal;
1507 addr += (src_rect->top / format->block_height) * src_pitch;
1508 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1510 if (srgb)
1511 internal = format->glGammaInternal;
1512 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1513 && wined3d_resource_is_offscreen(&surface->container->resource))
1514 internal = format->rtInternal;
1515 else
1516 internal = format->glInternal;
1518 TRACE("glCompressedTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, "
1519 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1520 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1522 if (row_length == src_pitch)
1524 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1525 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1527 else
1529 UINT row, y;
1531 /* glCompressedTexSubImage2D() ignores pixel store state, so we
1532 * can't use the unpack row length like for glTexSubImage2D. */
1533 for (row = 0, y = dst_point->y; row < row_count; ++row)
1535 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1536 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1537 y += format->block_height;
1538 addr += src_pitch;
1541 checkGLcall("glCompressedTexSubImage2D");
1543 else
1545 const BYTE *addr = data->addr;
1547 addr += src_rect->top * src_pitch;
1548 addr += src_rect->left * format->byte_count;
1550 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1551 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1552 update_w, update_h, format->glFormat, format->glType, addr);
1554 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1555 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1556 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1557 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1558 checkGLcall("glTexSubImage2D");
1561 if (data->buffer_object)
1563 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1564 checkGLcall("glBindBuffer");
1567 if (wined3d_settings.strict_draw_ordering)
1568 gl_info->gl_ops.gl.p_glFlush();
1570 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1572 struct wined3d_device *device = surface->resource.device;
1573 unsigned int i;
1575 for (i = 0; i < device->context_count; ++i)
1577 context_surface_update(device->contexts[i], surface);
1582 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
1584 UINT width_mask, height_mask;
1586 if (!rect->left && !rect->top
1587 && rect->right == surface->resource.width
1588 && rect->bottom == surface->resource.height)
1589 return TRUE;
1591 /* This assumes power of two block sizes, but NPOT block sizes would be
1592 * silly anyway. */
1593 width_mask = surface->resource.format->block_width - 1;
1594 height_mask = surface->resource.format->block_height - 1;
1596 if (!(rect->left & width_mask) && !(rect->top & height_mask)
1597 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
1598 return TRUE;
1600 return FALSE;
1603 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1604 struct wined3d_surface *src_surface, const RECT *src_rect)
1606 const struct wined3d_format *src_format;
1607 const struct wined3d_format *dst_format;
1608 const struct wined3d_gl_info *gl_info;
1609 struct wined3d_context *context;
1610 struct wined3d_bo_address data;
1611 UINT update_w, update_h;
1612 UINT dst_w, dst_h;
1613 RECT r, dst_rect;
1614 UINT src_pitch;
1615 POINT p;
1617 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1618 dst_surface, wine_dbgstr_point(dst_point),
1619 src_surface, wine_dbgstr_rect(src_rect));
1621 src_format = src_surface->resource.format;
1622 dst_format = dst_surface->resource.format;
1624 if (src_format->id != dst_format->id)
1626 WARN("Source and destination surfaces should have the same format.\n");
1627 return WINED3DERR_INVALIDCALL;
1630 if (!dst_point)
1632 p.x = 0;
1633 p.y = 0;
1634 dst_point = &p;
1636 else if (dst_point->x < 0 || dst_point->y < 0)
1638 WARN("Invalid destination point.\n");
1639 return WINED3DERR_INVALIDCALL;
1642 if (!src_rect)
1644 r.left = 0;
1645 r.top = 0;
1646 r.right = src_surface->resource.width;
1647 r.bottom = src_surface->resource.height;
1648 src_rect = &r;
1650 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1651 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1653 WARN("Invalid source rectangle.\n");
1654 return WINED3DERR_INVALIDCALL;
1657 dst_w = dst_surface->resource.width;
1658 dst_h = dst_surface->resource.height;
1660 update_w = src_rect->right - src_rect->left;
1661 update_h = src_rect->bottom - src_rect->top;
1663 if (update_w > dst_w || dst_point->x > dst_w - update_w
1664 || update_h > dst_h || dst_point->y > dst_h - update_h)
1666 WARN("Destination out of bounds.\n");
1667 return WINED3DERR_INVALIDCALL;
1670 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
1672 WARN("Source rectangle not block-aligned.\n");
1673 return WINED3DERR_INVALIDCALL;
1676 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1677 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
1679 WARN("Destination rectangle not block-aligned.\n");
1680 return WINED3DERR_INVALIDCALL;
1683 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1684 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_surface->container, FALSE))
1685 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1687 context = context_acquire(dst_surface->resource.device, NULL);
1688 gl_info = context->gl_info;
1690 /* Only load the surface for partial updates. For newly allocated texture
1691 * the texture wouldn't be the current location, and we'd upload zeroes
1692 * just to overwrite them again. */
1693 if (update_w == dst_w && update_h == dst_h)
1694 wined3d_texture_prepare_texture(dst_surface->container, context, FALSE);
1695 else
1696 surface_load_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1697 wined3d_texture_bind(dst_surface->container, context, FALSE);
1699 surface_get_memory(src_surface, &data, src_surface->locations);
1700 src_pitch = wined3d_surface_get_pitch(src_surface);
1702 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1703 src_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1705 context_invalidate_active_texture(context);
1707 context_release(context);
1709 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1710 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1712 return WINED3D_OK;
1715 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1716 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1717 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1718 /* Context activation is done by the caller. */
1719 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1721 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1722 struct wined3d_renderbuffer_entry *entry;
1723 GLuint renderbuffer = 0;
1724 unsigned int src_width, src_height;
1725 unsigned int width, height;
1727 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1729 width = rt->pow2Width;
1730 height = rt->pow2Height;
1732 else
1734 width = surface->pow2Width;
1735 height = surface->pow2Height;
1738 src_width = surface->pow2Width;
1739 src_height = surface->pow2Height;
1741 /* A depth stencil smaller than the render target is not valid */
1742 if (width > src_width || height > src_height) return;
1744 /* Remove any renderbuffer set if the sizes match */
1745 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1746 || (width == src_width && height == src_height))
1748 surface->current_renderbuffer = NULL;
1749 return;
1752 /* Look if we've already got a renderbuffer of the correct dimensions */
1753 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1755 if (entry->width == width && entry->height == height)
1757 renderbuffer = entry->id;
1758 surface->current_renderbuffer = entry;
1759 break;
1763 if (!renderbuffer)
1765 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1766 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1767 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1768 surface->resource.format->glInternal, width, height);
1770 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1771 entry->width = width;
1772 entry->height = height;
1773 entry->id = renderbuffer;
1774 list_add_head(&surface->renderbuffers, &entry->entry);
1776 surface->current_renderbuffer = entry;
1779 checkGLcall("set_compatible_renderbuffer");
1782 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1784 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1786 TRACE("surface %p.\n", surface);
1788 if (!swapchain)
1790 ERR("Surface %p is not on a swapchain.\n", surface);
1791 return GL_NONE;
1794 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
1796 if (swapchain->render_to_fbo)
1798 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1799 return GL_COLOR_ATTACHMENT0;
1801 TRACE("Returning GL_BACK\n");
1802 return GL_BACK;
1804 else if (surface->container == swapchain->front_buffer)
1806 TRACE("Returning GL_FRONT\n");
1807 return GL_FRONT;
1810 FIXME("Higher back buffer, returning GL_BACK\n");
1811 return GL_BACK;
1814 void surface_load(struct wined3d_surface *surface, BOOL srgb)
1816 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1818 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1820 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
1821 ERR("Not supported on scratch surfaces.\n");
1823 if (surface->locations & location)
1825 TRACE("surface is already in texture\n");
1826 return;
1828 TRACE("Reloading because surface is dirty.\n");
1830 surface_load_location(surface, location);
1831 surface_evict_sysmem(surface);
1834 /* See also float_16_to_32() in wined3d_private.h */
1835 static inline unsigned short float_32_to_16(const float *in)
1837 int exp = 0;
1838 float tmp = fabsf(*in);
1839 unsigned int mantissa;
1840 unsigned short ret;
1842 /* Deal with special numbers */
1843 if (*in == 0.0f)
1844 return 0x0000;
1845 if (isnan(*in))
1846 return 0x7c01;
1847 if (isinf(*in))
1848 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1850 if (tmp < powf(2, 10))
1854 tmp = tmp * 2.0f;
1855 exp--;
1856 } while (tmp < powf(2, 10));
1858 else if (tmp >= powf(2, 11))
1862 tmp /= 2.0f;
1863 exp++;
1864 } while (tmp >= powf(2, 11));
1867 mantissa = (unsigned int)tmp;
1868 if (tmp - mantissa >= 0.5f)
1869 ++mantissa; /* Round to nearest, away from zero. */
1871 exp += 10; /* Normalize the mantissa. */
1872 exp += 15; /* Exponent is encoded with excess 15. */
1874 if (exp > 30) /* too big */
1876 ret = 0x7c00; /* INF */
1878 else if (exp <= 0)
1880 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1881 while (exp <= 0)
1883 mantissa = mantissa >> 1;
1884 ++exp;
1886 ret = mantissa & 0x3ff;
1888 else
1890 ret = (exp << 10) | (mantissa & 0x3ff);
1893 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1894 return ret;
1897 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
1899 TRACE("surface %p, container %p.\n", surface, surface->container);
1901 return wined3d_texture_incref(surface->container);
1904 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
1906 TRACE("surface %p, container %p.\n", surface, surface->container);
1908 return wined3d_texture_decref(surface->container);
1911 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
1913 TRACE("surface %p.\n", surface);
1915 if (!surface->resource.device->d3d_initialized)
1917 ERR("D3D not initialized.\n");
1918 return;
1921 wined3d_texture_preload(surface->container);
1924 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
1926 TRACE("surface %p.\n", surface);
1928 return surface->resource.parent;
1931 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
1933 TRACE("surface %p.\n", surface);
1935 return &surface->resource;
1938 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
1940 TRACE("surface %p, flags %#x.\n", surface, flags);
1942 switch (flags)
1944 case WINEDDGBS_CANBLT:
1945 case WINEDDGBS_ISBLTDONE:
1946 return WINED3D_OK;
1948 default:
1949 return WINED3DERR_INVALIDCALL;
1953 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
1955 TRACE("surface %p, flags %#x.\n", surface, flags);
1957 /* XXX: DDERR_INVALIDSURFACETYPE */
1959 switch (flags)
1961 case WINEDDGFS_CANFLIP:
1962 case WINEDDGFS_ISFLIPDONE:
1963 return WINED3D_OK;
1965 default:
1966 return WINED3DERR_INVALIDCALL;
1970 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
1972 TRACE("surface %p.\n", surface);
1974 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
1975 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
1978 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
1980 TRACE("surface %p.\n", surface);
1982 surface->flags &= ~SFLAG_LOST;
1983 return WINED3D_OK;
1986 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
1988 unsigned int alignment;
1989 DWORD pitch;
1991 TRACE("surface %p.\n", surface);
1993 if (surface->pitch)
1994 return surface->pitch;
1996 alignment = surface->resource.device->surface_alignment;
1997 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
1998 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2000 TRACE("Returning %u.\n", pitch);
2002 return pitch;
2005 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2007 LONG w, h;
2009 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2011 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2013 WARN("Not an overlay surface.\n");
2014 return WINEDDERR_NOTAOVERLAYSURFACE;
2017 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2018 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2019 surface->overlay_destrect.left = x;
2020 surface->overlay_destrect.top = y;
2021 surface->overlay_destrect.right = x + w;
2022 surface->overlay_destrect.bottom = y + h;
2024 return WINED3D_OK;
2027 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2029 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2031 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2033 TRACE("Not an overlay surface.\n");
2034 return WINEDDERR_NOTAOVERLAYSURFACE;
2037 if (!surface->overlay_dest)
2039 TRACE("Overlay not visible.\n");
2040 *x = 0;
2041 *y = 0;
2042 return WINEDDERR_OVERLAYNOTVISIBLE;
2045 *x = surface->overlay_destrect.left;
2046 *y = surface->overlay_destrect.top;
2048 TRACE("Returning position %d, %d.\n", *x, *y);
2050 return WINED3D_OK;
2053 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2054 DWORD flags, struct wined3d_surface *ref)
2056 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2058 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2060 TRACE("Not an overlay surface.\n");
2061 return WINEDDERR_NOTAOVERLAYSURFACE;
2064 return WINED3D_OK;
2067 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2068 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2070 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2071 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2073 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2075 WARN("Not an overlay surface.\n");
2076 return WINEDDERR_NOTAOVERLAYSURFACE;
2078 else if (!dst_surface)
2080 WARN("Dest surface is NULL.\n");
2081 return WINED3DERR_INVALIDCALL;
2084 if (src_rect)
2086 surface->overlay_srcrect = *src_rect;
2088 else
2090 surface->overlay_srcrect.left = 0;
2091 surface->overlay_srcrect.top = 0;
2092 surface->overlay_srcrect.right = surface->resource.width;
2093 surface->overlay_srcrect.bottom = surface->resource.height;
2096 if (dst_rect)
2098 surface->overlay_destrect = *dst_rect;
2100 else
2102 surface->overlay_destrect.left = 0;
2103 surface->overlay_destrect.top = 0;
2104 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2105 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2108 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2110 surface->overlay_dest = NULL;
2111 list_remove(&surface->overlay_entry);
2114 if (flags & WINEDDOVER_SHOW)
2116 if (surface->overlay_dest != dst_surface)
2118 surface->overlay_dest = dst_surface;
2119 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2122 else if (flags & WINEDDOVER_HIDE)
2124 /* tests show that the rectangles are erased on hide */
2125 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2126 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2127 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2128 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2129 surface->overlay_dest = NULL;
2132 return WINED3D_OK;
2135 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface,
2136 const struct wined3d_gl_info *gl_info, void *mem, unsigned int pitch)
2138 struct wined3d_resource *texture_resource = &surface->container->resource;
2139 unsigned int width, height;
2140 BOOL create_dib = FALSE;
2141 DWORD valid_location = 0;
2142 HRESULT hr;
2144 if (surface->flags & SFLAG_DIBSECTION)
2146 DeleteDC(surface->hDC);
2147 DeleteObject(surface->dib.DIBsection);
2148 surface->dib.bitmap_data = NULL;
2149 surface->flags &= ~SFLAG_DIBSECTION;
2150 create_dib = TRUE;
2153 surface->locations = 0;
2154 wined3d_resource_free_sysmem(&surface->resource);
2156 width = texture_resource->width;
2157 height = texture_resource->height;
2158 surface->resource.width = width;
2159 surface->resource.height = height;
2160 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2161 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2163 surface->pow2Width = width;
2164 surface->pow2Height = height;
2166 else
2168 surface->pow2Width = surface->pow2Height = 1;
2169 while (surface->pow2Width < width)
2170 surface->pow2Width <<= 1;
2171 while (surface->pow2Height < height)
2172 surface->pow2Height <<= 1;
2175 if (surface->pow2Width != width || surface->pow2Height != height)
2176 surface->flags |= SFLAG_NONPOW2;
2177 else
2178 surface->flags &= ~SFLAG_NONPOW2;
2180 if ((surface->user_memory = mem))
2182 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
2183 valid_location = WINED3D_LOCATION_USER_MEMORY;
2185 surface->pitch = pitch;
2186 surface->resource.format = texture_resource->format;
2187 surface->resource.multisample_type = texture_resource->multisample_type;
2188 surface->resource.multisample_quality = texture_resource->multisample_quality;
2189 if (surface->pitch)
2191 surface->resource.size = height * surface->pitch;
2193 else
2195 /* User memory surfaces don't have the regular surface alignment. */
2196 surface->resource.size = wined3d_format_calculate_size(texture_resource->format,
2197 1, width, height, 1);
2198 surface->pitch = wined3d_format_calculate_pitch(texture_resource->format, width);
2201 /* The format might be changed to a format that needs conversion.
2202 * If the surface didn't use PBOs previously but could now, don't
2203 * change it - whatever made us not use PBOs might come back, e.g.
2204 * color keys. */
2205 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2206 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2208 if (create_dib)
2210 if (FAILED(hr = surface_create_dib_section(surface)))
2212 ERR("Failed to create dib section, hr %#x.\n", hr);
2213 return hr;
2215 if (!valid_location)
2216 valid_location = WINED3D_LOCATION_DIB;
2219 if (!valid_location)
2221 surface_prepare_system_memory(surface);
2222 valid_location = WINED3D_LOCATION_SYSMEM;
2225 surface_validate_location(surface, valid_location);
2227 return WINED3D_OK;
2230 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2231 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2233 unsigned short *dst_s;
2234 const float *src_f;
2235 unsigned int x, y;
2237 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2239 for (y = 0; y < h; ++y)
2241 src_f = (const float *)(src + y * pitch_in);
2242 dst_s = (unsigned short *) (dst + y * pitch_out);
2243 for (x = 0; x < w; ++x)
2245 dst_s[x] = float_32_to_16(src_f + x);
2250 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2251 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2253 static const unsigned char convert_5to8[] =
2255 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2256 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2257 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2258 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2260 static const unsigned char convert_6to8[] =
2262 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2263 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2264 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2265 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2266 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2267 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2268 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2269 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2271 unsigned int x, y;
2273 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2275 for (y = 0; y < h; ++y)
2277 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2278 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2279 for (x = 0; x < w; ++x)
2281 WORD pixel = src_line[x];
2282 dst_line[x] = 0xff000000
2283 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2284 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2285 | convert_5to8[(pixel & 0x001f)];
2290 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2291 * in both cases we're just setting the X / Alpha channel to 0xff. */
2292 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2293 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2295 unsigned int x, y;
2297 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2299 for (y = 0; y < h; ++y)
2301 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2302 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2304 for (x = 0; x < w; ++x)
2306 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2311 static inline BYTE cliptobyte(int x)
2313 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2316 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2317 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2319 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2320 unsigned int x, y;
2322 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2324 for (y = 0; y < h; ++y)
2326 const BYTE *src_line = src + y * pitch_in;
2327 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2328 for (x = 0; x < w; ++x)
2330 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2331 * C = Y - 16; D = U - 128; E = V - 128;
2332 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2333 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2334 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2335 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2336 * U and V are shared between the pixels. */
2337 if (!(x & 1)) /* For every even pixel, read new U and V. */
2339 d = (int) src_line[1] - 128;
2340 e = (int) src_line[3] - 128;
2341 r2 = 409 * e + 128;
2342 g2 = - 100 * d - 208 * e + 128;
2343 b2 = 516 * d + 128;
2345 c2 = 298 * ((int) src_line[0] - 16);
2346 dst_line[x] = 0xff000000
2347 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2348 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2349 | cliptobyte((c2 + b2) >> 8); /* blue */
2350 /* Scale RGB values to 0..255 range,
2351 * then clip them if still not in range (may be negative),
2352 * then shift them within DWORD if necessary. */
2353 src_line += 2;
2358 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2359 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2361 unsigned int x, y;
2362 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2364 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2366 for (y = 0; y < h; ++y)
2368 const BYTE *src_line = src + y * pitch_in;
2369 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2370 for (x = 0; x < w; ++x)
2372 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2373 * C = Y - 16; D = U - 128; E = V - 128;
2374 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2375 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2376 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2377 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2378 * U and V are shared between the pixels. */
2379 if (!(x & 1)) /* For every even pixel, read new U and V. */
2381 d = (int) src_line[1] - 128;
2382 e = (int) src_line[3] - 128;
2383 r2 = 409 * e + 128;
2384 g2 = - 100 * d - 208 * e + 128;
2385 b2 = 516 * d + 128;
2387 c2 = 298 * ((int) src_line[0] - 16);
2388 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2389 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2390 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2391 /* Scale RGB values to 0..255 range,
2392 * then clip them if still not in range (may be negative),
2393 * then shift them within DWORD if necessary. */
2394 src_line += 2;
2399 struct d3dfmt_converter_desc
2401 enum wined3d_format_id from, to;
2402 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2405 static const struct d3dfmt_converter_desc converters[] =
2407 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2408 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2409 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2410 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2411 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2412 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2415 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2416 enum wined3d_format_id to)
2418 unsigned int i;
2420 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2422 if (converters[i].from == from && converters[i].to == to)
2423 return &converters[i];
2426 return NULL;
2429 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2431 struct wined3d_map_desc src_map, dst_map;
2432 const struct d3dfmt_converter_desc *conv;
2433 struct wined3d_texture *ret = NULL;
2434 struct wined3d_resource_desc desc;
2435 struct wined3d_surface *dst;
2437 conv = find_converter(source->resource.format->id, to_fmt);
2438 if (!conv)
2440 FIXME("Cannot find a conversion function from format %s to %s.\n",
2441 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2442 return NULL;
2445 /* FIXME: Multisampled conversion? */
2446 wined3d_resource_get_desc(&source->resource, &desc);
2447 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2448 desc.format = to_fmt;
2449 desc.usage = 0;
2450 desc.pool = WINED3D_POOL_SCRATCH;
2451 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2452 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, NULL, &wined3d_null_parent_ops, &ret)))
2454 ERR("Failed to create a destination surface for conversion.\n");
2455 return NULL;
2457 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2459 memset(&src_map, 0, sizeof(src_map));
2460 memset(&dst_map, 0, sizeof(dst_map));
2462 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2464 ERR("Failed to lock the source surface.\n");
2465 wined3d_texture_decref(ret);
2466 return NULL;
2468 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2470 ERR("Failed to lock the destination surface.\n");
2471 wined3d_surface_unmap(source);
2472 wined3d_texture_decref(ret);
2473 return NULL;
2476 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2477 source->resource.width, source->resource.height);
2479 wined3d_surface_unmap(dst);
2480 wined3d_surface_unmap(source);
2482 return ret;
2485 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2486 unsigned int bpp, UINT pitch, DWORD color)
2488 BYTE *first;
2489 unsigned int x, y;
2491 /* Do first row */
2493 #define COLORFILL_ROW(type) \
2494 do { \
2495 type *d = (type *)buf; \
2496 for (x = 0; x < width; ++x) \
2497 d[x] = (type)color; \
2498 } while(0)
2500 switch (bpp)
2502 case 1:
2503 COLORFILL_ROW(BYTE);
2504 break;
2506 case 2:
2507 COLORFILL_ROW(WORD);
2508 break;
2510 case 3:
2512 BYTE *d = buf;
2513 for (x = 0; x < width; ++x, d += 3)
2515 d[0] = (color ) & 0xff;
2516 d[1] = (color >> 8) & 0xff;
2517 d[2] = (color >> 16) & 0xff;
2519 break;
2521 case 4:
2522 COLORFILL_ROW(DWORD);
2523 break;
2525 default:
2526 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2527 return WINED3DERR_NOTAVAILABLE;
2530 #undef COLORFILL_ROW
2532 /* Now copy first row. */
2533 first = buf;
2534 for (y = 1; y < height; ++y)
2536 buf += pitch;
2537 memcpy(buf, first, width * bpp);
2540 return WINED3D_OK;
2543 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2545 return surface_from_resource(resource);
2548 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2550 TRACE("surface %p.\n", surface);
2552 if (!surface->resource.map_count)
2554 WARN("Trying to unmap unmapped surface.\n");
2555 return WINEDDERR_NOTLOCKED;
2557 --surface->resource.map_count;
2559 surface->surface_ops->surface_unmap(surface);
2561 return WINED3D_OK;
2564 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2565 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
2567 const struct wined3d_format *format = surface->resource.format;
2568 struct wined3d_device *device = surface->resource.device;
2569 struct wined3d_context *context;
2570 const struct wined3d_gl_info *gl_info;
2571 BYTE *base_memory;
2573 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
2574 surface, map_desc, wine_dbgstr_rect(rect), flags);
2576 if (surface->resource.map_count)
2578 WARN("Surface is already mapped.\n");
2579 return WINED3DERR_INVALIDCALL;
2582 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
2583 && !surface_check_block_align(surface, rect))
2585 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
2586 wine_dbgstr_rect(rect), format->block_width, format->block_height);
2588 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2589 return WINED3DERR_INVALIDCALL;
2592 ++surface->resource.map_count;
2594 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2595 WARN("Trying to lock unlockable surface.\n");
2597 /* Performance optimization: Count how often a surface is mapped, if it is
2598 * mapped regularly do not throw away the system memory copy. This avoids
2599 * the need to download the surface from OpenGL all the time. The surface
2600 * is still downloaded if the OpenGL texture is changed. Note that this
2601 * only really makes sense for managed textures.*/
2602 if (!(surface->container->flags & WINED3D_TEXTURE_DYNAMIC_MAP)
2603 && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2605 if (++surface->lockCount > MAXLOCKCOUNT)
2607 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2608 surface->container->flags |= WINED3D_TEXTURE_DYNAMIC_MAP;
2612 surface_prepare_map_memory(surface);
2613 if (flags & WINED3D_MAP_DISCARD)
2615 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2616 wined3d_debug_location(surface->resource.map_binding));
2617 surface_validate_location(surface, surface->resource.map_binding);
2619 else
2621 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2622 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2624 surface_load_location(surface, surface->resource.map_binding);
2627 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2628 surface_invalidate_location(surface, ~surface->resource.map_binding);
2630 switch (surface->resource.map_binding)
2632 case WINED3D_LOCATION_SYSMEM:
2633 base_memory = surface->resource.heap_memory;
2634 break;
2636 case WINED3D_LOCATION_USER_MEMORY:
2637 base_memory = surface->user_memory;
2638 break;
2640 case WINED3D_LOCATION_DIB:
2641 base_memory = surface->dib.bitmap_data;
2642 break;
2644 case WINED3D_LOCATION_BUFFER:
2645 context = context_acquire(device, NULL);
2646 gl_info = context->gl_info;
2648 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
2649 base_memory = GL_EXTCALL(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE));
2650 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
2651 checkGLcall("map PBO");
2653 context_release(context);
2654 break;
2656 default:
2657 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2658 base_memory = NULL;
2661 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2662 map_desc->row_pitch = surface->resource.width * format->byte_count;
2663 else
2664 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2665 map_desc->slice_pitch = 0;
2667 if (!rect)
2669 map_desc->data = base_memory;
2670 surface->lockedRect.left = 0;
2671 surface->lockedRect.top = 0;
2672 surface->lockedRect.right = surface->resource.width;
2673 surface->lockedRect.bottom = surface->resource.height;
2675 else
2677 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2679 /* Compressed textures are block based, so calculate the offset of
2680 * the block that contains the top-left pixel of the locked rectangle. */
2681 map_desc->data = base_memory
2682 + ((rect->top / format->block_height) * map_desc->row_pitch)
2683 + ((rect->left / format->block_width) * format->block_byte_count);
2685 else
2687 map_desc->data = base_memory
2688 + (map_desc->row_pitch * rect->top)
2689 + (rect->left * format->byte_count);
2691 surface->lockedRect.left = rect->left;
2692 surface->lockedRect.top = rect->top;
2693 surface->lockedRect.right = rect->right;
2694 surface->lockedRect.bottom = rect->bottom;
2697 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2698 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2700 return WINED3D_OK;
2703 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
2705 HRESULT hr;
2707 TRACE("surface %p, dc %p.\n", surface, dc);
2709 /* Give more detailed info for ddraw. */
2710 if (surface->flags & SFLAG_DCINUSE)
2711 return WINEDDERR_DCALREADYCREATED;
2713 /* Can't GetDC if the surface is locked. */
2714 if (surface->resource.map_count)
2715 return WINED3DERR_INVALIDCALL;
2717 /* Create a DIB section if there isn't a dc yet. */
2718 if (!surface->hDC)
2720 if (surface->flags & SFLAG_CLIENT)
2722 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
2723 surface_release_client_storage(surface);
2725 hr = surface_create_dib_section(surface);
2726 if (FAILED(hr))
2727 return WINED3DERR_INVALIDCALL;
2728 if (!(surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2729 || surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2730 || surface->pbo))
2731 surface->resource.map_binding = WINED3D_LOCATION_DIB;
2734 surface_load_location(surface, WINED3D_LOCATION_DIB);
2735 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
2737 surface->flags |= SFLAG_DCINUSE;
2738 surface->resource.map_count++;
2740 *dc = surface->hDC;
2741 TRACE("Returning dc %p.\n", *dc);
2743 return WINED3D_OK;
2746 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
2748 TRACE("surface %p, dc %p.\n", surface, dc);
2750 if (!(surface->flags & SFLAG_DCINUSE))
2751 return WINEDDERR_NODC;
2753 if (surface->hDC != dc)
2755 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
2756 dc, surface->hDC);
2757 return WINEDDERR_NODC;
2760 surface->resource.map_count--;
2761 surface->flags &= ~SFLAG_DCINUSE;
2763 if (surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2764 || (surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2765 && surface->resource.map_binding != WINED3D_LOCATION_DIB))
2767 /* The game Salammbo modifies the surface contents without mapping the surface between
2768 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
2769 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
2770 * Do not only copy the DIB to the map location, but also make sure the map location is
2771 * copied back to the DIB in the next getdc call.
2773 * The same consideration applies to user memory surfaces. */
2774 surface_load_location(surface, surface->resource.map_binding);
2775 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
2778 return WINED3D_OK;
2781 static void read_from_framebuffer(struct wined3d_surface *surface, DWORD dst_location)
2783 struct wined3d_device *device = surface->resource.device;
2784 const struct wined3d_gl_info *gl_info;
2785 struct wined3d_context *context;
2786 BYTE *mem;
2787 BYTE *row, *top, *bottom;
2788 int i;
2789 BOOL srcIsUpsideDown;
2790 struct wined3d_bo_address data;
2792 surface_get_memory(surface, &data, dst_location);
2794 context = context_acquire(device, surface);
2795 context_apply_blit_state(context, device);
2796 gl_info = context->gl_info;
2798 /* Select the correct read buffer, and give some debug output.
2799 * There is no need to keep track of the current read buffer or reset it, every part of the code
2800 * that reads sets the read buffer as desired.
2802 if (wined3d_resource_is_offscreen(&surface->container->resource))
2804 /* Mapping the primary render target which is not on a swapchain.
2805 * Read from the back buffer. */
2806 TRACE("Mapping offscreen render target.\n");
2807 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2808 srcIsUpsideDown = TRUE;
2810 else
2812 /* Onscreen surfaces are always part of a swapchain */
2813 GLenum buffer = surface_get_gl_buffer(surface);
2814 TRACE("Mapping %#x buffer.\n", buffer);
2815 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2816 checkGLcall("glReadBuffer");
2817 srcIsUpsideDown = FALSE;
2820 if (data.buffer_object)
2822 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
2823 checkGLcall("glBindBuffer");
2826 /* Setup pixel store pack state -- to glReadPixels into the correct place */
2827 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH,
2828 wined3d_surface_get_pitch(surface) / surface->resource.format->byte_count);
2829 checkGLcall("glPixelStorei");
2831 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
2832 surface->resource.width, surface->resource.height,
2833 surface->resource.format->glFormat,
2834 surface->resource.format->glType, data.addr);
2835 checkGLcall("glReadPixels");
2837 /* Reset previous pixel store pack state */
2838 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
2839 checkGLcall("glPixelStorei");
2841 if (!srcIsUpsideDown)
2843 /* glReadPixels returns the image upside down, and there is no way to prevent this.
2844 * Flip the lines in software. */
2845 UINT pitch = wined3d_surface_get_pitch(surface);
2847 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
2848 goto error;
2850 if (data.buffer_object)
2852 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
2853 checkGLcall("glMapBuffer");
2855 else
2856 mem = data.addr;
2858 top = mem;
2859 bottom = mem + pitch * (surface->resource.height - 1);
2860 for (i = 0; i < surface->resource.height / 2; i++)
2862 memcpy(row, top, pitch);
2863 memcpy(top, bottom, pitch);
2864 memcpy(bottom, row, pitch);
2865 top += pitch;
2866 bottom -= pitch;
2868 HeapFree(GetProcessHeap(), 0, row);
2870 if (data.buffer_object)
2871 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
2874 error:
2875 if (data.buffer_object)
2877 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2878 checkGLcall("glBindBuffer");
2881 context_release(context);
2884 /* Read the framebuffer contents into a texture. Note that this function
2885 * doesn't do any kind of flipping. Using this on an onscreen surface will
2886 * result in a flipped D3D texture. */
2887 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
2889 struct wined3d_device *device = surface->resource.device;
2890 const struct wined3d_gl_info *gl_info;
2891 struct wined3d_context *context;
2893 context = context_acquire(device, surface);
2894 gl_info = context->gl_info;
2895 device_invalidate_state(device, STATE_FRAMEBUFFER);
2897 wined3d_texture_prepare_texture(surface->container, context, srgb);
2898 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
2900 TRACE("Reading back offscreen render target %p.\n", surface);
2902 if (wined3d_resource_is_offscreen(&surface->container->resource))
2903 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2904 else
2905 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
2906 checkGLcall("glReadBuffer");
2908 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
2909 0, 0, 0, 0, surface->resource.width, surface->resource.height);
2910 checkGLcall("glCopyTexSubImage2D");
2912 context_release(context);
2915 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2917 if (multisample)
2919 if (surface->rb_multisample)
2920 return;
2922 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2923 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2924 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
2925 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
2926 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2928 else
2930 if (surface->rb_resolved)
2931 return;
2933 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2934 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2935 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
2936 surface->pow2Width, surface->pow2Height);
2937 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2941 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
2943 if (front->container->level_count != 1 || front->container->layer_count != 1
2944 || back->container->level_count != 1 || back->container->layer_count != 1)
2945 ERR("Flip between surfaces %p and %p not supported.\n", front, back);
2947 /* Flip the surface contents */
2948 /* Flip the DC */
2950 HDC tmp;
2951 tmp = front->hDC;
2952 front->hDC = back->hDC;
2953 back->hDC = tmp;
2956 /* Flip the DIBsection */
2958 HBITMAP tmp = front->dib.DIBsection;
2959 front->dib.DIBsection = back->dib.DIBsection;
2960 back->dib.DIBsection = tmp;
2963 /* Flip the surface data */
2965 void* tmp;
2967 tmp = front->dib.bitmap_data;
2968 front->dib.bitmap_data = back->dib.bitmap_data;
2969 back->dib.bitmap_data = tmp;
2971 tmp = front->resource.heap_memory;
2972 front->resource.heap_memory = back->resource.heap_memory;
2973 back->resource.heap_memory = tmp;
2976 /* Flip the PBO */
2978 GLuint tmp_pbo = front->pbo;
2979 front->pbo = back->pbo;
2980 back->pbo = tmp_pbo;
2983 /* Flip the opengl texture */
2985 GLuint tmp;
2987 tmp = back->container->texture_rgb.name;
2988 back->container->texture_rgb.name = front->container->texture_rgb.name;
2989 front->container->texture_rgb.name = tmp;
2991 tmp = back->container->texture_srgb.name;
2992 back->container->texture_srgb.name = front->container->texture_srgb.name;
2993 front->container->texture_srgb.name = tmp;
2995 tmp = back->rb_multisample;
2996 back->rb_multisample = front->rb_multisample;
2997 front->rb_multisample = tmp;
2999 tmp = back->rb_resolved;
3000 back->rb_resolved = front->rb_resolved;
3001 front->rb_resolved = tmp;
3003 resource_unload(&back->resource);
3004 resource_unload(&front->resource);
3008 DWORD tmp_flags = back->flags;
3009 back->flags = front->flags;
3010 front->flags = tmp_flags;
3012 tmp_flags = back->locations;
3013 back->locations = front->locations;
3014 front->locations = tmp_flags;
3018 /* Does a direct frame buffer -> texture copy. Stretching is done with single
3019 * pixel copy calls. */
3020 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3021 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3023 struct wined3d_device *device = dst_surface->resource.device;
3024 const struct wined3d_gl_info *gl_info;
3025 float xrel, yrel;
3026 struct wined3d_context *context;
3027 BOOL upsidedown = FALSE;
3028 RECT dst_rect = *dst_rect_in;
3030 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3031 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3033 if(dst_rect.top > dst_rect.bottom) {
3034 UINT tmp = dst_rect.bottom;
3035 dst_rect.bottom = dst_rect.top;
3036 dst_rect.top = tmp;
3037 upsidedown = TRUE;
3040 context = context_acquire(device, src_surface);
3041 gl_info = context->gl_info;
3042 context_apply_blit_state(context, device);
3043 wined3d_texture_load(dst_surface->container, context, FALSE);
3045 /* Bind the target texture */
3046 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
3047 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
3049 TRACE("Reading from an offscreen target\n");
3050 upsidedown = !upsidedown;
3051 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3053 else
3055 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3057 checkGLcall("glReadBuffer");
3059 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3060 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3062 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3064 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3066 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3067 ERR("Texture filtering not supported in direct blit.\n");
3069 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3070 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3072 ERR("Texture filtering not supported in direct blit\n");
3075 if (upsidedown
3076 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3077 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3079 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
3080 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3081 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3082 src_rect->left, src_surface->resource.height - src_rect->bottom,
3083 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3085 else
3087 LONG row;
3088 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
3089 /* I have to process this row by row to swap the image,
3090 * otherwise it would be upside down, so stretching in y direction
3091 * doesn't cost extra time
3093 * However, stretching in x direction can be avoided if not necessary
3095 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3096 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3098 /* Well, that stuff works, but it's very slow.
3099 * find a better way instead
3101 LONG col;
3103 for (col = dst_rect.left; col < dst_rect.right; ++col)
3105 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3106 dst_rect.left + col /* x offset */, row /* y offset */,
3107 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3110 else
3112 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3113 dst_rect.left /* x offset */, row /* y offset */,
3114 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3118 checkGLcall("glCopyTexSubImage2D");
3120 context_release(context);
3122 /* The texture is now most up to date - If the surface is a render target
3123 * and has a drawable, this path is never entered. */
3124 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3125 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3128 /* Uses the hardware to stretch and flip the image */
3129 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3130 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3132 struct wined3d_device *device = dst_surface->resource.device;
3133 GLuint src, backup = 0;
3134 float left, right, top, bottom; /* Texture coordinates */
3135 UINT fbwidth = src_surface->resource.width;
3136 UINT fbheight = src_surface->resource.height;
3137 const struct wined3d_gl_info *gl_info;
3138 struct wined3d_context *context;
3139 GLenum drawBuffer = GL_BACK;
3140 GLenum texture_target;
3141 BOOL noBackBufferBackup;
3142 BOOL src_offscreen;
3143 BOOL upsidedown = FALSE;
3144 RECT dst_rect = *dst_rect_in;
3146 TRACE("Using hwstretch blit\n");
3147 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3148 context = context_acquire(device, src_surface);
3149 gl_info = context->gl_info;
3150 context_apply_blit_state(context, device);
3151 wined3d_texture_load(dst_surface->container, context, FALSE);
3153 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
3154 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3155 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3157 /* Get it a description */
3158 wined3d_texture_load(src_surface->container, context, FALSE);
3161 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3162 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3164 if (context->aux_buffers >= 2)
3166 /* Got more than one aux buffer? Use the 2nd aux buffer */
3167 drawBuffer = GL_AUX1;
3169 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3171 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3172 drawBuffer = GL_AUX0;
3175 if (noBackBufferBackup)
3177 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3178 checkGLcall("glGenTextures");
3179 context_bind_texture(context, GL_TEXTURE_2D, backup);
3180 texture_target = GL_TEXTURE_2D;
3182 else
3184 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3185 * we are reading from the back buffer, the backup can be used as source texture
3187 texture_target = src_surface->texture_target;
3188 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3189 gl_info->gl_ops.gl.p_glEnable(texture_target);
3190 checkGLcall("glEnable(texture_target)");
3192 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3193 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3196 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3197 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3199 if(dst_rect.top > dst_rect.bottom) {
3200 UINT tmp = dst_rect.bottom;
3201 dst_rect.bottom = dst_rect.top;
3202 dst_rect.top = tmp;
3203 upsidedown = TRUE;
3206 if (src_offscreen)
3208 TRACE("Reading from an offscreen target\n");
3209 upsidedown = !upsidedown;
3210 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3212 else
3214 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3217 /* TODO: Only back up the part that will be overwritten */
3218 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3220 checkGLcall("glCopyTexSubImage2D");
3222 /* No issue with overriding these - the sampler is dirty due to blit usage */
3223 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
3224 checkGLcall("glTexParameteri");
3225 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3226 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
3227 checkGLcall("glTexParameteri");
3229 if (!src_surface->container->swapchain
3230 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
3232 src = backup ? backup : src_surface->container->texture_rgb.name;
3234 else
3236 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3237 checkGLcall("glReadBuffer(GL_FRONT)");
3239 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3240 checkGLcall("glGenTextures(1, &src)");
3241 context_bind_texture(context, GL_TEXTURE_2D, src);
3243 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3244 * out for power of 2 sizes
3246 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3247 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3248 checkGLcall("glTexImage2D");
3249 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3251 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3252 checkGLcall("glTexParameteri");
3253 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3254 checkGLcall("glTexParameteri");
3256 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3257 checkGLcall("glReadBuffer(GL_BACK)");
3259 if (texture_target != GL_TEXTURE_2D)
3261 gl_info->gl_ops.gl.p_glDisable(texture_target);
3262 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3263 texture_target = GL_TEXTURE_2D;
3266 checkGLcall("glEnd and previous");
3268 left = src_rect->left;
3269 right = src_rect->right;
3271 if (!upsidedown)
3273 top = src_surface->resource.height - src_rect->top;
3274 bottom = src_surface->resource.height - src_rect->bottom;
3276 else
3278 top = src_surface->resource.height - src_rect->bottom;
3279 bottom = src_surface->resource.height - src_rect->top;
3282 if (src_surface->container->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
3284 left /= src_surface->pow2Width;
3285 right /= src_surface->pow2Width;
3286 top /= src_surface->pow2Height;
3287 bottom /= src_surface->pow2Height;
3290 /* draw the source texture stretched and upside down. The correct surface is bound already */
3291 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3292 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3294 context_set_draw_buffer(context, drawBuffer);
3295 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3297 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3298 /* bottom left */
3299 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3300 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3302 /* top left */
3303 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3304 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3306 /* top right */
3307 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3308 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3310 /* bottom right */
3311 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3312 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3313 gl_info->gl_ops.gl.p_glEnd();
3314 checkGLcall("glEnd and previous");
3316 if (texture_target != dst_surface->texture_target)
3318 gl_info->gl_ops.gl.p_glDisable(texture_target);
3319 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3320 texture_target = dst_surface->texture_target;
3323 /* Now read the stretched and upside down image into the destination texture */
3324 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
3325 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
3327 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3328 0, 0, /* We blitted the image to the origin */
3329 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3330 checkGLcall("glCopyTexSubImage2D");
3332 if (drawBuffer == GL_BACK)
3334 /* Write the back buffer backup back. */
3335 if (backup)
3337 if (texture_target != GL_TEXTURE_2D)
3339 gl_info->gl_ops.gl.p_glDisable(texture_target);
3340 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3341 texture_target = GL_TEXTURE_2D;
3343 context_bind_texture(context, GL_TEXTURE_2D, backup);
3345 else
3347 if (texture_target != src_surface->texture_target)
3349 gl_info->gl_ops.gl.p_glDisable(texture_target);
3350 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3351 texture_target = src_surface->texture_target;
3353 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3356 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3357 /* top left */
3358 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3359 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3361 /* bottom left */
3362 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3363 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3365 /* bottom right */
3366 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3367 (float)fbheight / (float)src_surface->pow2Height);
3368 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3370 /* top right */
3371 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3372 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3373 gl_info->gl_ops.gl.p_glEnd();
3375 gl_info->gl_ops.gl.p_glDisable(texture_target);
3376 checkGLcall("glDisable(texture_target)");
3378 /* Cleanup */
3379 if (src != src_surface->container->texture_rgb.name && src != backup)
3381 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3382 checkGLcall("glDeleteTextures(1, &src)");
3384 if (backup)
3386 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3387 checkGLcall("glDeleteTextures(1, &backup)");
3390 if (wined3d_settings.strict_draw_ordering)
3391 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3393 context_release(context);
3395 /* The texture is now most up to date - If the surface is a render target
3396 * and has a drawable, this path is never entered. */
3397 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3398 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3401 /* Front buffer coordinates are always full screen coordinates, but our GL
3402 * drawable is limited to the window's client area. The sysmem and texture
3403 * copies do have the full screen size. Note that GL has a bottom-left
3404 * origin, while D3D has a top-left origin. */
3405 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3407 UINT drawable_height;
3409 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3411 POINT offset = {0, 0};
3412 RECT windowsize;
3414 ScreenToClient(window, &offset);
3415 OffsetRect(rect, offset.x, offset.y);
3417 GetClientRect(window, &windowsize);
3418 drawable_height = windowsize.bottom - windowsize.top;
3420 else
3422 drawable_height = surface->resource.height;
3425 rect->top = drawable_height - rect->top;
3426 rect->bottom = drawable_height - rect->bottom;
3429 static void surface_blt_to_drawable(const struct wined3d_device *device,
3430 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3431 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3432 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3434 const struct wined3d_gl_info *gl_info;
3435 struct wined3d_context *context;
3436 RECT src_rect, dst_rect;
3438 src_rect = *src_rect_in;
3439 dst_rect = *dst_rect_in;
3441 context = context_acquire(device, dst_surface);
3442 gl_info = context->gl_info;
3444 /* Make sure the surface is up-to-date. This should probably use
3445 * surface_load_location() and worry about the destination surface too,
3446 * unless we're overwriting it completely. */
3447 wined3d_texture_load(src_surface->container, context, FALSE);
3449 /* Activate the destination context, set it up for blitting */
3450 context_apply_blit_state(context, device);
3452 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3453 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3455 device->blitter->set_shader(device->blit_priv, context, src_surface);
3457 if (alpha_test)
3459 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3460 checkGLcall("glEnable(GL_ALPHA_TEST)");
3462 /* For P8 surfaces, the alpha component contains the palette index.
3463 * Which means that the colorkey is one of the palette entries. In
3464 * other cases pixels that should be masked away have alpha set to 0. */
3465 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3466 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3467 (float)src_surface->container->src_blt_color_key.color_space_low_value / 255.0f);
3468 else
3469 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3470 checkGLcall("glAlphaFunc");
3472 else
3474 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3475 checkGLcall("glDisable(GL_ALPHA_TEST)");
3478 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3480 if (alpha_test)
3482 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3483 checkGLcall("glDisable(GL_ALPHA_TEST)");
3486 /* Leave the opengl state valid for blitting */
3487 device->blitter->unset_shader(context->gl_info);
3489 if (wined3d_settings.strict_draw_ordering
3490 || (dst_surface->container->swapchain
3491 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3492 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3494 context_release(context);
3497 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3499 struct wined3d_device *device = s->resource.device;
3500 const struct blit_shader *blitter;
3502 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
3503 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
3504 if (!blitter)
3506 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3507 return WINED3DERR_INVALIDCALL;
3510 return blitter->color_fill(device, s, rect, color);
3513 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3514 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3515 enum wined3d_texture_filter_type filter)
3517 struct wined3d_device *device = dst_surface->resource.device;
3518 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3519 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3520 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3522 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3523 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3524 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3526 /* Get the swapchain. One of the surfaces has to be a primary surface */
3527 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3529 WARN("Destination is in sysmem, rejecting gl blt\n");
3530 return WINED3DERR_INVALIDCALL;
3533 dst_swapchain = dst_surface->container->swapchain;
3535 if (src_surface)
3537 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3539 WARN("Src is in sysmem, rejecting gl blt\n");
3540 return WINED3DERR_INVALIDCALL;
3543 src_swapchain = src_surface->container->swapchain;
3545 else
3547 src_swapchain = NULL;
3550 /* Early sort out of cases where no render target is used */
3551 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
3553 TRACE("No surface is render target, not using hardware blit.\n");
3554 return WINED3DERR_INVALIDCALL;
3557 /* No destination color keying supported */
3558 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
3560 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3561 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3562 return WINED3DERR_INVALIDCALL;
3565 if (dst_swapchain && dst_swapchain == src_swapchain)
3567 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3568 return WINED3DERR_INVALIDCALL;
3571 if (dst_swapchain && src_swapchain)
3573 FIXME("Implement hardware blit between two different swapchains\n");
3574 return WINED3DERR_INVALIDCALL;
3577 if (dst_swapchain)
3579 /* Handled with regular texture -> swapchain blit */
3580 if (src_surface == rt)
3581 TRACE("Blit from active render target to a swapchain\n");
3583 else if (src_swapchain && dst_surface == rt)
3585 FIXME("Implement blit from a swapchain to the active render target\n");
3586 return WINED3DERR_INVALIDCALL;
3589 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
3591 /* Blit from render target to texture */
3592 BOOL stretchx;
3594 /* P8 read back is not implemented */
3595 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
3596 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
3598 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3599 return WINED3DERR_INVALIDCALL;
3602 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3604 TRACE("Color keying not supported by frame buffer to texture blit\n");
3605 return WINED3DERR_INVALIDCALL;
3606 /* Destination color key is checked above */
3609 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
3610 stretchx = TRUE;
3611 else
3612 stretchx = FALSE;
3614 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3615 * flip the image nor scale it.
3617 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3618 * -> If the app wants an image width an unscaled width, copy it line per line
3619 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
3620 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3621 * back buffer. This is slower than reading line per line, thus not used for flipping
3622 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3623 * pixel by pixel. */
3624 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
3625 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
3627 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
3628 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
3630 else
3632 TRACE("Using hardware stretching to flip / stretch the texture.\n");
3633 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
3636 surface_evict_sysmem(dst_surface);
3638 return WINED3D_OK;
3640 else if (src_surface)
3642 /* Blit from offscreen surface to render target */
3643 struct wined3d_color_key old_blt_key = src_surface->container->src_blt_color_key;
3644 DWORD old_color_key_flags = src_surface->container->color_key_flags;
3646 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
3648 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3649 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
3650 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
3652 FIXME("Unsupported blit operation falling back to software\n");
3653 return WINED3DERR_INVALIDCALL;
3656 /* Color keying: Check if we have to do a color keyed blt,
3657 * and if not check if a color key is activated.
3659 * Just modify the color keying parameters in the surface and restore them afterwards
3660 * The surface keeps track of the color key last used to load the opengl surface.
3661 * PreLoad will catch the change to the flags and color key and reload if necessary.
3663 if (flags & WINEDDBLT_KEYSRC)
3665 /* Use color key from surface */
3667 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
3669 /* Use color key from DDBltFx */
3670 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT, &DDBltFx->ddckSrcColorkey);
3672 else
3674 /* Do not use color key */
3675 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT, NULL);
3678 surface_blt_to_drawable(device, filter,
3679 flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_ALPHATEST),
3680 src_surface, src_rect, dst_surface, dst_rect);
3682 /* Restore the color key parameters */
3683 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT,
3684 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
3686 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
3687 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
3689 return WINED3D_OK;
3692 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3693 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3694 return WINED3DERR_INVALIDCALL;
3697 /* Context activation is done by the caller. */
3698 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
3699 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
3701 struct wined3d_device *device = surface->resource.device;
3702 const struct wined3d_gl_info *gl_info = context->gl_info;
3703 GLint compare_mode = GL_NONE;
3704 struct blt_info info;
3705 GLint old_binding = 0;
3706 RECT rect;
3708 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
3710 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
3711 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
3712 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3713 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
3714 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
3715 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
3716 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
3717 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
3718 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3719 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
3720 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
3722 SetRect(&rect, 0, h, w, 0);
3723 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
3724 context_active_texture(context, context->gl_info, 0);
3725 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
3726 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
3727 if (gl_info->supported[ARB_SHADOW])
3729 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
3730 if (compare_mode != GL_NONE)
3731 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
3734 device->shader_backend->shader_select_depth_blt(device->shader_priv,
3735 gl_info, info.tex_type, &surface->ds_current_size);
3737 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
3738 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
3739 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
3740 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
3741 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
3742 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
3743 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
3744 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
3745 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
3746 gl_info->gl_ops.gl.p_glEnd();
3748 if (compare_mode != GL_NONE)
3749 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
3750 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
3752 gl_info->gl_ops.gl.p_glPopAttrib();
3754 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
3757 void surface_modify_ds_location(struct wined3d_surface *surface,
3758 DWORD location, UINT w, UINT h)
3760 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
3762 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
3763 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
3764 wined3d_texture_set_dirty(surface->container);
3766 surface->ds_current_size.cx = w;
3767 surface->ds_current_size.cy = h;
3768 surface->locations = location;
3771 /* Context activation is done by the caller. */
3772 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3774 const struct wined3d_gl_info *gl_info = context->gl_info;
3775 struct wined3d_device *device = surface->resource.device;
3776 GLsizei w, h;
3778 TRACE("surface %p, new location %#x.\n", surface, location);
3780 /* TODO: Make this work for modes other than FBO */
3781 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3783 if (!(surface->locations & location))
3785 w = surface->ds_current_size.cx;
3786 h = surface->ds_current_size.cy;
3787 surface->ds_current_size.cx = 0;
3788 surface->ds_current_size.cy = 0;
3790 else
3792 w = surface->resource.width;
3793 h = surface->resource.height;
3796 if (surface->ds_current_size.cx == surface->resource.width
3797 && surface->ds_current_size.cy == surface->resource.height)
3799 TRACE("Location (%#x) is already up to date.\n", location);
3800 return;
3803 if (surface->current_renderbuffer)
3805 FIXME("Not supported with fixed up depth stencil.\n");
3806 return;
3809 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3811 TRACE("Surface was discarded, no need copy data.\n");
3812 switch (location)
3814 case WINED3D_LOCATION_TEXTURE_RGB:
3815 wined3d_texture_prepare_texture(surface->container, context, FALSE);
3816 break;
3817 case WINED3D_LOCATION_RB_MULTISAMPLE:
3818 surface_prepare_rb(surface, gl_info, TRUE);
3819 break;
3820 case WINED3D_LOCATION_DRAWABLE:
3821 /* Nothing to do */
3822 break;
3823 default:
3824 FIXME("Unhandled location %#x\n", location);
3826 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
3827 surface->locations |= location;
3828 surface->ds_current_size.cx = surface->resource.width;
3829 surface->ds_current_size.cy = surface->resource.height;
3830 return;
3833 if (!surface->locations)
3835 FIXME("No up to date depth stencil location.\n");
3836 surface->locations |= location;
3837 surface->ds_current_size.cx = surface->resource.width;
3838 surface->ds_current_size.cy = surface->resource.height;
3839 return;
3842 if (location == WINED3D_LOCATION_TEXTURE_RGB)
3844 GLint old_binding = 0;
3845 GLenum bind_target;
3847 /* The render target is allowed to be smaller than the depth/stencil
3848 * buffer, so the onscreen depth/stencil buffer is potentially smaller
3849 * than the offscreen surface. Don't overwrite the offscreen surface
3850 * with undefined data. */
3851 w = min(w, context->swapchain->desc.backbuffer_width);
3852 h = min(h, context->swapchain->desc.backbuffer_height);
3854 TRACE("Copying onscreen depth buffer to depth texture.\n");
3856 if (!device->depth_blt_texture)
3857 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
3859 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3860 * directly on the FBO texture. That's because we need to flip. */
3861 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3862 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3863 NULL, WINED3D_LOCATION_DRAWABLE);
3864 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
3866 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
3867 bind_target = GL_TEXTURE_RECTANGLE_ARB;
3869 else
3871 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3872 bind_target = GL_TEXTURE_2D;
3874 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
3875 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
3876 * internal format, because the internal format might include stencil
3877 * data. In principle we should copy stencil data as well, but unless
3878 * the driver supports stencil export it's hard to do, and doesn't
3879 * seem to be needed in practice. If the hardware doesn't support
3880 * writing stencil data, the glCopyTexImage2D() call might trigger
3881 * software fallbacks. */
3882 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
3883 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3884 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3885 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3886 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3887 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
3888 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
3889 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
3891 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3892 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
3893 context_set_draw_buffer(context, GL_NONE);
3895 /* Do the actual blit */
3896 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
3897 checkGLcall("depth_blt");
3899 context_invalidate_state(context, STATE_FRAMEBUFFER);
3901 if (wined3d_settings.strict_draw_ordering)
3902 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3904 else if (location == WINED3D_LOCATION_DRAWABLE)
3906 TRACE("Copying depth texture to onscreen depth buffer.\n");
3908 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3909 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3910 NULL, WINED3D_LOCATION_DRAWABLE);
3911 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
3912 0, surface->pow2Height - h, w, h, surface->texture_target);
3913 checkGLcall("depth_blt");
3915 context_invalidate_state(context, STATE_FRAMEBUFFER);
3917 if (wined3d_settings.strict_draw_ordering)
3918 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3920 else
3922 ERR("Invalid location (%#x) specified.\n", location);
3925 surface->locations |= location;
3926 surface->ds_current_size.cx = surface->resource.width;
3927 surface->ds_current_size.cy = surface->resource.height;
3930 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
3932 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3934 surface->locations |= location;
3937 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
3939 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3941 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3942 wined3d_texture_set_dirty(surface->container);
3943 surface->locations &= ~location;
3945 if (!surface->locations)
3946 ERR("Surface %p does not have any up to date location.\n", surface);
3949 static DWORD resource_access_from_location(DWORD location)
3951 switch (location)
3953 case WINED3D_LOCATION_SYSMEM:
3954 case WINED3D_LOCATION_USER_MEMORY:
3955 case WINED3D_LOCATION_DIB:
3956 case WINED3D_LOCATION_BUFFER:
3957 return WINED3D_RESOURCE_ACCESS_CPU;
3959 case WINED3D_LOCATION_DRAWABLE:
3960 case WINED3D_LOCATION_TEXTURE_SRGB:
3961 case WINED3D_LOCATION_TEXTURE_RGB:
3962 case WINED3D_LOCATION_RB_MULTISAMPLE:
3963 case WINED3D_LOCATION_RB_RESOLVED:
3964 return WINED3D_RESOURCE_ACCESS_GPU;
3966 default:
3967 FIXME("Unhandled location %#x.\n", location);
3968 return 0;
3972 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
3974 struct wined3d_device *device = surface->resource.device;
3975 struct wined3d_context *context;
3976 const struct wined3d_gl_info *gl_info;
3977 struct wined3d_bo_address dst, src;
3978 UINT size = surface->resource.size;
3980 surface_get_memory(surface, &dst, location);
3981 surface_get_memory(surface, &src, surface->locations);
3983 if (dst.buffer_object)
3985 context = context_acquire(device, NULL);
3986 gl_info = context->gl_info;
3987 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
3988 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr));
3989 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
3990 checkGLcall("Upload PBO");
3991 context_release(context);
3992 return;
3994 if (src.buffer_object)
3996 context = context_acquire(device, NULL);
3997 gl_info = context->gl_info;
3998 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
3999 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr));
4000 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
4001 checkGLcall("Download PBO");
4002 context_release(context);
4003 return;
4005 memcpy(dst.addr, src.addr, size);
4008 static void surface_load_sysmem(struct wined3d_surface *surface,
4009 const struct wined3d_gl_info *gl_info, DWORD dst_location)
4011 if (surface->locations & surface_simple_locations)
4013 surface_copy_simple_location(surface, dst_location);
4014 return;
4017 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
4018 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4020 /* Download the surface to system memory. */
4021 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4023 struct wined3d_device *device = surface->resource.device;
4024 struct wined3d_context *context;
4026 /* TODO: Use already acquired context when possible. */
4027 context = context_acquire(device, NULL);
4029 wined3d_texture_bind_and_dirtify(surface->container, context,
4030 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
4031 surface_download_data(surface, gl_info, dst_location);
4033 context_release(context);
4035 return;
4038 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
4040 read_from_framebuffer(surface, dst_location);
4041 return;
4044 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
4045 surface, wined3d_debug_location(surface->locations));
4048 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
4049 const struct wined3d_gl_info *gl_info)
4051 RECT r;
4053 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
4054 && wined3d_resource_is_offscreen(&surface->container->resource))
4056 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
4057 return WINED3DERR_INVALIDCALL;
4060 surface_get_rect(surface, NULL, &r);
4061 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4062 surface_blt_to_drawable(surface->resource.device,
4063 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
4065 return WINED3D_OK;
4068 static HRESULT surface_load_texture(struct wined3d_surface *surface,
4069 const struct wined3d_gl_info *gl_info, BOOL srgb)
4071 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
4072 struct wined3d_device *device = surface->resource.device;
4073 const struct wined3d_color_key_conversion *conversion;
4074 struct wined3d_texture *texture = surface->container;
4075 struct wined3d_context *context;
4076 UINT width, src_pitch, dst_pitch;
4077 struct wined3d_bo_address data;
4078 struct wined3d_format format;
4079 POINT dst_point = {0, 0};
4080 BYTE *mem = NULL;
4082 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
4083 && wined3d_resource_is_offscreen(&texture->resource)
4084 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
4086 surface_load_fb_texture(surface, srgb);
4088 return WINED3D_OK;
4091 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
4092 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
4093 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4094 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4095 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4097 if (srgb)
4098 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
4099 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
4100 else
4101 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
4102 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
4104 return WINED3D_OK;
4107 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
4108 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
4109 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4110 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4111 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4113 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
4114 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
4115 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
4116 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4118 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
4119 &rect, surface, dst_location, &rect);
4121 return WINED3D_OK;
4124 /* Upload from system memory */
4126 if (srgb)
4128 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
4129 == WINED3D_LOCATION_TEXTURE_RGB)
4131 /* Performance warning... */
4132 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
4133 surface_prepare_map_memory(surface);
4134 surface_load_location(surface, surface->resource.map_binding);
4137 else
4139 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
4140 == WINED3D_LOCATION_TEXTURE_SRGB)
4142 /* Performance warning... */
4143 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
4144 surface_prepare_map_memory(surface);
4145 surface_load_location(surface, surface->resource.map_binding);
4149 if (!(surface->locations & surface_simple_locations))
4151 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
4152 /* Lets hope we get it from somewhere... */
4153 surface_prepare_system_memory(surface);
4154 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
4157 /* TODO: Use already acquired context when possible. */
4158 context = context_acquire(device, NULL);
4160 wined3d_texture_prepare_texture(texture, context, srgb);
4161 wined3d_texture_bind_and_dirtify(texture, context, srgb);
4163 width = surface->resource.width;
4164 src_pitch = wined3d_surface_get_pitch(surface);
4166 format = *texture->resource.format;
4167 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
4168 format = *wined3d_get_format(gl_info, conversion->dst_format);
4170 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
4171 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
4172 * getting called. */
4173 if ((format.convert || conversion) && surface->pbo)
4175 TRACE("Removing the pbo attached to surface %p.\n", surface);
4177 if (surface->flags & SFLAG_DIBSECTION)
4178 surface->resource.map_binding = WINED3D_LOCATION_DIB;
4179 else
4180 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
4182 surface_prepare_map_memory(surface);
4183 surface_load_location(surface, surface->resource.map_binding);
4184 surface_remove_pbo(surface, gl_info);
4187 surface_get_memory(surface, &data, surface->locations);
4188 if (format.convert)
4190 /* This code is entered for texture formats which need a fixup. */
4191 UINT height = surface->resource.height;
4193 format.byte_count = format.conv_byte_count;
4194 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4195 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4197 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4199 ERR("Out of memory (%u).\n", dst_pitch * height);
4200 context_release(context);
4201 return E_OUTOFMEMORY;
4203 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4204 dst_pitch, dst_pitch * height, width, height, 1);
4205 src_pitch = dst_pitch;
4206 data.addr = mem;
4208 else if (conversion)
4210 /* This code is only entered for color keying fixups */
4211 struct wined3d_palette *palette = NULL;
4212 UINT height = surface->resource.height;
4214 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4215 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4217 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4219 ERR("Out of memory (%u).\n", dst_pitch * height);
4220 context_release(context);
4221 return E_OUTOFMEMORY;
4223 if (texture->swapchain && texture->swapchain->palette)
4224 palette = texture->swapchain->palette;
4225 conversion->convert(data.addr, src_pitch, mem, dst_pitch,
4226 width, height, palette, &texture->gl_color_key);
4227 src_pitch = dst_pitch;
4228 data.addr = mem;
4231 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
4232 src_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
4234 context_release(context);
4236 HeapFree(GetProcessHeap(), 0, mem);
4238 return WINED3D_OK;
4241 static void surface_multisample_resolve(struct wined3d_surface *surface)
4243 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4245 if (!(surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE))
4246 ERR("Trying to resolve multisampled surface %p, but location WINED3D_LOCATION_RB_MULTISAMPLE not current.\n",
4247 surface);
4249 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
4250 surface, WINED3D_LOCATION_RB_MULTISAMPLE, &rect, surface, WINED3D_LOCATION_RB_RESOLVED, &rect);
4253 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
4255 struct wined3d_device *device = surface->resource.device;
4256 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4257 HRESULT hr;
4259 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4261 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4263 if (location == WINED3D_LOCATION_TEXTURE_RGB
4264 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4266 struct wined3d_context *context = context_acquire(device, NULL);
4267 surface_load_ds_location(surface, context, location);
4268 context_release(context);
4269 return WINED3D_OK;
4271 else if (location & surface->locations
4272 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
4274 /* Already up to date, nothing to do. */
4275 return WINED3D_OK;
4277 else
4279 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4280 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4281 return WINED3DERR_INVALIDCALL;
4285 if (surface->locations & location)
4287 TRACE("Location already up to date.\n");
4288 return WINED3D_OK;
4291 if (WARN_ON(d3d_surface))
4293 DWORD required_access = resource_access_from_location(location);
4294 if ((surface->resource.access_flags & required_access) != required_access)
4295 WARN("Operation requires %#x access, but surface only has %#x.\n",
4296 required_access, surface->resource.access_flags);
4299 if (!surface->locations)
4301 ERR("Surface %p does not have any up to date location.\n", surface);
4302 surface->flags |= SFLAG_LOST;
4303 return WINED3DERR_DEVICELOST;
4306 switch (location)
4308 case WINED3D_LOCATION_DIB:
4309 case WINED3D_LOCATION_USER_MEMORY:
4310 case WINED3D_LOCATION_SYSMEM:
4311 case WINED3D_LOCATION_BUFFER:
4312 surface_load_sysmem(surface, gl_info, location);
4313 break;
4315 case WINED3D_LOCATION_DRAWABLE:
4316 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
4317 return hr;
4318 break;
4320 case WINED3D_LOCATION_RB_RESOLVED:
4321 surface_multisample_resolve(surface);
4322 break;
4324 case WINED3D_LOCATION_TEXTURE_RGB:
4325 case WINED3D_LOCATION_TEXTURE_SRGB:
4326 if (FAILED(hr = surface_load_texture(surface, gl_info, location == WINED3D_LOCATION_TEXTURE_SRGB)))
4327 return hr;
4328 break;
4330 default:
4331 ERR("Don't know how to handle location %#x.\n", location);
4332 break;
4335 surface_validate_location(surface, location);
4337 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
4338 surface_evict_sysmem(surface);
4340 return WINED3D_OK;
4343 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
4344 /* Context activation is done by the caller. */
4345 static void ffp_blit_free(struct wined3d_device *device) { }
4347 /* Context activation is done by the caller. */
4348 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4350 const struct wined3d_gl_info *gl_info = context->gl_info;
4352 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
4353 checkGLcall("glEnable(target)");
4355 return WINED3D_OK;
4358 /* Context activation is done by the caller. */
4359 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
4361 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
4362 checkGLcall("glDisable(GL_TEXTURE_2D)");
4363 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4365 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4366 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4368 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4370 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
4371 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4375 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4376 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4377 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4379 switch (blit_op)
4381 case WINED3D_BLIT_OP_COLOR_BLIT:
4382 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4383 return FALSE;
4385 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4387 TRACE("Checking support for fixup:\n");
4388 dump_color_fixup_desc(src_format->color_fixup);
4391 /* We only support identity conversions. */
4392 if (!is_identity_fixup(src_format->color_fixup)
4393 || !is_identity_fixup(dst_format->color_fixup))
4395 TRACE("Fixups are not supported.\n");
4396 return FALSE;
4399 return TRUE;
4401 case WINED3D_BLIT_OP_COLOR_FILL:
4402 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
4403 return FALSE;
4405 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4407 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4408 return FALSE;
4410 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4412 TRACE("Color fill not supported\n");
4413 return FALSE;
4416 /* FIXME: We should reject color fills on formats with fixups,
4417 * but this would break P8 color fills for example. */
4419 return TRUE;
4421 case WINED3D_BLIT_OP_DEPTH_FILL:
4422 return TRUE;
4424 default:
4425 TRACE("Unsupported blit_op=%d\n", blit_op);
4426 return FALSE;
4430 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4431 const RECT *dst_rect, const struct wined3d_color *color)
4433 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4434 struct wined3d_rendertarget_view *view;
4435 struct wined3d_fb_state fb = {&view, NULL};
4436 HRESULT hr;
4438 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4439 NULL, &wined3d_null_parent_ops, &view)))
4441 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4442 return hr;
4445 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4446 wined3d_rendertarget_view_decref(view);
4448 return WINED3D_OK;
4451 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4452 const RECT *dst_rect, float depth)
4454 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4455 struct wined3d_fb_state fb = {NULL, NULL};
4456 HRESULT hr;
4458 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4459 NULL, &wined3d_null_parent_ops, &fb.depth_stencil)))
4461 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4462 return hr;
4465 device_clear_render_targets(device, 0, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4466 wined3d_rendertarget_view_decref(fb.depth_stencil);
4468 return WINED3D_OK;
4471 const struct blit_shader ffp_blit = {
4472 ffp_blit_alloc,
4473 ffp_blit_free,
4474 ffp_blit_set,
4475 ffp_blit_unset,
4476 ffp_blit_supported,
4477 ffp_blit_color_fill,
4478 ffp_blit_depth_fill,
4481 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4483 return WINED3D_OK;
4486 /* Context activation is done by the caller. */
4487 static void cpu_blit_free(struct wined3d_device *device)
4491 /* Context activation is done by the caller. */
4492 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4494 return WINED3D_OK;
4497 /* Context activation is done by the caller. */
4498 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4502 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4503 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4504 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4506 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4508 return TRUE;
4511 return FALSE;
4514 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4515 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4516 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4518 UINT row_block_count;
4519 const BYTE *src_row;
4520 BYTE *dst_row;
4521 UINT x, y;
4523 src_row = src_data;
4524 dst_row = dst_data;
4526 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4528 if (!flags)
4530 for (y = 0; y < update_h; y += format->block_height)
4532 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4533 src_row += src_pitch;
4534 dst_row += dst_pitch;
4537 return WINED3D_OK;
4540 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4542 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4544 switch (format->id)
4546 case WINED3DFMT_DXT1:
4547 for (y = 0; y < update_h; y += format->block_height)
4549 struct block
4551 WORD color[2];
4552 BYTE control_row[4];
4555 const struct block *s = (const struct block *)src_row;
4556 struct block *d = (struct block *)dst_row;
4558 for (x = 0; x < row_block_count; ++x)
4560 d[x].color[0] = s[x].color[0];
4561 d[x].color[1] = s[x].color[1];
4562 d[x].control_row[0] = s[x].control_row[3];
4563 d[x].control_row[1] = s[x].control_row[2];
4564 d[x].control_row[2] = s[x].control_row[1];
4565 d[x].control_row[3] = s[x].control_row[0];
4567 src_row -= src_pitch;
4568 dst_row += dst_pitch;
4570 return WINED3D_OK;
4572 case WINED3DFMT_DXT2:
4573 case WINED3DFMT_DXT3:
4574 for (y = 0; y < update_h; y += format->block_height)
4576 struct block
4578 WORD alpha_row[4];
4579 WORD color[2];
4580 BYTE control_row[4];
4583 const struct block *s = (const struct block *)src_row;
4584 struct block *d = (struct block *)dst_row;
4586 for (x = 0; x < row_block_count; ++x)
4588 d[x].alpha_row[0] = s[x].alpha_row[3];
4589 d[x].alpha_row[1] = s[x].alpha_row[2];
4590 d[x].alpha_row[2] = s[x].alpha_row[1];
4591 d[x].alpha_row[3] = s[x].alpha_row[0];
4592 d[x].color[0] = s[x].color[0];
4593 d[x].color[1] = s[x].color[1];
4594 d[x].control_row[0] = s[x].control_row[3];
4595 d[x].control_row[1] = s[x].control_row[2];
4596 d[x].control_row[2] = s[x].control_row[1];
4597 d[x].control_row[3] = s[x].control_row[0];
4599 src_row -= src_pitch;
4600 dst_row += dst_pitch;
4602 return WINED3D_OK;
4604 default:
4605 FIXME("Compressed flip not implemented for format %s.\n",
4606 debug_d3dformat(format->id));
4607 return E_NOTIMPL;
4611 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
4612 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
4614 return E_NOTIMPL;
4617 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4618 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4619 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4621 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
4622 const struct wined3d_format *src_format, *dst_format;
4623 struct wined3d_texture *src_texture = NULL;
4624 struct wined3d_map_desc dst_map, src_map;
4625 const BYTE *sbase = NULL;
4626 HRESULT hr = WINED3D_OK;
4627 const BYTE *sbuf;
4628 BYTE *dbuf;
4629 int x, y;
4631 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4632 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4633 flags, fx, debug_d3dtexturefiltertype(filter));
4635 if (src_surface == dst_surface)
4637 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
4638 src_map = dst_map;
4639 src_format = dst_surface->resource.format;
4640 dst_format = src_format;
4642 else
4644 dst_format = dst_surface->resource.format;
4645 if (src_surface)
4647 if (dst_surface->resource.format->id != src_surface->resource.format->id)
4649 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
4651 /* The conv function writes a FIXME */
4652 WARN("Cannot convert source surface format to dest format.\n");
4653 goto release;
4655 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
4657 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
4658 src_format = src_surface->resource.format;
4660 else
4662 src_format = dst_format;
4665 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
4668 bpp = dst_surface->resource.format->byte_count;
4669 srcheight = src_rect->bottom - src_rect->top;
4670 srcwidth = src_rect->right - src_rect->left;
4671 dstheight = dst_rect->bottom - dst_rect->top;
4672 dstwidth = dst_rect->right - dst_rect->left;
4673 width = (dst_rect->right - dst_rect->left) * bpp;
4675 if (src_surface)
4676 sbase = (BYTE *)src_map.data
4677 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
4678 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
4679 if (src_surface != dst_surface)
4680 dbuf = dst_map.data;
4681 else
4682 dbuf = (BYTE *)dst_map.data
4683 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
4684 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
4686 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
4688 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
4690 if (src_surface == dst_surface)
4692 FIXME("Only plain blits supported on compressed surfaces.\n");
4693 hr = E_NOTIMPL;
4694 goto release;
4697 if (srcheight != dstheight || srcwidth != dstwidth)
4699 WARN("Stretching not supported on compressed surfaces.\n");
4700 hr = WINED3DERR_INVALIDCALL;
4701 goto release;
4704 if (!surface_check_block_align(src_surface, src_rect))
4706 WARN("Source rectangle not block-aligned.\n");
4707 hr = WINED3DERR_INVALIDCALL;
4708 goto release;
4711 if (!surface_check_block_align(dst_surface, dst_rect))
4713 WARN("Destination rectangle not block-aligned.\n");
4714 hr = WINED3DERR_INVALIDCALL;
4715 goto release;
4718 hr = surface_cpu_blt_compressed(sbase, dbuf,
4719 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
4720 src_format, flags, fx);
4721 goto release;
4724 /* First, all the 'source-less' blits */
4725 if (flags & WINEDDBLT_COLORFILL)
4727 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
4728 flags &= ~WINEDDBLT_COLORFILL;
4731 if (flags & WINEDDBLT_DEPTHFILL)
4733 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
4735 if (flags & WINEDDBLT_ROP)
4737 /* Catch some degenerate cases here. */
4738 switch (fx->dwROP)
4740 case BLACKNESS:
4741 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
4742 break;
4743 case 0xaa0029: /* No-op */
4744 break;
4745 case WHITENESS:
4746 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
4747 break;
4748 case SRCCOPY: /* Well, we do that below? */
4749 break;
4750 default:
4751 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
4752 goto error;
4754 flags &= ~WINEDDBLT_ROP;
4756 if (flags & WINEDDBLT_DDROPS)
4758 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
4760 /* Now the 'with source' blits. */
4761 if (src_surface)
4763 int sx, xinc, sy, yinc;
4765 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
4766 goto release;
4768 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
4769 && (srcwidth != dstwidth || srcheight != dstheight))
4771 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
4772 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
4775 xinc = (srcwidth << 16) / dstwidth;
4776 yinc = (srcheight << 16) / dstheight;
4778 if (!flags)
4780 /* No effects, we can cheat here. */
4781 if (dstwidth == srcwidth)
4783 if (dstheight == srcheight)
4785 /* No stretching in either direction. This needs to be as
4786 * fast as possible. */
4787 sbuf = sbase;
4789 /* Check for overlapping surfaces. */
4790 if (src_surface != dst_surface || dst_rect->top < src_rect->top
4791 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
4793 /* No overlap, or dst above src, so copy from top downwards. */
4794 for (y = 0; y < dstheight; ++y)
4796 memcpy(dbuf, sbuf, width);
4797 sbuf += src_map.row_pitch;
4798 dbuf += dst_map.row_pitch;
4801 else if (dst_rect->top > src_rect->top)
4803 /* Copy from bottom upwards. */
4804 sbuf += src_map.row_pitch * dstheight;
4805 dbuf += dst_map.row_pitch * dstheight;
4806 for (y = 0; y < dstheight; ++y)
4808 sbuf -= src_map.row_pitch;
4809 dbuf -= dst_map.row_pitch;
4810 memcpy(dbuf, sbuf, width);
4813 else
4815 /* Src and dst overlapping on the same line, use memmove. */
4816 for (y = 0; y < dstheight; ++y)
4818 memmove(dbuf, sbuf, width);
4819 sbuf += src_map.row_pitch;
4820 dbuf += dst_map.row_pitch;
4824 else
4826 /* Stretching in y direction only. */
4827 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4829 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4830 memcpy(dbuf, sbuf, width);
4831 dbuf += dst_map.row_pitch;
4835 else
4837 /* Stretching in X direction. */
4838 int last_sy = -1;
4839 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4841 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4843 if ((sy >> 16) == (last_sy >> 16))
4845 /* This source row is the same as last source row -
4846 * Copy the already stretched row. */
4847 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
4849 else
4851 #define STRETCH_ROW(type) \
4852 do { \
4853 const type *s = (const type *)sbuf; \
4854 type *d = (type *)dbuf; \
4855 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4856 d[x] = s[sx >> 16]; \
4857 } while(0)
4859 switch(bpp)
4861 case 1:
4862 STRETCH_ROW(BYTE);
4863 break;
4864 case 2:
4865 STRETCH_ROW(WORD);
4866 break;
4867 case 4:
4868 STRETCH_ROW(DWORD);
4869 break;
4870 case 3:
4872 const BYTE *s;
4873 BYTE *d = dbuf;
4874 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
4876 DWORD pixel;
4878 s = sbuf + 3 * (sx >> 16);
4879 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4880 d[0] = (pixel ) & 0xff;
4881 d[1] = (pixel >> 8) & 0xff;
4882 d[2] = (pixel >> 16) & 0xff;
4883 d += 3;
4885 break;
4887 default:
4888 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
4889 hr = WINED3DERR_NOTAVAILABLE;
4890 goto error;
4892 #undef STRETCH_ROW
4894 dbuf += dst_map.row_pitch;
4895 last_sy = sy;
4899 else
4901 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
4902 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
4903 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
4904 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
4906 /* The color keying flags are checked for correctness in ddraw */
4907 if (flags & WINEDDBLT_KEYSRC)
4909 keylow = src_surface->container->src_blt_color_key.color_space_low_value;
4910 keyhigh = src_surface->container->src_blt_color_key.color_space_high_value;
4912 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4914 keylow = fx->ddckSrcColorkey.color_space_low_value;
4915 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
4918 if (flags & WINEDDBLT_KEYDEST)
4920 /* Destination color keys are taken from the source surface! */
4921 destkeylow = src_surface->container->dst_blt_color_key.color_space_low_value;
4922 destkeyhigh = src_surface->container->dst_blt_color_key.color_space_high_value;
4924 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
4926 destkeylow = fx->ddckDestColorkey.color_space_low_value;
4927 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
4930 if (bpp == 1)
4932 keymask = 0xff;
4934 else
4936 DWORD masks[3];
4937 get_color_masks(src_format, masks);
4938 keymask = masks[0]
4939 | masks[1]
4940 | masks[2];
4942 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
4945 if (flags & WINEDDBLT_DDFX)
4947 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
4948 LONG tmpxy;
4949 dTopLeft = dbuf;
4950 dTopRight = dbuf + ((dstwidth - 1) * bpp);
4951 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
4952 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
4954 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
4956 /* I don't think we need to do anything about this flag */
4957 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
4959 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
4961 tmp = dTopRight;
4962 dTopRight = dTopLeft;
4963 dTopLeft = tmp;
4964 tmp = dBottomRight;
4965 dBottomRight = dBottomLeft;
4966 dBottomLeft = tmp;
4967 dstxinc = dstxinc * -1;
4969 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
4971 tmp = dTopLeft;
4972 dTopLeft = dBottomLeft;
4973 dBottomLeft = tmp;
4974 tmp = dTopRight;
4975 dTopRight = dBottomRight;
4976 dBottomRight = tmp;
4977 dstyinc = dstyinc * -1;
4979 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
4981 /* I don't think we need to do anything about this flag */
4982 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
4984 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
4986 tmp = dBottomRight;
4987 dBottomRight = dTopLeft;
4988 dTopLeft = tmp;
4989 tmp = dBottomLeft;
4990 dBottomLeft = dTopRight;
4991 dTopRight = tmp;
4992 dstxinc = dstxinc * -1;
4993 dstyinc = dstyinc * -1;
4995 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
4997 tmp = dTopLeft;
4998 dTopLeft = dBottomLeft;
4999 dBottomLeft = dBottomRight;
5000 dBottomRight = dTopRight;
5001 dTopRight = tmp;
5002 tmpxy = dstxinc;
5003 dstxinc = dstyinc;
5004 dstyinc = tmpxy;
5005 dstxinc = dstxinc * -1;
5007 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
5009 tmp = dTopLeft;
5010 dTopLeft = dTopRight;
5011 dTopRight = dBottomRight;
5012 dBottomRight = dBottomLeft;
5013 dBottomLeft = tmp;
5014 tmpxy = dstxinc;
5015 dstxinc = dstyinc;
5016 dstyinc = tmpxy;
5017 dstyinc = dstyinc * -1;
5019 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
5021 /* I don't think we need to do anything about this flag */
5022 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
5024 dbuf = dTopLeft;
5025 flags &= ~(WINEDDBLT_DDFX);
5028 #define COPY_COLORKEY_FX(type) \
5029 do { \
5030 const type *s; \
5031 type *d = (type *)dbuf, *dx, tmp; \
5032 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
5034 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
5035 dx = d; \
5036 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5038 tmp = s[sx >> 16]; \
5039 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
5040 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
5042 dx[0] = tmp; \
5044 dx = (type *)(((BYTE *)dx) + dstxinc); \
5046 d = (type *)(((BYTE *)d) + dstyinc); \
5048 } while(0)
5050 switch (bpp)
5052 case 1:
5053 COPY_COLORKEY_FX(BYTE);
5054 break;
5055 case 2:
5056 COPY_COLORKEY_FX(WORD);
5057 break;
5058 case 4:
5059 COPY_COLORKEY_FX(DWORD);
5060 break;
5061 case 3:
5063 const BYTE *s;
5064 BYTE *d = dbuf, *dx;
5065 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5067 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5068 dx = d;
5069 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
5071 DWORD pixel, dpixel = 0;
5072 s = sbuf + 3 * (sx>>16);
5073 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5074 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
5075 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
5076 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
5078 dx[0] = (pixel ) & 0xff;
5079 dx[1] = (pixel >> 8) & 0xff;
5080 dx[2] = (pixel >> 16) & 0xff;
5082 dx += dstxinc;
5084 d += dstyinc;
5086 break;
5088 default:
5089 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
5090 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
5091 hr = WINED3DERR_NOTAVAILABLE;
5092 goto error;
5093 #undef COPY_COLORKEY_FX
5098 error:
5099 if (flags && FIXME_ON(d3d_surface))
5101 FIXME("\tUnsupported flags: %#x.\n", flags);
5104 release:
5105 wined3d_surface_unmap(dst_surface);
5106 if (src_surface && src_surface != dst_surface)
5107 wined3d_surface_unmap(src_surface);
5108 /* Release the converted surface, if any. */
5109 if (src_texture)
5110 wined3d_texture_decref(src_texture);
5112 return hr;
5115 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5116 const RECT *dst_rect, const struct wined3d_color *color)
5118 static const RECT src_rect;
5119 WINEDDBLTFX BltFx;
5121 memset(&BltFx, 0, sizeof(BltFx));
5122 BltFx.dwSize = sizeof(BltFx);
5123 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
5124 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
5125 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
5128 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
5129 struct wined3d_surface *surface, const RECT *rect, float depth)
5131 FIXME("Depth filling not implemented by cpu_blit.\n");
5132 return WINED3DERR_INVALIDCALL;
5135 const struct blit_shader cpu_blit = {
5136 cpu_blit_alloc,
5137 cpu_blit_free,
5138 cpu_blit_set,
5139 cpu_blit_unset,
5140 cpu_blit_supported,
5141 cpu_blit_color_fill,
5142 cpu_blit_depth_fill,
5145 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5146 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5147 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5149 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5150 struct wined3d_device *device = dst_surface->resource.device;
5151 DWORD src_ds_flags, dst_ds_flags;
5152 RECT src_rect, dst_rect;
5153 BOOL scale, convert;
5155 static const DWORD simple_blit = WINEDDBLT_ASYNC
5156 | WINEDDBLT_COLORFILL
5157 | WINEDDBLT_WAIT
5158 | WINEDDBLT_DEPTHFILL
5159 | WINEDDBLT_DONOTWAIT;
5161 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5162 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
5163 flags, fx, debug_d3dtexturefiltertype(filter));
5164 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5166 if (fx)
5168 TRACE("dwSize %#x.\n", fx->dwSize);
5169 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5170 TRACE("dwROP %#x.\n", fx->dwROP);
5171 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5172 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5173 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5174 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5175 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5176 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5177 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5178 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5179 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5180 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5181 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5182 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5183 TRACE("dwReserved %#x.\n", fx->dwReserved);
5184 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5185 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5186 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5187 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5188 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5189 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5190 fx->ddckDestColorkey.color_space_low_value,
5191 fx->ddckDestColorkey.color_space_high_value);
5192 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5193 fx->ddckSrcColorkey.color_space_low_value,
5194 fx->ddckSrcColorkey.color_space_high_value);
5197 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5199 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5200 return WINEDDERR_SURFACEBUSY;
5203 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5205 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5206 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5207 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5208 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5209 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5211 WARN("The application gave us a bad destination rectangle.\n");
5212 return WINEDDERR_INVALIDRECT;
5215 if (src_surface)
5217 surface_get_rect(src_surface, src_rect_in, &src_rect);
5219 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5220 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5221 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5222 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5223 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5225 WARN("Application gave us bad source rectangle for Blt.\n");
5226 return WINEDDERR_INVALIDRECT;
5229 else
5231 memset(&src_rect, 0, sizeof(src_rect));
5234 if (!fx || !(fx->dwDDFX))
5235 flags &= ~WINEDDBLT_DDFX;
5237 if (flags & WINEDDBLT_WAIT)
5238 flags &= ~WINEDDBLT_WAIT;
5240 if (flags & WINEDDBLT_ASYNC)
5242 static unsigned int once;
5244 if (!once++)
5245 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5246 flags &= ~WINEDDBLT_ASYNC;
5249 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5250 if (flags & WINEDDBLT_DONOTWAIT)
5252 static unsigned int once;
5254 if (!once++)
5255 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5256 flags &= ~WINEDDBLT_DONOTWAIT;
5259 if (!device->d3d_initialized)
5261 WARN("D3D not initialized, using fallback.\n");
5262 goto cpu;
5265 /* We want to avoid invalidating the sysmem location for converted
5266 * surfaces, since otherwise we'd have to convert the data back when
5267 * locking them. */
5268 if (dst_surface->container->flags & WINED3D_TEXTURE_CONVERTED
5269 || dst_surface->container->resource.format->convert
5270 || wined3d_format_get_color_key_conversion(dst_surface->container, TRUE))
5272 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5273 goto cpu;
5276 if (flags & ~simple_blit)
5278 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5279 goto fallback;
5282 if (src_surface)
5283 src_swapchain = src_surface->container->swapchain;
5284 else
5285 src_swapchain = NULL;
5287 dst_swapchain = dst_surface->container->swapchain;
5289 /* This isn't strictly needed. FBO blits for example could deal with
5290 * cross-swapchain blits by first downloading the source to a texture
5291 * before switching to the destination context. We just have this here to
5292 * not have to deal with the issue, since cross-swapchain blits should be
5293 * rare. */
5294 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5296 FIXME("Using fallback for cross-swapchain blit.\n");
5297 goto fallback;
5300 scale = src_surface
5301 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5302 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5303 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5305 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5306 if (src_surface)
5307 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5308 else
5309 src_ds_flags = 0;
5311 if (src_ds_flags || dst_ds_flags)
5313 if (flags & WINEDDBLT_DEPTHFILL)
5315 float depth;
5317 TRACE("Depth fill.\n");
5319 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5320 return WINED3DERR_INVALIDCALL;
5322 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
5323 return WINED3D_OK;
5325 else
5327 if (src_ds_flags != dst_ds_flags)
5329 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
5330 return WINED3DERR_INVALIDCALL;
5333 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
5334 &src_rect, dst_surface, dst_surface->container->resource.draw_binding, &dst_rect)))
5335 return WINED3D_OK;
5338 else
5340 /* In principle this would apply to depth blits as well, but we don't
5341 * implement those in the CPU blitter at the moment. */
5342 if ((dst_surface->locations & dst_surface->resource.map_binding)
5343 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
5345 if (scale)
5346 TRACE("Not doing sysmem blit because of scaling.\n");
5347 else if (convert)
5348 TRACE("Not doing sysmem blit because of format conversion.\n");
5349 else
5350 goto cpu;
5353 if (flags & WINEDDBLT_COLORFILL)
5355 struct wined3d_color color;
5357 TRACE("Color fill.\n");
5359 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
5360 goto fallback;
5362 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
5363 return WINED3D_OK;
5365 else
5367 TRACE("Color blit.\n");
5369 /* Upload */
5370 if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5371 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5373 if (scale)
5374 TRACE("Not doing upload because of scaling.\n");
5375 else if (convert)
5376 TRACE("Not doing upload because of format conversion.\n");
5377 else
5379 POINT dst_point = {dst_rect.left, dst_rect.top};
5381 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
5383 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
5384 surface_load_location(dst_surface, dst_surface->container->resource.draw_binding);
5385 return WINED3D_OK;
5390 /* Use present for back -> front blits. The idea behind this is
5391 * that present is potentially faster than a blit, in particular
5392 * when FBO blits aren't available. Some ddraw applications like
5393 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5394 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5395 * applications can't blit directly to the frontbuffer. */
5396 if (dst_swapchain && dst_swapchain->back_buffers
5397 && dst_surface->container == dst_swapchain->front_buffer
5398 && src_surface->container == dst_swapchain->back_buffers[0])
5400 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5402 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5404 /* Set the swap effect to COPY, we don't want the backbuffer
5405 * to become undefined. */
5406 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5407 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5408 dst_swapchain->desc.swap_effect = swap_effect;
5410 return WINED3D_OK;
5413 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5414 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5415 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5417 TRACE("Using FBO blit.\n");
5419 surface_blt_fbo(device, filter,
5420 src_surface, src_surface->container->resource.draw_binding, &src_rect,
5421 dst_surface, dst_surface->container->resource.draw_binding, &dst_rect);
5422 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5423 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5425 return WINED3D_OK;
5428 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5429 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5430 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5432 TRACE("Using arbfp blit.\n");
5434 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
5435 return WINED3D_OK;
5440 fallback:
5441 /* Special cases for render targets. */
5442 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
5443 return WINED3D_OK;
5445 cpu:
5447 /* For the rest call the X11 surface implementation. For render targets
5448 * this should be implemented OpenGL accelerated in surface_blt_special(),
5449 * other blits are rather rare. */
5450 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
5453 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5454 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5456 struct wined3d_device *device = container->resource.device;
5457 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5458 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5459 UINT multisample_quality = desc->multisample_quality;
5460 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
5461 unsigned int resource_size;
5462 HRESULT hr;
5464 if (multisample_quality > 0)
5466 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
5467 multisample_quality = 0;
5470 /* Quick lockable sanity check.
5471 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5472 * this function is too deep to need to care about things like this.
5473 * Levels need to be checked too, since they all affect what can be done. */
5474 switch (desc->pool)
5476 case WINED3D_POOL_MANAGED:
5477 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5478 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5479 break;
5481 case WINED3D_POOL_DEFAULT:
5482 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5483 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5484 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5485 break;
5487 case WINED3D_POOL_SCRATCH:
5488 case WINED3D_POOL_SYSTEM_MEM:
5489 break;
5491 default:
5492 FIXME("Unknown pool %#x.\n", desc->pool);
5493 break;
5496 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5497 FIXME("Trying to create a render target that isn't in the default pool.\n");
5499 /* FIXME: Check that the format is supported by the device. */
5501 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5502 if (!resource_size)
5503 return WINED3DERR_INVALIDCALL;
5505 if (device->wined3d->flags & WINED3D_NO3D)
5506 surface->surface_ops = &gdi_surface_ops;
5507 else
5508 surface->surface_ops = &surface_ops;
5510 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
5511 desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height, 1,
5512 resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5514 WARN("Failed to initialize resource, returning %#x.\n", hr);
5515 return hr;
5518 surface->container = container;
5519 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5520 list_init(&surface->renderbuffers);
5521 list_init(&surface->overlays);
5523 /* Flags */
5524 if (flags & WINED3D_SURFACE_DISCARD)
5525 surface->flags |= SFLAG_DISCARD;
5526 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5527 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5529 surface->texture_target = target;
5530 surface->texture_level = level;
5531 surface->texture_layer = layer;
5533 /* Call the private setup routine */
5534 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5536 ERR("Private setup failed, hr %#x.\n", hr);
5537 surface_cleanup(surface);
5538 return hr;
5541 /* Similar to lockable rendertargets above, creating the DIB section
5542 * during surface initialization prevents the sysmem pointer from changing
5543 * after a wined3d_surface_getdc() call. */
5544 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5545 && SUCCEEDED(surface_create_dib_section(surface)))
5546 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5548 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5550 wined3d_resource_free_sysmem(&surface->resource);
5551 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5552 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5555 return hr;
5558 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5559 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
5561 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
5562 const struct wined3d_parent_ops *parent_ops;
5563 struct wined3d_surface *object;
5564 void *parent;
5565 HRESULT hr;
5567 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
5568 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
5569 container, desc->width, desc->height, debug_d3dformat(desc->format),
5570 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
5571 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
5573 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
5574 return E_OUTOFMEMORY;
5576 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
5578 WARN("Failed to initialize surface, returning %#x.\n", hr);
5579 HeapFree(GetProcessHeap(), 0, object);
5580 return hr;
5583 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
5584 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
5586 WARN("Failed to create surface parent, hr %#x.\n", hr);
5587 wined3d_surface_destroy(object);
5588 return hr;
5591 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
5593 object->resource.parent = parent;
5594 object->resource.parent_ops = parent_ops;
5595 *surface = object;
5597 return hr;