wined3d: Get resource info from the texture in surface_upload_from_surface().
[wine.git] / dlls / wined3d / surface.c
blob4423f99fbaf9037bb8121a8cb2df442f4ec3f262
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 unsigned int surface_get_sub_resource_idx(const struct wined3d_surface *surface)
45 return surface->texture_layer * surface->container->level_count + surface->texture_level;
48 void wined3d_surface_cleanup(struct wined3d_surface *surface)
50 struct wined3d_surface *overlay, *cur;
52 TRACE("surface %p.\n", surface);
54 if (surface->rb_multisample || surface->rb_resolved || !list_empty(&surface->renderbuffers))
56 struct wined3d_device *device = surface->container->resource.device;
57 struct wined3d_renderbuffer_entry *entry, *entry2;
58 const struct wined3d_gl_info *gl_info;
59 struct wined3d_context *context;
61 context = context_acquire(device, NULL);
62 gl_info = context->gl_info;
64 if (surface->rb_multisample)
66 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
67 context_gl_resource_released(device, surface->rb_multisample, TRUE);
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 context_gl_resource_released(device, surface->rb_resolved, TRUE);
75 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
78 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
80 TRACE("Deleting renderbuffer %u.\n", entry->id);
81 context_gl_resource_released(device, entry->id, TRUE);
82 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
83 HeapFree(GetProcessHeap(), 0, entry);
86 context_release(context);
89 if (surface->flags & SFLAG_DIBSECTION)
91 DeleteDC(surface->hDC);
92 DeleteObject(surface->dib.DIBsection);
93 surface->dib.bitmap_data = NULL;
96 if (surface->overlay_dest)
97 list_remove(&surface->overlay_entry);
99 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
101 list_remove(&overlay->overlay_entry);
102 overlay->overlay_dest = NULL;
105 resource_cleanup(&surface->resource);
108 void surface_get_drawable_size(const struct wined3d_surface *surface, const struct wined3d_context *context,
109 unsigned int *width, unsigned int *height)
111 if (surface->container->swapchain)
113 /* The drawable size of an onscreen drawable is the surface size.
114 * (Actually: The window size, but the surface is created in window
115 * size.) */
116 *width = context->current_rt->resource.width;
117 *height = context->current_rt->resource.height;
119 else if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER)
121 const struct wined3d_swapchain *swapchain = context->swapchain;
123 /* The drawable size of a backbuffer / aux buffer offscreen target is
124 * the size of the current context's drawable, which is the size of
125 * the back buffer of the swapchain the active context belongs to. */
126 *width = swapchain->desc.backbuffer_width;
127 *height = swapchain->desc.backbuffer_height;
129 else
131 /* The drawable size of an FBO target is the OpenGL texture size,
132 * which is the power of two size. */
133 *width = context->current_rt->pow2Width;
134 *height = context->current_rt->pow2Height;
138 struct blt_info
140 GLenum binding;
141 GLenum bind_target;
142 enum wined3d_gl_resource_type tex_type;
143 GLfloat coords[4][3];
146 struct float_rect
148 float l;
149 float t;
150 float r;
151 float b;
154 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
156 f->l = ((r->left * 2.0f) / w) - 1.0f;
157 f->t = ((r->top * 2.0f) / h) - 1.0f;
158 f->r = ((r->right * 2.0f) / w) - 1.0f;
159 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
162 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
164 GLfloat (*coords)[3] = info->coords;
165 struct float_rect f;
167 switch (target)
169 default:
170 FIXME("Unsupported texture target %#x\n", target);
171 /* Fall back to GL_TEXTURE_2D */
172 case GL_TEXTURE_2D:
173 info->binding = GL_TEXTURE_BINDING_2D;
174 info->bind_target = GL_TEXTURE_2D;
175 info->tex_type = WINED3D_GL_RES_TYPE_TEX_2D;
176 coords[0][0] = (float)rect->left / w;
177 coords[0][1] = (float)rect->top / h;
178 coords[0][2] = 0.0f;
180 coords[1][0] = (float)rect->right / w;
181 coords[1][1] = (float)rect->top / h;
182 coords[1][2] = 0.0f;
184 coords[2][0] = (float)rect->left / w;
185 coords[2][1] = (float)rect->bottom / h;
186 coords[2][2] = 0.0f;
188 coords[3][0] = (float)rect->right / w;
189 coords[3][1] = (float)rect->bottom / h;
190 coords[3][2] = 0.0f;
191 break;
193 case GL_TEXTURE_RECTANGLE_ARB:
194 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
195 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
196 info->tex_type = WINED3D_GL_RES_TYPE_TEX_RECT;
197 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
198 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
199 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
200 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
201 break;
203 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
204 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
205 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
206 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
207 cube_coords_float(rect, w, h, &f);
209 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
210 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
211 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
212 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
213 break;
215 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
216 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
217 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
218 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
219 cube_coords_float(rect, w, h, &f);
221 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
222 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
223 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
224 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
225 break;
227 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
228 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
229 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
230 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
231 cube_coords_float(rect, w, h, &f);
233 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
234 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
235 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
236 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
237 break;
239 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
240 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
241 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
242 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
243 cube_coords_float(rect, w, h, &f);
245 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
246 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
247 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
248 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
249 break;
251 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
252 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
253 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
254 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
255 cube_coords_float(rect, w, h, &f);
257 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
258 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
259 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
260 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
261 break;
263 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
264 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
265 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
266 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
267 cube_coords_float(rect, w, h, &f);
269 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
270 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
271 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
272 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
273 break;
277 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
279 if (rect_in)
280 *rect_out = *rect_in;
281 else
283 rect_out->left = 0;
284 rect_out->top = 0;
285 rect_out->right = surface->resource.width;
286 rect_out->bottom = surface->resource.height;
290 /* Context activation is done by the caller. */
291 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
292 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
294 const struct wined3d_gl_info *gl_info = context->gl_info;
295 struct wined3d_texture *texture = src_surface->container;
296 struct blt_info info;
298 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
300 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
301 checkGLcall("glEnable(bind_target)");
303 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
305 /* Filtering for StretchRect */
306 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
307 checkGLcall("glTexParameteri");
308 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
309 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
310 checkGLcall("glTexParameteri");
311 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
312 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
313 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
314 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
315 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
316 checkGLcall("glTexEnvi");
318 /* Draw a quad */
319 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
320 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
321 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
323 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
324 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
326 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
327 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
329 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
330 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
331 gl_info->gl_ops.gl.p_glEnd();
333 /* Unbind the texture */
334 context_bind_texture(context, info.bind_target, 0);
336 /* We changed the filtering settings on the texture. Inform the
337 * container about this to get the filters reset properly next draw. */
338 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
339 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
340 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
341 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
344 /* Works correctly only for <= 4 bpp formats. */
345 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
347 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
348 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
349 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
352 HRESULT surface_create_dib_section(struct wined3d_surface *surface)
354 struct wined3d_texture *texture = surface->container;
355 const struct wined3d_format *format = texture->resource.format;
356 unsigned int format_flags = texture->resource.format_flags;
357 unsigned int row_pitch, slice_pitch;
358 BITMAPINFO *b_info;
359 DWORD *masks;
361 TRACE("surface %p.\n", surface);
363 if (!(format_flags & WINED3DFMT_FLAG_GETDC))
365 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
366 return WINED3DERR_INVALIDCALL;
369 switch (format->byte_count)
371 case 2:
372 case 4:
373 /* Allocate extra space to store the RGB bit masks. */
374 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, FIELD_OFFSET(BITMAPINFO, bmiColors[3]));
375 break;
377 case 3:
378 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, FIELD_OFFSET(BITMAPINFO, bmiColors[0]));
379 break;
381 default:
382 /* Allocate extra space for a palette. */
383 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
384 FIELD_OFFSET(BITMAPINFO, bmiColors[1u << (format->byte_count * 8)]));
385 break;
388 if (!b_info)
389 return E_OUTOFMEMORY;
391 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
392 wined3d_texture_get_pitch(texture, surface->texture_level, &row_pitch, &slice_pitch);
393 b_info->bmiHeader.biWidth = row_pitch / format->byte_count;
394 b_info->bmiHeader.biHeight = 0 - surface->resource.height;
395 b_info->bmiHeader.biSizeImage = slice_pitch;
396 b_info->bmiHeader.biPlanes = 1;
397 b_info->bmiHeader.biBitCount = format->byte_count * 8;
399 b_info->bmiHeader.biXPelsPerMeter = 0;
400 b_info->bmiHeader.biYPelsPerMeter = 0;
401 b_info->bmiHeader.biClrUsed = 0;
402 b_info->bmiHeader.biClrImportant = 0;
404 /* Get the bit masks */
405 masks = (DWORD *)b_info->bmiColors;
406 switch (format->id)
408 case WINED3DFMT_B8G8R8_UNORM:
409 b_info->bmiHeader.biCompression = BI_RGB;
410 break;
412 case WINED3DFMT_B5G5R5X1_UNORM:
413 case WINED3DFMT_B5G5R5A1_UNORM:
414 case WINED3DFMT_B4G4R4A4_UNORM:
415 case WINED3DFMT_B4G4R4X4_UNORM:
416 case WINED3DFMT_B2G3R3_UNORM:
417 case WINED3DFMT_B2G3R3A8_UNORM:
418 case WINED3DFMT_R10G10B10A2_UNORM:
419 case WINED3DFMT_R8G8B8A8_UNORM:
420 case WINED3DFMT_R8G8B8X8_UNORM:
421 case WINED3DFMT_B10G10R10A2_UNORM:
422 case WINED3DFMT_B5G6R5_UNORM:
423 case WINED3DFMT_R16G16B16A16_UNORM:
424 b_info->bmiHeader.biCompression = BI_BITFIELDS;
425 get_color_masks(format, masks);
426 break;
428 default:
429 /* Don't know palette */
430 b_info->bmiHeader.biCompression = BI_RGB;
431 break;
434 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
435 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
436 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
437 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
439 if (!surface->dib.DIBsection)
441 ERR("Failed to create DIB section.\n");
442 HeapFree(GetProcessHeap(), 0, b_info);
443 return HRESULT_FROM_WIN32(GetLastError());
446 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
447 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
449 HeapFree(GetProcessHeap(), 0, b_info);
451 /* Now allocate a DC. */
452 surface->hDC = CreateCompatibleDC(0);
453 SelectObject(surface->hDC, surface->dib.DIBsection);
455 surface->flags |= SFLAG_DIBSECTION;
457 return WINED3D_OK;
460 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
461 DWORD location)
463 if (location & WINED3D_LOCATION_BUFFER)
465 data->addr = NULL;
466 data->buffer_object = surface->container->sub_resources[surface_get_sub_resource_idx(surface)].buffer_object;
467 return;
469 if (location & WINED3D_LOCATION_USER_MEMORY)
471 data->addr = surface->container->user_memory;
472 data->buffer_object = 0;
473 return;
475 if (location & WINED3D_LOCATION_DIB)
477 data->addr = surface->dib.bitmap_data;
478 data->buffer_object = 0;
479 return;
481 if (location & WINED3D_LOCATION_SYSMEM)
483 data->addr = surface->resource.heap_memory;
484 data->buffer_object = 0;
485 return;
488 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
489 data->addr = NULL;
490 data->buffer_object = 0;
493 static void surface_prepare_system_memory(struct wined3d_surface *surface)
495 TRACE("surface %p.\n", surface);
497 if (surface->resource.heap_memory)
498 return;
500 /* Whatever surface we have, make sure that there is memory allocated
501 * for the downloaded copy, or a PBO to map. */
502 if (!wined3d_resource_allocate_sysmem(&surface->resource))
503 ERR("Failed to allocate system memory.\n");
505 if (surface->locations & WINED3D_LOCATION_SYSMEM)
506 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
509 static void surface_evict_sysmem(struct wined3d_surface *surface)
511 struct wined3d_texture *texture = surface->container;
513 if (surface->resource.map_count || texture->download_count > MAXLOCKCOUNT
514 || texture->flags & (WINED3D_TEXTURE_CONVERTED | WINED3D_TEXTURE_PIN_SYSMEM))
515 return;
517 wined3d_resource_free_sysmem(&surface->resource);
518 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
521 static HRESULT surface_private_setup(struct wined3d_surface *surface)
523 /* TODO: Check against the maximum texture sizes supported by the video card. */
524 struct wined3d_texture *texture = surface->container;
525 const struct wined3d_gl_info *gl_info = &texture->resource.device->adapter->gl_info;
526 unsigned int pow2Width, pow2Height;
528 TRACE("surface %p.\n", surface);
530 /* Non-power2 support */
531 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
532 || gl_info->supported[ARB_TEXTURE_RECTANGLE])
534 pow2Width = surface->resource.width;
535 pow2Height = surface->resource.height;
537 else
539 /* Find the nearest pow2 match */
540 pow2Width = pow2Height = 1;
541 while (pow2Width < surface->resource.width)
542 pow2Width <<= 1;
543 while (pow2Height < surface->resource.height)
544 pow2Height <<= 1;
546 surface->pow2Width = pow2Width;
547 surface->pow2Height = pow2Height;
549 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
551 /* TODO: Add support for non power two compressed textures. */
552 if (texture->resource.format_flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE))
554 FIXME("(%p) Compressed or height scaled non-power-two textures are not supported w(%d) h(%d)\n",
555 surface, surface->resource.width, surface->resource.height);
556 return WINED3DERR_NOTAVAILABLE;
560 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
561 && !(texture->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
563 /* One of three options:
564 * 1: Do the same as we do with NPOT and scale the texture, (any
565 * texture ops would require the texture to be scaled which is
566 * potentially slow)
567 * 2: Set the texture to the maximum size (bad idea).
568 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
569 * 4: Create the surface, but allow it to be used only for DirectDraw
570 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
571 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
572 * the render target. */
573 if (texture->resource.pool == WINED3D_POOL_DEFAULT || texture->resource.pool == WINED3D_POOL_MANAGED)
575 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
576 return WINED3DERR_NOTAVAILABLE;
579 /* We should never use this surface in combination with OpenGL! */
580 TRACE("Creating an oversized surface: %ux%u.\n",
581 surface->pow2Width, surface->pow2Height);
584 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
585 surface->locations = WINED3D_LOCATION_DISCARDED;
587 if (wined3d_texture_use_pbo(texture, gl_info))
588 surface->resource.map_binding = WINED3D_LOCATION_BUFFER;
590 return WINED3D_OK;
593 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
595 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
596 return FALSE;
597 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
598 return FALSE;
599 return TRUE;
602 static void surface_depth_blt_fbo(const struct wined3d_device *device,
603 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
604 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
606 const struct wined3d_gl_info *gl_info;
607 struct wined3d_context *context;
608 DWORD src_mask, dst_mask;
609 GLbitfield gl_mask;
611 TRACE("device %p\n", device);
612 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
613 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
614 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
615 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
617 src_mask = src_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
618 dst_mask = dst_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
620 if (src_mask != dst_mask)
622 ERR("Incompatible formats %s and %s.\n",
623 debug_d3dformat(src_surface->container->resource.format->id),
624 debug_d3dformat(dst_surface->container->resource.format->id));
625 return;
628 if (!src_mask)
630 ERR("Not a depth / stencil format: %s.\n",
631 debug_d3dformat(src_surface->container->resource.format->id));
632 return;
635 gl_mask = 0;
636 if (src_mask & WINED3DFMT_FLAG_DEPTH)
637 gl_mask |= GL_DEPTH_BUFFER_BIT;
638 if (src_mask & WINED3DFMT_FLAG_STENCIL)
639 gl_mask |= GL_STENCIL_BUFFER_BIT;
641 context = context_acquire(device, NULL);
642 if (!context->valid)
644 context_release(context);
645 WARN("Invalid context, skipping blit.\n");
646 return;
649 /* Make sure the locations are up-to-date. Loading the destination
650 * surface isn't required if the entire surface is overwritten. */
651 surface_load_location(src_surface, context, src_location);
652 if (!surface_is_full_rect(dst_surface, dst_rect))
653 surface_load_location(dst_surface, context, dst_location);
654 else
655 wined3d_surface_prepare(dst_surface, context, dst_location);
657 gl_info = context->gl_info;
659 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
660 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
662 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
663 context_set_draw_buffer(context, GL_NONE);
664 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
665 context_invalidate_state(context, STATE_FRAMEBUFFER);
667 if (gl_mask & GL_DEPTH_BUFFER_BIT)
669 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
670 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
672 if (gl_mask & GL_STENCIL_BUFFER_BIT)
674 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
676 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
677 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
679 gl_info->gl_ops.gl.p_glStencilMask(~0U);
680 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
683 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
684 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
686 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
687 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
688 checkGLcall("glBlitFramebuffer()");
690 if (wined3d_settings.strict_draw_ordering)
691 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
693 context_release(context);
696 /* Blit between surface locations. Onscreen on different swapchains is not supported.
697 * Depth / stencil is not supported. Context activation is done by the caller. */
698 static void surface_blt_fbo(const struct wined3d_device *device,
699 struct wined3d_context *old_ctx, enum wined3d_texture_filter_type filter,
700 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
701 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
703 const struct wined3d_gl_info *gl_info;
704 struct wined3d_context *context = old_ctx;
705 struct wined3d_surface *required_rt, *restore_rt = NULL;
706 RECT src_rect, dst_rect;
707 GLenum gl_filter;
708 GLenum buffer;
710 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
711 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
712 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
713 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
714 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
716 src_rect = *src_rect_in;
717 dst_rect = *dst_rect_in;
719 switch (filter)
721 case WINED3D_TEXF_LINEAR:
722 gl_filter = GL_LINEAR;
723 break;
725 default:
726 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
727 case WINED3D_TEXF_NONE:
728 case WINED3D_TEXF_POINT:
729 gl_filter = GL_NEAREST;
730 break;
733 /* Resolve the source surface first if needed. */
734 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
735 && (src_surface->container->resource.format->id != dst_surface->container->resource.format->id
736 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
737 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
738 src_location = WINED3D_LOCATION_RB_RESOLVED;
740 /* Make sure the locations are up-to-date. Loading the destination
741 * surface isn't required if the entire surface is overwritten. (And is
742 * in fact harmful if we're being called by surface_load_location() with
743 * the purpose of loading the destination surface.) */
744 surface_load_location(src_surface, old_ctx, src_location);
745 if (!surface_is_full_rect(dst_surface, &dst_rect))
746 surface_load_location(dst_surface, old_ctx, dst_location);
747 else
748 wined3d_surface_prepare(dst_surface, old_ctx, dst_location);
751 if (src_location == WINED3D_LOCATION_DRAWABLE) required_rt = src_surface;
752 else if (dst_location == WINED3D_LOCATION_DRAWABLE) required_rt = dst_surface;
753 else required_rt = NULL;
755 if (required_rt && required_rt != old_ctx->current_rt)
757 restore_rt = old_ctx->current_rt;
758 context = context_acquire(device, required_rt);
761 if (!context->valid)
763 context_release(context);
764 WARN("Invalid context, skipping blit.\n");
765 return;
768 gl_info = context->gl_info;
770 if (src_location == WINED3D_LOCATION_DRAWABLE)
772 TRACE("Source surface %p is onscreen.\n", src_surface);
773 buffer = surface_get_gl_buffer(src_surface);
774 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
776 else
778 TRACE("Source surface %p is offscreen.\n", src_surface);
779 buffer = GL_COLOR_ATTACHMENT0;
782 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
783 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
784 checkGLcall("glReadBuffer()");
785 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
787 if (dst_location == WINED3D_LOCATION_DRAWABLE)
789 TRACE("Destination surface %p is onscreen.\n", dst_surface);
790 buffer = surface_get_gl_buffer(dst_surface);
791 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
793 else
795 TRACE("Destination surface %p is offscreen.\n", dst_surface);
796 buffer = GL_COLOR_ATTACHMENT0;
799 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
800 context_set_draw_buffer(context, buffer);
801 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
802 context_invalidate_state(context, STATE_FRAMEBUFFER);
804 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
805 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
806 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
807 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
808 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
810 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
811 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
813 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
814 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
815 checkGLcall("glBlitFramebuffer()");
817 if (wined3d_settings.strict_draw_ordering
818 || (dst_location == WINED3D_LOCATION_DRAWABLE
819 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
820 gl_info->gl_ops.gl.p_glFlush();
822 if (restore_rt)
823 context_restore(context, restore_rt);
826 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
827 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
828 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
830 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
831 return FALSE;
833 /* Source and/or destination need to be on the GL side */
834 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
835 return FALSE;
837 switch (blit_op)
839 case WINED3D_BLIT_OP_COLOR_BLIT:
840 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
841 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
842 return FALSE;
843 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
844 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
845 return FALSE;
846 if (!(src_format->id == dst_format->id
847 || (is_identity_fixup(src_format->color_fixup)
848 && is_identity_fixup(dst_format->color_fixup))))
849 return FALSE;
850 break;
852 case WINED3D_BLIT_OP_DEPTH_BLIT:
853 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
854 return FALSE;
855 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
856 return FALSE;
857 /* Accept pure swizzle fixups for depth formats. In general we
858 * ignore the stencil component (if present) at the moment and the
859 * swizzle is not relevant with just the depth component. */
860 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
861 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
862 return FALSE;
863 break;
865 default:
866 return FALSE;
869 return TRUE;
872 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
874 const struct wined3d_format *format = surface->container->resource.format;
876 switch (format->id)
878 case WINED3DFMT_S1_UINT_D15_UNORM:
879 *float_depth = depth / (float)0x00007fff;
880 break;
882 case WINED3DFMT_D16_UNORM:
883 *float_depth = depth / (float)0x0000ffff;
884 break;
886 case WINED3DFMT_D24_UNORM_S8_UINT:
887 case WINED3DFMT_X8D24_UNORM:
888 *float_depth = depth / (float)0x00ffffff;
889 break;
891 case WINED3DFMT_D32_UNORM:
892 *float_depth = depth / (float)0xffffffff;
893 break;
895 default:
896 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
897 return FALSE;
900 return TRUE;
903 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
905 struct wined3d_resource *resource = &surface->container->resource;
906 struct wined3d_device *device = resource->device;
907 struct wined3d_rendertarget_view_desc view_desc;
908 struct wined3d_rendertarget_view *view;
909 const struct blit_shader *blitter;
910 HRESULT hr;
912 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
913 WINED3D_BLIT_OP_DEPTH_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
915 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
916 return WINED3DERR_INVALIDCALL;
919 view_desc.format_id = resource->format->id;
920 view_desc.u.texture.level_idx = surface->texture_level;
921 view_desc.u.texture.layer_idx = surface->texture_layer;
922 view_desc.u.texture.layer_count = 1;
923 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
924 resource, NULL, &wined3d_null_parent_ops, &view)))
926 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
927 return hr;
930 hr = blitter->depth_fill(device, view, rect, WINED3DCLEAR_ZBUFFER, depth, 0);
931 wined3d_rendertarget_view_decref(view);
933 return hr;
936 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
937 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
939 struct wined3d_texture *src_texture = src_surface->container;
940 struct wined3d_texture *dst_texture = dst_surface->container;
941 struct wined3d_device *device = src_texture->resource.device;
943 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
944 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
945 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format))
946 return WINED3DERR_INVALIDCALL;
948 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
950 surface_modify_ds_location(dst_surface, dst_location,
951 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
953 return WINED3D_OK;
956 static ULONG surface_resource_incref(struct wined3d_resource *resource)
958 struct wined3d_surface *surface = surface_from_resource(resource);
960 TRACE("surface %p, container %p.\n", surface, surface->container);
962 return wined3d_texture_incref(surface->container);
965 static ULONG surface_resource_decref(struct wined3d_resource *resource)
967 struct wined3d_surface *surface = surface_from_resource(resource);
969 TRACE("surface %p, container %p.\n", surface, surface->container);
971 return wined3d_texture_decref(surface->container);
974 static void surface_unload(struct wined3d_resource *resource)
976 struct wined3d_surface *surface = surface_from_resource(resource);
977 struct wined3d_renderbuffer_entry *entry, *entry2;
978 struct wined3d_device *device = resource->device;
979 const struct wined3d_gl_info *gl_info;
980 struct wined3d_context *context;
982 TRACE("surface %p.\n", surface);
984 context = context_acquire(device, NULL);
985 gl_info = context->gl_info;
987 if (resource->pool == WINED3D_POOL_DEFAULT)
989 /* Default pool resources are supposed to be destroyed before Reset is called.
990 * Implicit resources stay however. So this means we have an implicit render target
991 * or depth stencil. The content may be destroyed, but we still have to tear down
992 * opengl resources, so we cannot leave early.
994 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
995 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
996 * or the depth stencil into an FBO the texture or render buffer will be removed
997 * and all flags get lost */
998 if (resource->usage & WINED3DUSAGE_DEPTHSTENCIL)
1000 surface_validate_location(surface, WINED3D_LOCATION_DISCARDED);
1001 surface_invalidate_location(surface, ~WINED3D_LOCATION_DISCARDED);
1003 else
1005 surface_prepare_system_memory(surface);
1006 memset(surface->resource.heap_memory, 0, surface->resource.size);
1007 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1008 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1011 else
1013 surface_load_location(surface, context, surface->resource.map_binding);
1014 surface_invalidate_location(surface, ~surface->resource.map_binding);
1017 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1018 * all application-created targets the application has to release the surface
1019 * before calling _Reset
1021 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1023 context_gl_resource_released(device, entry->id, TRUE);
1024 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1025 list_remove(&entry->entry);
1026 HeapFree(GetProcessHeap(), 0, entry);
1028 list_init(&surface->renderbuffers);
1029 surface->current_renderbuffer = NULL;
1031 if (surface->rb_multisample)
1033 context_gl_resource_released(device, surface->rb_multisample, TRUE);
1034 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1035 surface->rb_multisample = 0;
1037 if (surface->rb_resolved)
1039 context_gl_resource_released(device, surface->rb_resolved, TRUE);
1040 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1041 surface->rb_resolved = 0;
1044 context_release(context);
1046 resource_unload(resource);
1049 static HRESULT surface_resource_sub_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx,
1050 struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
1052 ERR("Not supported on sub-resources.\n");
1053 return WINED3DERR_INVALIDCALL;
1056 static HRESULT surface_resource_sub_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx)
1058 ERR("Not supported on sub-resources.\n");
1059 return WINED3DERR_INVALIDCALL;
1062 static const struct wined3d_resource_ops surface_resource_ops =
1064 surface_resource_incref,
1065 surface_resource_decref,
1066 surface_unload,
1067 surface_resource_sub_resource_map,
1068 surface_resource_sub_resource_unmap,
1071 static const struct wined3d_surface_ops surface_ops =
1073 surface_private_setup,
1076 /*****************************************************************************
1077 * Initializes the GDI surface, aka creates the DIB section we render to
1078 * The DIB section creation is done by calling GetDC, which will create the
1079 * section and releasing the dc to allow the app to use it. The dib section
1080 * will stay until the surface is released
1082 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1083 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1084 * avoid confusion in the shared surface code.
1086 * Returns:
1087 * WINED3D_OK on success
1088 * The return values of called methods on failure
1090 *****************************************************************************/
1091 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1093 HRESULT hr;
1095 TRACE("surface %p.\n", surface);
1097 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1099 ERR("Overlays not yet supported by GDI surfaces.\n");
1100 return WINED3DERR_INVALIDCALL;
1103 /* Sysmem textures have memory already allocated - release it,
1104 * this avoids an unnecessary memcpy. */
1105 hr = surface_create_dib_section(surface);
1106 if (FAILED(hr))
1107 return hr;
1108 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1110 /* We don't mind the nonpow2 stuff in GDI. */
1111 surface->pow2Width = surface->resource.width;
1112 surface->pow2Height = surface->resource.height;
1114 return WINED3D_OK;
1117 static const struct wined3d_surface_ops gdi_surface_ops =
1119 gdi_surface_private_setup,
1122 /* This call just downloads data, the caller is responsible for binding the
1123 * correct texture. */
1124 /* Context activation is done by the caller. */
1125 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1126 DWORD dst_location)
1128 struct wined3d_texture *texture = surface->container;
1129 const struct wined3d_format *format = texture->resource.format;
1130 struct wined3d_bo_address data;
1132 /* Only support read back of converted P8 surfaces. */
1133 if (texture->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1135 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1136 return;
1139 surface_get_memory(surface, &data, dst_location);
1141 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
1143 TRACE("(%p) : Calling glGetCompressedTexImage level %d, format %#x, type %#x, data %p.\n",
1144 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1146 if (data.buffer_object)
1148 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1149 checkGLcall("glBindBuffer");
1150 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, NULL));
1151 checkGLcall("glGetCompressedTexImage");
1152 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1153 checkGLcall("glBindBuffer");
1155 else
1157 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target,
1158 surface->texture_level, data.addr));
1159 checkGLcall("glGetCompressedTexImage");
1162 else
1164 unsigned int dst_row_pitch, dst_slice_pitch;
1165 unsigned int src_row_pitch, src_slice_pitch;
1166 GLenum gl_format = format->glFormat;
1167 GLenum gl_type = format->glType;
1168 void *mem;
1170 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
1172 wined3d_texture_get_pitch(texture, surface->texture_level, &dst_row_pitch, &dst_slice_pitch);
1173 wined3d_format_calculate_pitch(format, texture->resource.device->surface_alignment,
1174 surface->pow2Width, surface->pow2Height, &src_row_pitch, &src_slice_pitch);
1175 mem = HeapAlloc(GetProcessHeap(), 0, src_slice_pitch);
1177 else
1179 mem = data.addr;
1182 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1183 surface, surface->texture_level, gl_format, gl_type, mem);
1185 if (data.buffer_object)
1187 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1188 checkGLcall("glBindBuffer");
1190 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1191 gl_format, gl_type, NULL);
1192 checkGLcall("glGetTexImage");
1194 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1195 checkGLcall("glBindBuffer");
1197 else
1199 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1200 gl_format, gl_type, mem);
1201 checkGLcall("glGetTexImage");
1204 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
1206 const BYTE *src_data;
1207 BYTE *dst_data;
1208 UINT y;
1210 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1211 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1212 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1214 * We're doing this...
1216 * instead of boxing the texture :
1217 * |<-texture width ->| -->pow2width| /\
1218 * |111111111111111111| | |
1219 * |222 Texture 222222| boxed empty | texture height
1220 * |3333 Data 33333333| | |
1221 * |444444444444444444| | \/
1222 * ----------------------------------- |
1223 * | boxed empty | boxed empty | pow2height
1224 * | | | \/
1225 * -----------------------------------
1228 * we're repacking the data to the expected texture width
1230 * |<-texture width ->| -->pow2width| /\
1231 * |111111111111111111222222222222222| |
1232 * |222333333333333333333444444444444| texture height
1233 * |444444 | |
1234 * | | \/
1235 * | | |
1236 * | empty | pow2height
1237 * | | \/
1238 * -----------------------------------
1240 * == is the same as
1242 * |<-texture width ->| /\
1243 * |111111111111111111|
1244 * |222222222222222222|texture height
1245 * |333333333333333333|
1246 * |444444444444444444| \/
1247 * --------------------
1249 * This also means that any references to surface memory should work with the data as if it were a
1250 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1252 * internally the texture is still stored in a boxed format so any references to textureName will
1253 * get a boxed texture with width pow2width and not a texture of width resource.width. */
1254 src_data = mem;
1255 dst_data = data.addr;
1256 TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
1257 for (y = 0; y < surface->resource.height; ++y)
1259 memcpy(dst_data, src_data, dst_row_pitch);
1260 src_data += src_row_pitch;
1261 dst_data += dst_row_pitch;
1264 HeapFree(GetProcessHeap(), 0, mem);
1269 /* This call just uploads data, the caller is responsible for binding the
1270 * correct texture. */
1271 /* Context activation is done by the caller. */
1272 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1273 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1274 BOOL srgb, const struct wined3d_const_bo_address *data)
1276 struct wined3d_texture *texture = surface->container;
1277 UINT update_w = src_rect->right - src_rect->left;
1278 UINT update_h = src_rect->bottom - src_rect->top;
1280 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1281 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1282 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1284 if (surface->resource.map_count)
1286 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
1287 texture->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
1290 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
1292 update_h *= format->height_scale.numerator;
1293 update_h /= format->height_scale.denominator;
1296 if (data->buffer_object)
1298 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
1299 checkGLcall("glBindBuffer");
1302 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
1304 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1305 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1306 const BYTE *addr = data->addr;
1307 GLenum internal;
1309 addr += (src_rect->top / format->block_height) * src_pitch;
1310 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1312 if (srgb)
1313 internal = format->glGammaInternal;
1314 else if (texture->resource.usage & WINED3DUSAGE_RENDERTARGET
1315 && wined3d_resource_is_offscreen(&texture->resource))
1316 internal = format->rtInternal;
1317 else
1318 internal = format->glInternal;
1320 TRACE("glCompressedTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, "
1321 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1322 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1324 if (row_length == src_pitch)
1326 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1327 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1329 else
1331 UINT row, y;
1333 /* glCompressedTexSubImage2D() ignores pixel store state, so we
1334 * can't use the unpack row length like for glTexSubImage2D. */
1335 for (row = 0, y = dst_point->y; row < row_count; ++row)
1337 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1338 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1339 y += format->block_height;
1340 addr += src_pitch;
1343 checkGLcall("glCompressedTexSubImage2D");
1345 else
1347 const BYTE *addr = data->addr;
1349 addr += src_rect->top * src_pitch;
1350 addr += src_rect->left * format->byte_count;
1352 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1353 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1354 update_w, update_h, format->glFormat, format->glType, addr);
1356 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1357 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1358 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1359 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1360 checkGLcall("glTexSubImage2D");
1363 if (data->buffer_object)
1365 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1366 checkGLcall("glBindBuffer");
1369 if (wined3d_settings.strict_draw_ordering)
1370 gl_info->gl_ops.gl.p_glFlush();
1372 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1374 struct wined3d_device *device = texture->resource.device;
1375 unsigned int i;
1377 for (i = 0; i < device->context_count; ++i)
1379 context_surface_update(device->contexts[i], surface);
1384 static BOOL surface_check_block_align_rect(struct wined3d_surface *surface, const RECT *rect)
1386 struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
1388 return wined3d_texture_check_block_align(surface->container, surface->texture_level, &box);
1391 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1392 struct wined3d_surface *src_surface, const RECT *src_rect)
1394 struct wined3d_texture *src_texture = src_surface->container;
1395 struct wined3d_texture *dst_texture = dst_surface->container;
1396 unsigned int src_row_pitch, src_slice_pitch;
1397 const struct wined3d_format *src_format;
1398 const struct wined3d_format *dst_format;
1399 unsigned int src_fmt_flags, dst_fmt_flags;
1400 const struct wined3d_gl_info *gl_info;
1401 struct wined3d_context *context;
1402 struct wined3d_bo_address data;
1403 UINT update_w, update_h;
1404 UINT dst_w, dst_h;
1405 RECT r, dst_rect;
1406 POINT p;
1408 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1409 dst_surface, wine_dbgstr_point(dst_point),
1410 src_surface, wine_dbgstr_rect(src_rect));
1412 src_format = src_texture->resource.format;
1413 dst_format = dst_texture->resource.format;
1414 src_fmt_flags = src_texture->resource.format_flags;
1415 dst_fmt_flags = dst_texture->resource.format_flags;
1417 if (src_format->id != dst_format->id)
1419 WARN("Source and destination surfaces should have the same format.\n");
1420 return WINED3DERR_INVALIDCALL;
1423 if (!dst_point)
1425 p.x = 0;
1426 p.y = 0;
1427 dst_point = &p;
1429 else if (dst_point->x < 0 || dst_point->y < 0)
1431 WARN("Invalid destination point.\n");
1432 return WINED3DERR_INVALIDCALL;
1435 if (!src_rect)
1437 SetRect(&r, 0, 0, src_surface->resource.width, src_surface->resource.height);
1438 src_rect = &r;
1440 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1441 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1443 WARN("Invalid source rectangle.\n");
1444 return WINED3DERR_INVALIDCALL;
1447 dst_w = dst_surface->resource.width;
1448 dst_h = dst_surface->resource.height;
1450 update_w = src_rect->right - src_rect->left;
1451 update_h = src_rect->bottom - src_rect->top;
1453 if (update_w > dst_w || dst_point->x > dst_w - update_w
1454 || update_h > dst_h || dst_point->y > dst_h - update_h)
1456 WARN("Destination out of bounds.\n");
1457 return WINED3DERR_INVALIDCALL;
1460 if ((src_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(src_surface, src_rect))
1462 WARN("Source rectangle not block-aligned.\n");
1463 return WINED3DERR_INVALIDCALL;
1466 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1467 if ((dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(dst_surface, &dst_rect))
1469 WARN("Destination rectangle not block-aligned.\n");
1470 return WINED3DERR_INVALIDCALL;
1473 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1474 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_texture, FALSE))
1475 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1477 context = context_acquire(dst_texture->resource.device, NULL);
1478 gl_info = context->gl_info;
1480 /* Only load the surface for partial updates. For newly allocated texture
1481 * the texture wouldn't be the current location, and we'd upload zeroes
1482 * just to overwrite them again. */
1483 if (update_w == dst_w && update_h == dst_h)
1484 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1485 else
1486 surface_load_location(dst_surface, context, WINED3D_LOCATION_TEXTURE_RGB);
1487 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1489 surface_get_memory(src_surface, &data, src_surface->locations);
1490 wined3d_texture_get_pitch(src_texture, src_surface->texture_level, &src_row_pitch, &src_slice_pitch);
1492 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1493 src_row_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1495 context_release(context);
1497 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1498 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1500 return WINED3D_OK;
1503 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1504 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1505 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1506 /* Context activation is done by the caller. */
1507 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1509 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1510 struct wined3d_renderbuffer_entry *entry;
1511 GLuint renderbuffer = 0;
1512 unsigned int src_width, src_height;
1513 unsigned int width, height;
1515 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1517 width = rt->pow2Width;
1518 height = rt->pow2Height;
1520 else
1522 width = surface->pow2Width;
1523 height = surface->pow2Height;
1526 src_width = surface->pow2Width;
1527 src_height = surface->pow2Height;
1529 /* A depth stencil smaller than the render target is not valid */
1530 if (width > src_width || height > src_height) return;
1532 /* Remove any renderbuffer set if the sizes match */
1533 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1534 || (width == src_width && height == src_height))
1536 surface->current_renderbuffer = NULL;
1537 return;
1540 /* Look if we've already got a renderbuffer of the correct dimensions */
1541 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1543 if (entry->width == width && entry->height == height)
1545 renderbuffer = entry->id;
1546 surface->current_renderbuffer = entry;
1547 break;
1551 if (!renderbuffer)
1553 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1554 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1555 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1556 surface->resource.format->glInternal, width, height);
1558 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1559 entry->width = width;
1560 entry->height = height;
1561 entry->id = renderbuffer;
1562 list_add_head(&surface->renderbuffers, &entry->entry);
1564 surface->current_renderbuffer = entry;
1567 checkGLcall("set_compatible_renderbuffer");
1570 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1572 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1574 TRACE("surface %p.\n", surface);
1576 if (!swapchain)
1578 ERR("Surface %p is not on a swapchain.\n", surface);
1579 return GL_NONE;
1582 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
1584 if (swapchain->render_to_fbo)
1586 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1587 return GL_COLOR_ATTACHMENT0;
1589 TRACE("Returning GL_BACK\n");
1590 return GL_BACK;
1592 else if (surface->container == swapchain->front_buffer)
1594 TRACE("Returning GL_FRONT\n");
1595 return GL_FRONT;
1598 FIXME("Higher back buffer, returning GL_BACK\n");
1599 return GL_BACK;
1602 /* Context activation is done by the caller. */
1603 void surface_load(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
1605 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1607 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1609 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
1610 ERR("Not supported on scratch surfaces.\n");
1612 if (surface->locations & location)
1614 TRACE("surface is already in texture\n");
1615 return;
1617 TRACE("Reloading because surface is dirty.\n");
1619 surface_load_location(surface, context, location);
1620 surface_evict_sysmem(surface);
1623 /* See also float_16_to_32() in wined3d_private.h */
1624 static inline unsigned short float_32_to_16(const float *in)
1626 int exp = 0;
1627 float tmp = fabsf(*in);
1628 unsigned int mantissa;
1629 unsigned short ret;
1631 /* Deal with special numbers */
1632 if (*in == 0.0f)
1633 return 0x0000;
1634 if (isnan(*in))
1635 return 0x7c01;
1636 if (isinf(*in))
1637 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1639 if (tmp < (float)(1u << 10))
1643 tmp = tmp * 2.0f;
1644 exp--;
1645 } while (tmp < (float)(1u << 10));
1647 else if (tmp >= (float)(1u << 11))
1651 tmp /= 2.0f;
1652 exp++;
1653 } while (tmp >= (float)(1u << 11));
1656 mantissa = (unsigned int)tmp;
1657 if (tmp - mantissa >= 0.5f)
1658 ++mantissa; /* Round to nearest, away from zero. */
1660 exp += 10; /* Normalize the mantissa. */
1661 exp += 15; /* Exponent is encoded with excess 15. */
1663 if (exp > 30) /* too big */
1665 ret = 0x7c00; /* INF */
1667 else if (exp <= 0)
1669 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1670 while (exp <= 0)
1672 mantissa = mantissa >> 1;
1673 ++exp;
1675 ret = mantissa & 0x3ff;
1677 else
1679 ret = (exp << 10) | (mantissa & 0x3ff);
1682 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1683 return ret;
1686 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
1687 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1689 unsigned short *dst_s;
1690 const float *src_f;
1691 unsigned int x, y;
1693 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1695 for (y = 0; y < h; ++y)
1697 src_f = (const float *)(src + y * pitch_in);
1698 dst_s = (unsigned short *) (dst + y * pitch_out);
1699 for (x = 0; x < w; ++x)
1701 dst_s[x] = float_32_to_16(src_f + x);
1706 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
1707 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1709 static const unsigned char convert_5to8[] =
1711 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
1712 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
1713 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
1714 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
1716 static const unsigned char convert_6to8[] =
1718 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
1719 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
1720 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
1721 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
1722 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
1723 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
1724 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
1725 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
1727 unsigned int x, y;
1729 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1731 for (y = 0; y < h; ++y)
1733 const WORD *src_line = (const WORD *)(src + y * pitch_in);
1734 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1735 for (x = 0; x < w; ++x)
1737 WORD pixel = src_line[x];
1738 dst_line[x] = 0xff000000u
1739 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
1740 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
1741 | convert_5to8[(pixel & 0x001fu)];
1746 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
1747 * in both cases we're just setting the X / Alpha channel to 0xff. */
1748 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
1749 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1751 unsigned int x, y;
1753 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1755 for (y = 0; y < h; ++y)
1757 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
1758 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1760 for (x = 0; x < w; ++x)
1762 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
1767 static inline BYTE cliptobyte(int x)
1769 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
1772 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
1773 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1775 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1776 unsigned int x, y;
1778 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1780 for (y = 0; y < h; ++y)
1782 const BYTE *src_line = src + y * pitch_in;
1783 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1784 for (x = 0; x < w; ++x)
1786 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1787 * C = Y - 16; D = U - 128; E = V - 128;
1788 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1789 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1790 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1791 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1792 * U and V are shared between the pixels. */
1793 if (!(x & 1)) /* For every even pixel, read new U and V. */
1795 d = (int) src_line[1] - 128;
1796 e = (int) src_line[3] - 128;
1797 r2 = 409 * e + 128;
1798 g2 = - 100 * d - 208 * e + 128;
1799 b2 = 516 * d + 128;
1801 c2 = 298 * ((int) src_line[0] - 16);
1802 dst_line[x] = 0xff000000
1803 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
1804 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
1805 | cliptobyte((c2 + b2) >> 8); /* blue */
1806 /* Scale RGB values to 0..255 range,
1807 * then clip them if still not in range (may be negative),
1808 * then shift them within DWORD if necessary. */
1809 src_line += 2;
1814 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
1815 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1817 unsigned int x, y;
1818 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1820 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
1822 for (y = 0; y < h; ++y)
1824 const BYTE *src_line = src + y * pitch_in;
1825 WORD *dst_line = (WORD *)(dst + y * pitch_out);
1826 for (x = 0; x < w; ++x)
1828 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1829 * C = Y - 16; D = U - 128; E = V - 128;
1830 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1831 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1832 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1833 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1834 * U and V are shared between the pixels. */
1835 if (!(x & 1)) /* For every even pixel, read new U and V. */
1837 d = (int) src_line[1] - 128;
1838 e = (int) src_line[3] - 128;
1839 r2 = 409 * e + 128;
1840 g2 = - 100 * d - 208 * e + 128;
1841 b2 = 516 * d + 128;
1843 c2 = 298 * ((int) src_line[0] - 16);
1844 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
1845 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
1846 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
1847 /* Scale RGB values to 0..255 range,
1848 * then clip them if still not in range (may be negative),
1849 * then shift them within DWORD if necessary. */
1850 src_line += 2;
1855 struct d3dfmt_converter_desc
1857 enum wined3d_format_id from, to;
1858 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
1861 static const struct d3dfmt_converter_desc converters[] =
1863 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
1864 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
1865 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1866 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1867 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
1868 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
1871 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
1872 enum wined3d_format_id to)
1874 unsigned int i;
1876 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
1878 if (converters[i].from == from && converters[i].to == to)
1879 return &converters[i];
1882 return NULL;
1885 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
1886 unsigned int sub_resource_idx, enum wined3d_format_id format)
1888 struct wined3d_map_desc src_map, dst_map;
1889 const struct d3dfmt_converter_desc *conv;
1890 struct wined3d_texture *dst_texture;
1891 struct wined3d_resource_desc desc;
1893 if (!(conv = find_converter(src_texture->resource.format->id, format)))
1895 FIXME("Cannot find a conversion function from format %s to %s.\n",
1896 debug_d3dformat(src_texture->resource.format->id), debug_d3dformat(format));
1897 return NULL;
1900 /* FIXME: Multisampled conversion? */
1901 wined3d_resource_get_desc(src_texture->sub_resources[sub_resource_idx].resource, &desc);
1902 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1903 desc.format = format;
1904 desc.usage = 0;
1905 desc.pool = WINED3D_POOL_SCRATCH;
1906 if (FAILED(wined3d_texture_create(src_texture->resource.device, &desc, 1,
1907 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
1908 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
1910 ERR("Failed to create a destination texture for conversion.\n");
1911 return NULL;
1914 memset(&src_map, 0, sizeof(src_map));
1915 memset(&dst_map, 0, sizeof(dst_map));
1917 if (FAILED(wined3d_resource_map(&src_texture->resource, sub_resource_idx,
1918 &src_map, NULL, WINED3D_MAP_READONLY)))
1920 ERR("Failed to map the source texture.\n");
1921 wined3d_texture_decref(dst_texture);
1922 return NULL;
1924 if (FAILED(wined3d_resource_map(&dst_texture->resource, 0, &dst_map, NULL, 0)))
1926 ERR("Failed to map the destination texture.\n");
1927 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1928 wined3d_texture_decref(dst_texture);
1929 return NULL;
1932 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch, desc.width, desc.height);
1934 wined3d_resource_unmap(&dst_texture->resource, 0);
1935 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1937 return dst_texture;
1940 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
1941 unsigned int bpp, UINT pitch, DWORD color)
1943 BYTE *first;
1944 unsigned int x, y;
1946 /* Do first row */
1948 #define COLORFILL_ROW(type) \
1949 do { \
1950 type *d = (type *)buf; \
1951 for (x = 0; x < width; ++x) \
1952 d[x] = (type)color; \
1953 } while(0)
1955 switch (bpp)
1957 case 1:
1958 COLORFILL_ROW(BYTE);
1959 break;
1961 case 2:
1962 COLORFILL_ROW(WORD);
1963 break;
1965 case 3:
1967 BYTE *d = buf;
1968 for (x = 0; x < width; ++x, d += 3)
1970 d[0] = (color ) & 0xff;
1971 d[1] = (color >> 8) & 0xff;
1972 d[2] = (color >> 16) & 0xff;
1974 break;
1976 case 4:
1977 COLORFILL_ROW(DWORD);
1978 break;
1980 default:
1981 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
1982 return WINED3DERR_NOTAVAILABLE;
1985 #undef COLORFILL_ROW
1987 /* Now copy first row. */
1988 first = buf;
1989 for (y = 1; y < height; ++y)
1991 buf += pitch;
1992 memcpy(buf, first, width * bpp);
1995 return WINED3D_OK;
1998 static void read_from_framebuffer(struct wined3d_surface *surface,
1999 struct wined3d_context *old_ctx, DWORD dst_location)
2001 struct wined3d_device *device = surface->resource.device;
2002 const struct wined3d_gl_info *gl_info;
2003 struct wined3d_context *context = old_ctx;
2004 struct wined3d_surface *restore_rt = NULL;
2005 unsigned int row_pitch, slice_pitch;
2006 BYTE *mem;
2007 BYTE *row, *top, *bottom;
2008 int i;
2009 BOOL srcIsUpsideDown;
2010 struct wined3d_bo_address data;
2012 surface_get_memory(surface, &data, dst_location);
2014 if (surface != old_ctx->current_rt)
2016 restore_rt = old_ctx->current_rt;
2017 context = context_acquire(device, surface);
2020 context_apply_blit_state(context, device);
2021 gl_info = context->gl_info;
2023 /* Select the correct read buffer, and give some debug output.
2024 * There is no need to keep track of the current read buffer or reset it, every part of the code
2025 * that reads sets the read buffer as desired.
2027 if (wined3d_resource_is_offscreen(&surface->container->resource))
2029 /* Mapping the primary render target which is not on a swapchain.
2030 * Read from the back buffer. */
2031 TRACE("Mapping offscreen render target.\n");
2032 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
2033 srcIsUpsideDown = TRUE;
2035 else
2037 /* Onscreen surfaces are always part of a swapchain */
2038 GLenum buffer = surface_get_gl_buffer(surface);
2039 TRACE("Mapping %#x buffer.\n", buffer);
2040 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2041 checkGLcall("glReadBuffer");
2042 srcIsUpsideDown = FALSE;
2045 if (data.buffer_object)
2047 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
2048 checkGLcall("glBindBuffer");
2051 wined3d_texture_get_pitch(surface->container, surface->texture_level, &row_pitch, &slice_pitch);
2053 /* Setup pixel store pack state -- to glReadPixels into the correct place */
2054 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / surface->resource.format->byte_count);
2055 checkGLcall("glPixelStorei");
2057 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
2058 surface->resource.width, surface->resource.height,
2059 surface->resource.format->glFormat,
2060 surface->resource.format->glType, data.addr);
2061 checkGLcall("glReadPixels");
2063 /* Reset previous pixel store pack state */
2064 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
2065 checkGLcall("glPixelStorei");
2067 if (!srcIsUpsideDown)
2069 /* glReadPixels returns the image upside down, and there is no way to
2070 * prevent this. Flip the lines in software. */
2072 if (!(row = HeapAlloc(GetProcessHeap(), 0, row_pitch)))
2073 goto error;
2075 if (data.buffer_object)
2077 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
2078 checkGLcall("glMapBuffer");
2080 else
2081 mem = data.addr;
2083 top = mem;
2084 bottom = mem + row_pitch * (surface->resource.height - 1);
2085 for (i = 0; i < surface->resource.height / 2; i++)
2087 memcpy(row, top, row_pitch);
2088 memcpy(top, bottom, row_pitch);
2089 memcpy(bottom, row, row_pitch);
2090 top += row_pitch;
2091 bottom -= row_pitch;
2093 HeapFree(GetProcessHeap(), 0, row);
2095 if (data.buffer_object)
2096 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
2099 error:
2100 if (data.buffer_object)
2102 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2103 checkGLcall("glBindBuffer");
2106 if (restore_rt)
2107 context_restore(context, restore_rt);
2110 /* Read the framebuffer contents into a texture. Note that this function
2111 * doesn't do any kind of flipping. Using this on an onscreen surface will
2112 * result in a flipped D3D texture.
2114 * Context activation is done by the caller. This function may temporarily
2115 * switch to a different context and restore the original one before return. */
2116 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
2118 struct wined3d_device *device = surface->resource.device;
2119 const struct wined3d_gl_info *gl_info;
2120 struct wined3d_context *context = old_ctx;
2121 struct wined3d_surface *restore_rt = NULL;
2123 if (old_ctx->current_rt != surface)
2125 restore_rt = old_ctx->current_rt;
2126 context = context_acquire(device, surface);
2129 gl_info = context->gl_info;
2130 device_invalidate_state(device, STATE_FRAMEBUFFER);
2132 wined3d_texture_prepare_texture(surface->container, context, srgb);
2133 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
2135 TRACE("Reading back offscreen render target %p.\n", surface);
2137 if (wined3d_resource_is_offscreen(&surface->container->resource))
2138 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
2139 else
2140 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
2141 checkGLcall("glReadBuffer");
2143 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
2144 0, 0, 0, 0, surface->resource.width, surface->resource.height);
2145 checkGLcall("glCopyTexSubImage2D");
2147 if (restore_rt)
2148 context_restore(context, restore_rt);
2151 static void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2153 if (multisample)
2155 DWORD samples;
2157 if (surface->rb_multisample)
2158 return;
2160 /* TODO: Nvidia exposes their Coverage Sample Anti-Aliasing (CSAA) feature
2161 * through type == MULTISAMPLE_XX and quality != 0. This could be mapped
2162 * to GL_NV_framebuffer_multisample_coverage.
2164 * AMD has a similar feature called Enhanced Quality Anti-Aliasing (EQAA),
2165 * but it does not have an equivalent OpenGL extension. */
2167 /* We advertise as many WINED3D_MULTISAMPLE_NON_MASKABLE quality levels
2168 * as the count of advertised multisample types for the surface format. */
2169 if (surface->resource.multisample_type == WINED3D_MULTISAMPLE_NON_MASKABLE)
2171 const struct wined3d_format *format = surface->resource.format;
2172 unsigned int i, count = 0;
2174 for (i = 0; i < sizeof(format->multisample_types) * 8; ++i)
2176 if (format->multisample_types & 1u << i)
2178 if (surface->resource.multisample_quality == count++)
2179 break;
2182 samples = i + 1;
2184 else
2186 samples = surface->resource.multisample_type;
2189 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2190 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2191 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
2192 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
2193 checkGLcall("glRenderbufferStorageMultisample()");
2194 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2196 else
2198 if (surface->rb_resolved)
2199 return;
2201 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2202 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2203 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
2204 surface->pow2Width, surface->pow2Height);
2205 checkGLcall("glRenderbufferStorage()");
2206 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2210 /* Does a direct frame buffer -> texture copy. Stretching is done with single
2211 * pixel copy calls. */
2212 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2213 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2215 struct wined3d_device *device = dst_surface->resource.device;
2216 const struct wined3d_gl_info *gl_info;
2217 float xrel, yrel;
2218 struct wined3d_context *context;
2219 BOOL upsidedown = FALSE;
2220 RECT dst_rect = *dst_rect_in;
2222 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2223 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2225 if(dst_rect.top > dst_rect.bottom) {
2226 UINT tmp = dst_rect.bottom;
2227 dst_rect.bottom = dst_rect.top;
2228 dst_rect.top = tmp;
2229 upsidedown = TRUE;
2232 context = context_acquire(device, src_surface);
2233 gl_info = context->gl_info;
2234 context_apply_blit_state(context, device);
2235 wined3d_texture_load(dst_surface->container, context, FALSE);
2237 /* Bind the target texture */
2238 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
2239 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
2241 TRACE("Reading from an offscreen target\n");
2242 upsidedown = !upsidedown;
2243 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
2245 else
2247 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
2249 checkGLcall("glReadBuffer");
2251 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
2252 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
2254 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2256 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2258 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2259 ERR("Texture filtering not supported in direct blit.\n");
2261 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2262 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2264 ERR("Texture filtering not supported in direct blit\n");
2267 if (upsidedown
2268 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2269 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2271 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
2272 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2273 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
2274 src_rect->left, src_surface->resource.height - src_rect->bottom,
2275 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2277 else
2279 LONG row;
2280 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
2281 /* I have to process this row by row to swap the image,
2282 * otherwise it would be upside down, so stretching in y direction
2283 * doesn't cost extra time
2285 * However, stretching in x direction can be avoided if not necessary
2287 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
2288 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2290 /* Well, that stuff works, but it's very slow.
2291 * find a better way instead
2293 LONG col;
2295 for (col = dst_rect.left; col < dst_rect.right; ++col)
2297 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2298 dst_rect.left + col /* x offset */, row /* y offset */,
2299 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
2302 else
2304 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2305 dst_rect.left /* x offset */, row /* y offset */,
2306 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
2310 checkGLcall("glCopyTexSubImage2D");
2312 context_release(context);
2314 /* The texture is now most up to date - If the surface is a render target
2315 * and has a drawable, this path is never entered. */
2316 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
2317 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
2320 /* Uses the hardware to stretch and flip the image */
2321 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2322 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2324 struct wined3d_device *device = dst_surface->resource.device;
2325 GLuint src, backup = 0;
2326 float left, right, top, bottom; /* Texture coordinates */
2327 UINT fbwidth = src_surface->resource.width;
2328 UINT fbheight = src_surface->resource.height;
2329 const struct wined3d_gl_info *gl_info;
2330 struct wined3d_context *context;
2331 GLenum drawBuffer = GL_BACK;
2332 GLenum offscreen_buffer;
2333 GLenum texture_target;
2334 BOOL noBackBufferBackup;
2335 BOOL src_offscreen;
2336 BOOL upsidedown = FALSE;
2337 RECT dst_rect = *dst_rect_in;
2339 TRACE("Using hwstretch blit\n");
2340 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2341 context = context_acquire(device, src_surface);
2342 gl_info = context->gl_info;
2343 context_apply_blit_state(context, device);
2344 wined3d_texture_load(dst_surface->container, context, FALSE);
2346 offscreen_buffer = context_get_offscreen_gl_buffer(context);
2348 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
2349 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2350 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
2352 /* Get it a description */
2353 wined3d_texture_load(src_surface->container, context, FALSE);
2356 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2357 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2359 if (context->aux_buffers >= 2)
2361 /* Got more than one aux buffer? Use the 2nd aux buffer */
2362 drawBuffer = GL_AUX1;
2364 else if ((!src_offscreen || offscreen_buffer == GL_BACK) && context->aux_buffers >= 1)
2366 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2367 drawBuffer = GL_AUX0;
2370 if (noBackBufferBackup)
2372 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
2373 checkGLcall("glGenTextures");
2374 context_bind_texture(context, GL_TEXTURE_2D, backup);
2375 texture_target = GL_TEXTURE_2D;
2377 else
2379 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2380 * we are reading from the back buffer, the backup can be used as source texture
2382 texture_target = src_surface->texture_target;
2383 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
2384 gl_info->gl_ops.gl.p_glEnable(texture_target);
2385 checkGLcall("glEnable(texture_target)");
2387 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2388 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
2391 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2392 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2394 if(dst_rect.top > dst_rect.bottom) {
2395 UINT tmp = dst_rect.bottom;
2396 dst_rect.bottom = dst_rect.top;
2397 dst_rect.top = tmp;
2398 upsidedown = TRUE;
2401 if (src_offscreen)
2403 TRACE("Reading from an offscreen target\n");
2404 upsidedown = !upsidedown;
2405 gl_info->gl_ops.gl.p_glReadBuffer(offscreen_buffer);
2407 else
2409 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
2412 /* TODO: Only back up the part that will be overwritten */
2413 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
2415 checkGLcall("glCopyTexSubImage2D");
2417 /* No issue with overriding these - the sampler is dirty due to blit usage */
2418 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
2419 checkGLcall("glTexParameteri");
2420 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2421 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
2422 checkGLcall("glTexParameteri");
2424 if (!src_surface->container->swapchain
2425 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
2427 src = backup ? backup : src_surface->container->texture_rgb.name;
2429 else
2431 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
2432 checkGLcall("glReadBuffer(GL_FRONT)");
2434 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
2435 checkGLcall("glGenTextures(1, &src)");
2436 context_bind_texture(context, GL_TEXTURE_2D, src);
2438 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
2439 * out for power of 2 sizes
2441 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
2442 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2443 checkGLcall("glTexImage2D");
2444 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
2446 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2447 checkGLcall("glTexParameteri");
2448 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2449 checkGLcall("glTexParameteri");
2451 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
2452 checkGLcall("glReadBuffer(GL_BACK)");
2454 if (texture_target != GL_TEXTURE_2D)
2456 gl_info->gl_ops.gl.p_glDisable(texture_target);
2457 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2458 texture_target = GL_TEXTURE_2D;
2461 checkGLcall("glEnd and previous");
2463 left = src_rect->left;
2464 right = src_rect->right;
2466 if (!upsidedown)
2468 top = src_surface->resource.height - src_rect->top;
2469 bottom = src_surface->resource.height - src_rect->bottom;
2471 else
2473 top = src_surface->resource.height - src_rect->bottom;
2474 bottom = src_surface->resource.height - src_rect->top;
2477 if (src_surface->container->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
2479 left /= src_surface->pow2Width;
2480 right /= src_surface->pow2Width;
2481 top /= src_surface->pow2Height;
2482 bottom /= src_surface->pow2Height;
2485 /* draw the source texture stretched and upside down. The correct surface is bound already */
2486 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2487 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2489 context_set_draw_buffer(context, drawBuffer);
2490 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
2492 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2493 /* bottom left */
2494 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
2495 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2497 /* top left */
2498 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
2499 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
2501 /* top right */
2502 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
2503 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2505 /* bottom right */
2506 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
2507 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
2508 gl_info->gl_ops.gl.p_glEnd();
2509 checkGLcall("glEnd and previous");
2511 if (texture_target != dst_surface->texture_target)
2513 gl_info->gl_ops.gl.p_glDisable(texture_target);
2514 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
2515 texture_target = dst_surface->texture_target;
2518 /* Now read the stretched and upside down image into the destination texture */
2519 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
2520 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
2522 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
2523 0, 0, /* We blitted the image to the origin */
2524 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2525 checkGLcall("glCopyTexSubImage2D");
2527 if (drawBuffer == GL_BACK)
2529 /* Write the back buffer backup back. */
2530 if (backup)
2532 if (texture_target != GL_TEXTURE_2D)
2534 gl_info->gl_ops.gl.p_glDisable(texture_target);
2535 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2536 texture_target = GL_TEXTURE_2D;
2538 context_bind_texture(context, GL_TEXTURE_2D, backup);
2540 else
2542 if (texture_target != src_surface->texture_target)
2544 gl_info->gl_ops.gl.p_glDisable(texture_target);
2545 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
2546 texture_target = src_surface->texture_target;
2548 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
2551 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2552 /* top left */
2553 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
2554 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
2556 /* bottom left */
2557 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
2558 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2560 /* bottom right */
2561 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
2562 (float)fbheight / (float)src_surface->pow2Height);
2563 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
2565 /* top right */
2566 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
2567 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
2568 gl_info->gl_ops.gl.p_glEnd();
2570 gl_info->gl_ops.gl.p_glDisable(texture_target);
2571 checkGLcall("glDisable(texture_target)");
2573 /* Cleanup */
2574 if (src != src_surface->container->texture_rgb.name && src != backup)
2576 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
2577 checkGLcall("glDeleteTextures(1, &src)");
2579 if (backup)
2581 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
2582 checkGLcall("glDeleteTextures(1, &backup)");
2585 if (wined3d_settings.strict_draw_ordering)
2586 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2588 context_release(context);
2590 /* The texture is now most up to date - If the surface is a render target
2591 * and has a drawable, this path is never entered. */
2592 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
2593 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
2596 /* Front buffer coordinates are always full screen coordinates, but our GL
2597 * drawable is limited to the window's client area. The sysmem and texture
2598 * copies do have the full screen size. Note that GL has a bottom-left
2599 * origin, while D3D has a top-left origin. */
2600 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
2602 UINT drawable_height;
2604 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
2606 POINT offset = {0, 0};
2607 RECT windowsize;
2609 ScreenToClient(window, &offset);
2610 OffsetRect(rect, offset.x, offset.y);
2612 GetClientRect(window, &windowsize);
2613 drawable_height = windowsize.bottom - windowsize.top;
2615 else
2617 drawable_height = surface->resource.height;
2620 rect->top = drawable_height - rect->top;
2621 rect->bottom = drawable_height - rect->bottom;
2624 /* Context activation is done by the caller. */
2625 static void surface_blt_to_drawable(const struct wined3d_device *device,
2626 struct wined3d_context *old_ctx,
2627 enum wined3d_texture_filter_type filter, BOOL alpha_test,
2628 struct wined3d_surface *src_surface, const RECT *src_rect_in,
2629 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
2631 const struct wined3d_gl_info *gl_info;
2632 struct wined3d_context *context = old_ctx;
2633 struct wined3d_surface *restore_rt = NULL;
2634 RECT src_rect, dst_rect;
2636 src_rect = *src_rect_in;
2637 dst_rect = *dst_rect_in;
2640 if (old_ctx->current_rt != dst_surface)
2642 restore_rt = old_ctx->current_rt;
2643 context = context_acquire(device, dst_surface);
2646 gl_info = context->gl_info;
2648 /* Make sure the surface is up-to-date. This should probably use
2649 * surface_load_location() and worry about the destination surface too,
2650 * unless we're overwriting it completely. */
2651 wined3d_texture_load(src_surface->container, context, FALSE);
2653 /* Activate the destination context, set it up for blitting */
2654 context_apply_blit_state(context, device);
2656 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
2657 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
2659 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
2661 if (alpha_test)
2663 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2664 checkGLcall("glEnable(GL_ALPHA_TEST)");
2666 /* For P8 surfaces, the alpha component contains the palette index.
2667 * Which means that the colorkey is one of the palette entries. In
2668 * other cases pixels that should be masked away have alpha set to 0. */
2669 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
2670 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
2671 (float)src_surface->container->async.src_blt_color_key.color_space_low_value / 255.0f);
2672 else
2673 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
2674 checkGLcall("glAlphaFunc");
2676 else
2678 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2679 checkGLcall("glDisable(GL_ALPHA_TEST)");
2682 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
2684 if (alpha_test)
2686 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2687 checkGLcall("glDisable(GL_ALPHA_TEST)");
2690 /* Leave the opengl state valid for blitting */
2691 device->blitter->unset_shader(context->gl_info);
2693 if (wined3d_settings.strict_draw_ordering
2694 || (dst_surface->container->swapchain
2695 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
2696 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2698 if (restore_rt)
2699 context_restore(context, restore_rt);
2702 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
2704 struct wined3d_resource *resource = &s->container->resource;
2705 struct wined3d_device *device = resource->device;
2706 struct wined3d_rendertarget_view_desc view_desc;
2707 struct wined3d_rendertarget_view *view;
2708 const struct blit_shader *blitter;
2709 HRESULT hr;
2711 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
2712 WINED3D_BLIT_OP_COLOR_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
2714 FIXME("No blitter is capable of performing the requested color fill operation.\n");
2715 return WINED3DERR_INVALIDCALL;
2718 view_desc.format_id = resource->format->id;
2719 view_desc.u.texture.level_idx = s->texture_level;
2720 view_desc.u.texture.layer_idx = s->texture_layer;
2721 view_desc.u.texture.layer_count = 1;
2722 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
2723 resource, NULL, &wined3d_null_parent_ops, &view)))
2725 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
2726 return hr;
2729 hr = blitter->color_fill(device, view, rect, color);
2730 wined3d_rendertarget_view_decref(view);
2732 return hr;
2735 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
2736 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
2737 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
2739 struct wined3d_device *device = dst_surface->resource.device;
2740 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
2741 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
2743 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
2744 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
2745 flags, fx, debug_d3dtexturefiltertype(filter));
2747 /* Get the swapchain. One of the surfaces has to be a primary surface */
2748 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2750 WARN("Destination is in sysmem, rejecting gl blt\n");
2751 return WINED3DERR_INVALIDCALL;
2754 dst_swapchain = dst_surface->container->swapchain;
2756 if (src_surface)
2758 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2760 WARN("Src is in sysmem, rejecting gl blt\n");
2761 return WINED3DERR_INVALIDCALL;
2764 src_swapchain = src_surface->container->swapchain;
2766 else
2768 src_swapchain = NULL;
2771 /* Early sort out of cases where no render target is used */
2772 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
2774 TRACE("No surface is render target, not using hardware blit.\n");
2775 return WINED3DERR_INVALIDCALL;
2778 /* No destination color keying supported */
2779 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
2781 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2782 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2783 return WINED3DERR_INVALIDCALL;
2786 if (dst_swapchain && dst_swapchain == src_swapchain)
2788 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
2789 return WINED3DERR_INVALIDCALL;
2792 if (dst_swapchain && src_swapchain)
2794 FIXME("Implement hardware blit between two different swapchains\n");
2795 return WINED3DERR_INVALIDCALL;
2798 if (dst_swapchain)
2800 /* Handled with regular texture -> swapchain blit */
2801 if (src_surface == rt)
2802 TRACE("Blit from active render target to a swapchain\n");
2804 else if (src_swapchain && dst_surface == rt)
2806 FIXME("Implement blit from a swapchain to the active render target\n");
2807 return WINED3DERR_INVALIDCALL;
2810 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
2812 /* Blit from render target to texture */
2813 BOOL stretchx;
2815 /* P8 read back is not implemented */
2816 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
2817 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
2819 TRACE("P8 read back not supported by frame buffer to texture blit\n");
2820 return WINED3DERR_INVALIDCALL;
2823 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
2825 TRACE("Color keying not supported by frame buffer to texture blit\n");
2826 return WINED3DERR_INVALIDCALL;
2827 /* Destination color key is checked above */
2830 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
2831 stretchx = TRUE;
2832 else
2833 stretchx = FALSE;
2835 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2836 * flip the image nor scale it.
2838 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2839 * -> If the app wants an image width an unscaled width, copy it line per line
2840 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
2841 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2842 * back buffer. This is slower than reading line per line, thus not used for flipping
2843 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2844 * pixel by pixel. */
2845 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
2846 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
2848 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
2849 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
2851 else
2853 TRACE("Using hardware stretching to flip / stretch the texture.\n");
2854 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
2857 surface_evict_sysmem(dst_surface);
2859 return WINED3D_OK;
2862 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
2863 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
2864 return WINED3DERR_INVALIDCALL;
2867 /* Context activation is done by the caller. */
2868 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
2869 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
2871 struct wined3d_device *device = surface->resource.device;
2872 const struct wined3d_gl_info *gl_info = context->gl_info;
2873 GLint compare_mode = GL_NONE;
2874 struct blt_info info;
2875 GLint old_binding = 0;
2876 RECT rect;
2878 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
2880 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
2881 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
2882 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2883 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
2884 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
2885 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
2886 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
2887 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
2888 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2889 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
2890 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
2892 SetRect(&rect, 0, h, w, 0);
2893 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
2894 context_active_texture(context, context->gl_info, 0);
2895 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
2896 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
2897 if (gl_info->supported[ARB_SHADOW])
2899 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
2900 if (compare_mode != GL_NONE)
2901 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
2904 device->shader_backend->shader_select_depth_blt(device->shader_priv,
2905 gl_info, info.tex_type, &surface->ds_current_size);
2907 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
2908 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
2909 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
2910 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
2911 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
2912 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
2913 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
2914 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
2915 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
2916 gl_info->gl_ops.gl.p_glEnd();
2918 if (compare_mode != GL_NONE)
2919 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
2920 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
2922 gl_info->gl_ops.gl.p_glPopAttrib();
2924 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
2927 void surface_modify_ds_location(struct wined3d_surface *surface,
2928 DWORD location, UINT w, UINT h)
2930 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
2932 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
2933 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
2934 wined3d_texture_set_dirty(surface->container);
2936 surface->ds_current_size.cx = w;
2937 surface->ds_current_size.cy = h;
2938 surface->locations = location;
2941 /* Context activation is done by the caller. */
2942 static void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
2944 const struct wined3d_gl_info *gl_info = context->gl_info;
2945 struct wined3d_device *device = surface->resource.device;
2946 GLsizei w, h;
2948 TRACE("surface %p, context %p, new location %#x.\n", surface, context, location);
2950 /* TODO: Make this work for modes other than FBO */
2951 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
2953 if (!(surface->locations & location))
2955 w = surface->ds_current_size.cx;
2956 h = surface->ds_current_size.cy;
2957 surface->ds_current_size.cx = 0;
2958 surface->ds_current_size.cy = 0;
2960 else
2962 w = surface->resource.width;
2963 h = surface->resource.height;
2966 if (surface->current_renderbuffer)
2968 FIXME("Not supported with fixed up depth stencil.\n");
2969 return;
2972 wined3d_surface_prepare(surface, context, location);
2974 if (location == WINED3D_LOCATION_TEXTURE_RGB)
2976 GLint old_binding = 0;
2977 GLenum bind_target;
2979 /* The render target is allowed to be smaller than the depth/stencil
2980 * buffer, so the onscreen depth/stencil buffer is potentially smaller
2981 * than the offscreen surface. Don't overwrite the offscreen surface
2982 * with undefined data. */
2983 w = min(w, context->swapchain->desc.backbuffer_width);
2984 h = min(h, context->swapchain->desc.backbuffer_height);
2986 TRACE("Copying onscreen depth buffer to depth texture.\n");
2988 if (!device->depth_blt_texture)
2989 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
2991 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
2992 * directly on the FBO texture. That's because we need to flip. */
2993 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
2994 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
2995 NULL, WINED3D_LOCATION_DRAWABLE);
2996 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2998 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
2999 bind_target = GL_TEXTURE_RECTANGLE_ARB;
3001 else
3003 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3004 bind_target = GL_TEXTURE_2D;
3006 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
3007 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
3008 * internal format, because the internal format might include stencil
3009 * data. In principle we should copy stencil data as well, but unless
3010 * the driver supports stencil export it's hard to do, and doesn't
3011 * seem to be needed in practice. If the hardware doesn't support
3012 * writing stencil data, the glCopyTexImage2D() call might trigger
3013 * software fallbacks. */
3014 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
3015 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3016 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3017 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3018 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3019 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
3020 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
3022 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3023 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
3024 context_set_draw_buffer(context, GL_NONE);
3026 /* Do the actual blit */
3027 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
3028 checkGLcall("depth_blt");
3030 context_invalidate_state(context, STATE_FRAMEBUFFER);
3032 if (wined3d_settings.strict_draw_ordering)
3033 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3035 else if (location == WINED3D_LOCATION_DRAWABLE)
3037 TRACE("Copying depth texture to onscreen depth buffer.\n");
3039 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3040 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3041 NULL, WINED3D_LOCATION_DRAWABLE);
3042 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
3043 0, surface->pow2Height - h, w, h, surface->texture_target);
3044 checkGLcall("depth_blt");
3046 context_invalidate_state(context, STATE_FRAMEBUFFER);
3048 if (wined3d_settings.strict_draw_ordering)
3049 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3051 else
3053 ERR("Invalid location (%#x) specified.\n", location);
3057 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
3059 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3061 surface->locations |= location;
3064 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
3066 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3068 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3069 wined3d_texture_set_dirty(surface->container);
3070 surface->locations &= ~location;
3072 if (!surface->locations)
3073 ERR("Surface %p does not have any up to date location.\n", surface);
3076 static DWORD resource_access_from_location(DWORD location)
3078 switch (location)
3080 case WINED3D_LOCATION_SYSMEM:
3081 case WINED3D_LOCATION_USER_MEMORY:
3082 case WINED3D_LOCATION_DIB:
3083 case WINED3D_LOCATION_BUFFER:
3084 return WINED3D_RESOURCE_ACCESS_CPU;
3086 case WINED3D_LOCATION_DRAWABLE:
3087 case WINED3D_LOCATION_TEXTURE_SRGB:
3088 case WINED3D_LOCATION_TEXTURE_RGB:
3089 case WINED3D_LOCATION_RB_MULTISAMPLE:
3090 case WINED3D_LOCATION_RB_RESOLVED:
3091 return WINED3D_RESOURCE_ACCESS_GPU;
3093 default:
3094 FIXME("Unhandled location %#x.\n", location);
3095 return 0;
3099 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
3101 struct wined3d_device *device = surface->resource.device;
3102 struct wined3d_context *context;
3103 const struct wined3d_gl_info *gl_info;
3104 struct wined3d_bo_address dst, src;
3105 UINT size = surface->resource.size;
3107 surface_get_memory(surface, &dst, location);
3108 surface_get_memory(surface, &src, surface->locations);
3110 if (dst.buffer_object)
3112 context = context_acquire(device, NULL);
3113 gl_info = context->gl_info;
3114 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
3115 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr));
3116 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
3117 checkGLcall("Upload PBO");
3118 context_release(context);
3119 return;
3121 if (src.buffer_object)
3123 context = context_acquire(device, NULL);
3124 gl_info = context->gl_info;
3125 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
3126 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr));
3127 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
3128 checkGLcall("Download PBO");
3129 context_release(context);
3130 return;
3132 memcpy(dst.addr, src.addr, size);
3135 /* Context activation is done by the caller. */
3136 static void surface_load_sysmem(struct wined3d_surface *surface,
3137 struct wined3d_context *context, DWORD dst_location)
3139 const struct wined3d_gl_info *gl_info = context->gl_info;
3141 wined3d_surface_prepare(surface, context, dst_location);
3143 if (surface->locations & surface_simple_locations)
3145 surface_copy_simple_location(surface, dst_location);
3146 return;
3149 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
3150 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3152 /* Download the surface to system memory. */
3153 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3155 struct wined3d_texture *texture = surface->container;
3157 wined3d_texture_bind_and_dirtify(texture, context,
3158 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
3159 surface_download_data(surface, gl_info, dst_location);
3160 ++texture->download_count;
3162 return;
3165 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
3167 read_from_framebuffer(surface, context, dst_location);
3168 return;
3171 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
3172 surface, wined3d_debug_location(surface->locations));
3175 /* Context activation is done by the caller. */
3176 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
3177 struct wined3d_context *context)
3179 RECT r;
3181 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3182 && wined3d_resource_is_offscreen(&surface->container->resource))
3184 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
3185 return WINED3DERR_INVALIDCALL;
3188 surface_get_rect(surface, NULL, &r);
3189 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3190 surface_blt_to_drawable(surface->resource.device, context,
3191 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
3193 return WINED3D_OK;
3196 static HRESULT surface_load_texture(struct wined3d_surface *surface,
3197 struct wined3d_context *context, BOOL srgb)
3199 unsigned int width, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
3200 const RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
3201 const struct wined3d_gl_info *gl_info = context->gl_info;
3202 struct wined3d_device *device = surface->resource.device;
3203 const struct wined3d_color_key_conversion *conversion;
3204 struct wined3d_texture *texture = surface->container;
3205 struct wined3d_bo_address data;
3206 struct wined3d_format format;
3207 POINT dst_point = {0, 0};
3208 BYTE *mem = NULL;
3210 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
3211 && wined3d_resource_is_offscreen(&texture->resource)
3212 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
3214 surface_load_fb_texture(surface, srgb, context);
3216 return WINED3D_OK;
3219 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
3220 && (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
3221 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3222 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3223 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3225 if (srgb)
3226 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
3227 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
3228 else
3229 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
3230 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
3232 return WINED3D_OK;
3235 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
3236 && (!srgb || (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
3237 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3238 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3239 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3241 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
3242 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
3243 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
3244 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3246 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
3247 &rect, surface, dst_location, &rect);
3249 return WINED3D_OK;
3252 /* Upload from system memory */
3254 if (srgb)
3256 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
3257 == WINED3D_LOCATION_TEXTURE_RGB)
3259 /* Performance warning... */
3260 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
3261 surface_load_location(surface, context, surface->resource.map_binding);
3264 else
3266 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
3267 == WINED3D_LOCATION_TEXTURE_SRGB)
3269 /* Performance warning... */
3270 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
3271 surface_load_location(surface, context, surface->resource.map_binding);
3275 if (!(surface->locations & surface_simple_locations))
3277 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
3278 /* Lets hope we get it from somewhere... */
3279 surface_load_location(surface, context, WINED3D_LOCATION_SYSMEM);
3282 wined3d_texture_prepare_texture(texture, context, srgb);
3283 wined3d_texture_bind_and_dirtify(texture, context, srgb);
3284 wined3d_texture_get_pitch(texture, surface->texture_level, &src_row_pitch, &src_slice_pitch);
3286 width = surface->resource.width;
3288 format = *texture->resource.format;
3289 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
3290 format = *wined3d_get_format(gl_info, conversion->dst_format);
3292 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
3293 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
3294 * getting called. */
3295 if ((format.convert || conversion)
3296 && texture->sub_resources[surface_get_sub_resource_idx(surface)].buffer_object)
3298 TRACE("Removing the pbo attached to surface %p.\n", surface);
3300 if (surface->flags & SFLAG_DIBSECTION)
3301 surface->resource.map_binding = WINED3D_LOCATION_DIB;
3302 else
3303 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
3305 surface_load_location(surface, context, surface->resource.map_binding);
3306 wined3d_texture_remove_buffer_object(texture, surface_get_sub_resource_idx(surface), gl_info);
3309 surface_get_memory(surface, &data, surface->locations);
3310 if (format.convert)
3312 /* This code is entered for texture formats which need a fixup. */
3313 UINT height = surface->resource.height;
3315 format.byte_count = format.conv_byte_count;
3316 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
3318 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
3320 ERR("Out of memory (%u).\n", dst_slice_pitch);
3321 context_release(context);
3322 return E_OUTOFMEMORY;
3324 format.convert(data.addr, mem, src_row_pitch, src_slice_pitch,
3325 dst_row_pitch, dst_slice_pitch, width, height, 1);
3326 src_row_pitch = dst_row_pitch;
3327 data.addr = mem;
3329 else if (conversion)
3331 /* This code is only entered for color keying fixups */
3332 struct wined3d_palette *palette = NULL;
3333 UINT height = surface->resource.height;
3335 wined3d_format_calculate_pitch(&format, device->surface_alignment,
3336 width, height, &dst_row_pitch, &dst_slice_pitch);
3338 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
3340 ERR("Out of memory (%u).\n", dst_slice_pitch);
3341 context_release(context);
3342 return E_OUTOFMEMORY;
3344 if (texture->swapchain && texture->swapchain->palette)
3345 palette = texture->swapchain->palette;
3346 conversion->convert(data.addr, src_row_pitch, mem, dst_row_pitch,
3347 width, height, palette, &texture->async.gl_color_key);
3348 src_row_pitch = dst_row_pitch;
3349 data.addr = mem;
3352 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
3353 src_row_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
3355 HeapFree(GetProcessHeap(), 0, mem);
3357 return WINED3D_OK;
3360 /* Context activation is done by the caller. */
3361 static void surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
3362 DWORD dst_location)
3364 const RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3365 DWORD src_location;
3367 if (surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE)
3368 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
3369 else if (surface->locations & WINED3D_LOCATION_RB_RESOLVED)
3370 src_location = WINED3D_LOCATION_RB_RESOLVED;
3371 else if (surface->locations & WINED3D_LOCATION_TEXTURE_SRGB)
3372 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
3373 else /* surface_blt_fbo will load the source location if necessary. */
3374 src_location = WINED3D_LOCATION_TEXTURE_RGB;
3376 surface_blt_fbo(surface->resource.device, context, WINED3D_TEXF_POINT,
3377 surface, src_location, &rect, surface, dst_location, &rect);
3380 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
3381 HRESULT surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3383 HRESULT hr;
3385 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3387 if (surface->locations & location && (!(surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3388 || (surface->ds_current_size.cx == surface->resource.width
3389 && surface->ds_current_size.cy == surface->resource.height)))
3391 TRACE("Location (%#x) is already up to date.\n", location);
3392 return WINED3D_OK;
3395 if (WARN_ON(d3d_surface))
3397 DWORD required_access = resource_access_from_location(location);
3398 if ((surface->resource.access_flags & required_access) != required_access)
3399 WARN("Operation requires %#x access, but surface only has %#x.\n",
3400 required_access, surface->resource.access_flags);
3403 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3405 TRACE("Surface previously discarded, nothing to do.\n");
3406 wined3d_surface_prepare(surface, context, location);
3407 surface_validate_location(surface, location);
3408 surface_invalidate_location(surface, WINED3D_LOCATION_DISCARDED);
3409 goto done;
3412 if (!surface->locations)
3414 ERR("Surface %p does not have any up to date location.\n", surface);
3415 surface_validate_location(surface, WINED3D_LOCATION_DISCARDED);
3416 return surface_load_location(surface, context, location);
3419 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3421 if ((location == WINED3D_LOCATION_TEXTURE_RGB && surface->locations & WINED3D_LOCATION_DRAWABLE)
3422 || (location == WINED3D_LOCATION_DRAWABLE && surface->locations & WINED3D_LOCATION_TEXTURE_RGB))
3424 surface_load_ds_location(surface, context, location);
3425 goto done;
3428 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
3429 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
3430 return WINED3DERR_INVALIDCALL;
3433 switch (location)
3435 case WINED3D_LOCATION_DIB:
3436 case WINED3D_LOCATION_USER_MEMORY:
3437 case WINED3D_LOCATION_SYSMEM:
3438 case WINED3D_LOCATION_BUFFER:
3439 surface_load_sysmem(surface, context, location);
3440 break;
3442 case WINED3D_LOCATION_DRAWABLE:
3443 if (FAILED(hr = surface_load_drawable(surface, context)))
3444 return hr;
3445 break;
3447 case WINED3D_LOCATION_RB_RESOLVED:
3448 case WINED3D_LOCATION_RB_MULTISAMPLE:
3449 surface_load_renderbuffer(surface, context, location);
3450 break;
3452 case WINED3D_LOCATION_TEXTURE_RGB:
3453 case WINED3D_LOCATION_TEXTURE_SRGB:
3454 if (FAILED(hr = surface_load_texture(surface, context,
3455 location == WINED3D_LOCATION_TEXTURE_SRGB)))
3456 return hr;
3457 break;
3459 default:
3460 ERR("Don't know how to handle location %#x.\n", location);
3461 break;
3464 done:
3465 surface_validate_location(surface, location);
3467 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3469 surface->ds_current_size.cx = surface->resource.width;
3470 surface->ds_current_size.cy = surface->resource.height;
3473 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
3474 surface_evict_sysmem(surface);
3476 return WINED3D_OK;
3479 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
3480 /* Context activation is done by the caller. */
3481 static void ffp_blit_free(struct wined3d_device *device) { }
3483 /* Context activation is done by the caller. */
3484 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
3485 const struct wined3d_color_key *color_key)
3487 const struct wined3d_gl_info *gl_info = context->gl_info;
3489 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
3490 checkGLcall("glEnable(target)");
3492 return WINED3D_OK;
3495 /* Context activation is done by the caller. */
3496 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
3498 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
3499 checkGLcall("glDisable(GL_TEXTURE_2D)");
3500 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
3502 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3503 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3505 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
3507 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
3508 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
3512 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
3513 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
3514 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
3515 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
3517 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
3519 TRACE("Source or destination is in system memory.\n");
3520 return FALSE;
3523 switch (blit_op)
3525 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
3526 if (d3d_info->shader_color_key)
3528 TRACE("Color keying requires converted textures.\n");
3529 return FALSE;
3531 case WINED3D_BLIT_OP_COLOR_BLIT:
3532 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
3533 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
3535 TRACE("Checking support for fixup:\n");
3536 dump_color_fixup_desc(src_format->color_fixup);
3539 /* We only support identity conversions. */
3540 if (!is_identity_fixup(src_format->color_fixup)
3541 || !is_identity_fixup(dst_format->color_fixup))
3543 TRACE("Fixups are not supported.\n");
3544 return FALSE;
3547 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
3549 TRACE("Can only blit to render targets.\n");
3550 return FALSE;
3552 return TRUE;
3554 case WINED3D_BLIT_OP_COLOR_FILL:
3555 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
3557 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
3558 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
3559 return FALSE;
3561 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
3563 TRACE("Color fill not supported\n");
3564 return FALSE;
3567 /* FIXME: We should reject color fills on formats with fixups,
3568 * but this would break P8 color fills for example. */
3570 return TRUE;
3572 case WINED3D_BLIT_OP_DEPTH_FILL:
3573 return TRUE;
3575 default:
3576 TRACE("Unsupported blit_op=%d\n", blit_op);
3577 return FALSE;
3581 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
3582 const RECT *rect, const struct wined3d_color *color)
3584 const RECT draw_rect = {0, 0, view->width, view->height};
3585 struct wined3d_fb_state fb = {&view, NULL};
3587 device_clear_render_targets(device, 1, &fb, 1, rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
3589 return WINED3D_OK;
3592 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
3593 struct wined3d_rendertarget_view *view, const RECT *rect, DWORD clear_flags,
3594 float depth, DWORD stencil)
3596 const RECT draw_rect = {0, 0, view->width, view->height};
3597 struct wined3d_fb_state fb = {NULL, view};
3599 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, clear_flags, NULL, depth, stencil);
3601 return WINED3D_OK;
3604 static void ffp_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
3605 struct wined3d_surface *src_surface, const RECT *src_rect,
3606 struct wined3d_surface *dst_surface, const RECT *dst_rect,
3607 const struct wined3d_color_key *color_key)
3609 struct wined3d_context *context;
3611 /* Blit from offscreen surface to render target */
3612 struct wined3d_color_key old_blt_key = src_surface->container->async.src_blt_color_key;
3613 DWORD old_color_key_flags = src_surface->container->async.color_key_flags;
3615 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
3617 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT, color_key);
3619 context = context_acquire(device, dst_surface);
3621 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
3622 glEnable(GL_ALPHA_TEST);
3624 surface_blt_to_drawable(device, context, filter,
3625 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
3627 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
3628 glDisable(GL_ALPHA_TEST);
3630 context_release(context);
3632 /* Restore the color key parameters */
3633 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT,
3634 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
3636 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
3637 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
3640 const struct blit_shader ffp_blit = {
3641 ffp_blit_alloc,
3642 ffp_blit_free,
3643 ffp_blit_set,
3644 ffp_blit_unset,
3645 ffp_blit_supported,
3646 ffp_blit_color_fill,
3647 ffp_blit_depth_fill,
3648 ffp_blit_blit_surface,
3651 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
3653 return WINED3D_OK;
3656 /* Context activation is done by the caller. */
3657 static void cpu_blit_free(struct wined3d_device *device)
3661 /* Context activation is done by the caller. */
3662 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
3663 const struct wined3d_color_key *color_key)
3665 return WINED3D_OK;
3668 /* Context activation is done by the caller. */
3669 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
3673 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
3674 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
3675 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
3676 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
3678 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
3680 return TRUE;
3683 return FALSE;
3686 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
3687 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
3688 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
3690 UINT row_block_count;
3691 const BYTE *src_row;
3692 BYTE *dst_row;
3693 UINT x, y;
3695 src_row = src_data;
3696 dst_row = dst_data;
3698 row_block_count = (update_w + format->block_width - 1) / format->block_width;
3700 if (!flags)
3702 for (y = 0; y < update_h; y += format->block_height)
3704 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
3705 src_row += src_pitch;
3706 dst_row += dst_pitch;
3709 return WINED3D_OK;
3712 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
3714 src_row += (((update_h / format->block_height) - 1) * src_pitch);
3716 switch (format->id)
3718 case WINED3DFMT_DXT1:
3719 for (y = 0; y < update_h; y += format->block_height)
3721 struct block
3723 WORD color[2];
3724 BYTE control_row[4];
3727 const struct block *s = (const struct block *)src_row;
3728 struct block *d = (struct block *)dst_row;
3730 for (x = 0; x < row_block_count; ++x)
3732 d[x].color[0] = s[x].color[0];
3733 d[x].color[1] = s[x].color[1];
3734 d[x].control_row[0] = s[x].control_row[3];
3735 d[x].control_row[1] = s[x].control_row[2];
3736 d[x].control_row[2] = s[x].control_row[1];
3737 d[x].control_row[3] = s[x].control_row[0];
3739 src_row -= src_pitch;
3740 dst_row += dst_pitch;
3742 return WINED3D_OK;
3744 case WINED3DFMT_DXT2:
3745 case WINED3DFMT_DXT3:
3746 for (y = 0; y < update_h; y += format->block_height)
3748 struct block
3750 WORD alpha_row[4];
3751 WORD color[2];
3752 BYTE control_row[4];
3755 const struct block *s = (const struct block *)src_row;
3756 struct block *d = (struct block *)dst_row;
3758 for (x = 0; x < row_block_count; ++x)
3760 d[x].alpha_row[0] = s[x].alpha_row[3];
3761 d[x].alpha_row[1] = s[x].alpha_row[2];
3762 d[x].alpha_row[2] = s[x].alpha_row[1];
3763 d[x].alpha_row[3] = s[x].alpha_row[0];
3764 d[x].color[0] = s[x].color[0];
3765 d[x].color[1] = s[x].color[1];
3766 d[x].control_row[0] = s[x].control_row[3];
3767 d[x].control_row[1] = s[x].control_row[2];
3768 d[x].control_row[2] = s[x].control_row[1];
3769 d[x].control_row[3] = s[x].control_row[0];
3771 src_row -= src_pitch;
3772 dst_row += dst_pitch;
3774 return WINED3D_OK;
3776 default:
3777 FIXME("Compressed flip not implemented for format %s.\n",
3778 debug_d3dformat(format->id));
3779 return E_NOTIMPL;
3783 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
3784 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
3786 return E_NOTIMPL;
3789 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
3790 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3791 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
3792 enum wined3d_texture_filter_type filter)
3794 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
3795 const struct wined3d_format *src_format, *dst_format;
3796 struct wined3d_texture *converted_texture = NULL;
3797 unsigned int src_fmt_flags, dst_fmt_flags;
3798 struct wined3d_map_desc dst_map, src_map;
3799 const BYTE *sbase = NULL;
3800 HRESULT hr = WINED3D_OK;
3801 BOOL same_sub_resource;
3802 const BYTE *sbuf;
3803 BYTE *dbuf;
3804 int x, y;
3806 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
3807 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
3808 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
3809 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
3811 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
3813 same_sub_resource = TRUE;
3814 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, NULL, 0);
3815 src_map = dst_map;
3816 src_format = dst_texture->resource.format;
3817 dst_format = src_format;
3818 dst_fmt_flags = dst_texture->resource.format_flags;
3819 src_fmt_flags = dst_fmt_flags;
3821 else
3823 same_sub_resource = FALSE;
3824 dst_format = dst_texture->resource.format;
3825 dst_fmt_flags = dst_texture->resource.format_flags;
3826 if (src_texture)
3828 if (dst_texture->resource.format->id != src_texture->resource.format->id)
3830 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format->id)))
3832 /* The conv function writes a FIXME */
3833 WARN("Cannot convert source surface format to dest format.\n");
3834 goto release;
3836 src_texture = converted_texture;
3837 src_sub_resource_idx = 0;
3839 wined3d_resource_map(&src_texture->resource, src_sub_resource_idx, &src_map, NULL, WINED3D_MAP_READONLY);
3840 src_format = src_texture->resource.format;
3841 src_fmt_flags = src_texture->resource.format_flags;
3843 else
3845 src_format = dst_format;
3846 src_fmt_flags = dst_fmt_flags;
3849 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, dst_box, 0);
3852 bpp = dst_format->byte_count;
3853 src_height = src_box->bottom - src_box->top;
3854 src_width = src_box->right - src_box->left;
3855 dst_height = dst_box->bottom - dst_box->top;
3856 dst_width = dst_box->right - dst_box->left;
3857 row_byte_count = dst_width * bpp;
3859 if (src_texture)
3860 sbase = (BYTE *)src_map.data
3861 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
3862 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
3863 if (same_sub_resource)
3864 dbuf = (BYTE *)dst_map.data
3865 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
3866 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
3867 else
3868 dbuf = dst_map.data;
3870 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
3872 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
3874 if (same_sub_resource)
3876 FIXME("Only plain blits supported on compressed surfaces.\n");
3877 hr = E_NOTIMPL;
3878 goto release;
3881 if (src_height != dst_height || src_width != dst_width)
3883 WARN("Stretching not supported on compressed surfaces.\n");
3884 hr = WINED3DERR_INVALIDCALL;
3885 goto release;
3888 if (!wined3d_texture_check_block_align(src_texture,
3889 src_sub_resource_idx % src_texture->level_count, src_box))
3891 WARN("Source rectangle not block-aligned.\n");
3892 hr = WINED3DERR_INVALIDCALL;
3893 goto release;
3896 if (!wined3d_texture_check_block_align(dst_texture,
3897 dst_sub_resource_idx % dst_texture->level_count, dst_box))
3899 WARN("Destination rectangle not block-aligned.\n");
3900 hr = WINED3DERR_INVALIDCALL;
3901 goto release;
3904 hr = surface_cpu_blt_compressed(sbase, dbuf,
3905 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
3906 src_format, flags, fx);
3907 goto release;
3910 /* First, all the 'source-less' blits */
3911 if (flags & WINED3D_BLT_COLOR_FILL)
3913 hr = _Blt_ColorFill(dbuf, dst_width, dst_height, bpp, dst_map.row_pitch, fx->fill_color);
3914 flags &= ~WINED3D_BLT_COLOR_FILL;
3917 if (flags & WINED3D_BLT_DEPTH_FILL)
3918 FIXME("WINED3D_BLT_DEPTH_FILL needs to be implemented!\n");
3920 /* Now the 'with source' blits. */
3921 if (src_texture)
3923 int sx, xinc, sy, yinc;
3925 if (!dst_width || !dst_height) /* Hmm... stupid program? */
3926 goto release;
3928 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
3929 && (src_width != dst_width || src_height != dst_height))
3931 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
3932 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
3935 xinc = (src_width << 16) / dst_width;
3936 yinc = (src_height << 16) / dst_height;
3938 if (!flags)
3940 /* No effects, we can cheat here. */
3941 if (dst_width == src_width)
3943 if (dst_height == src_height)
3945 /* No stretching in either direction. This needs to be as
3946 * fast as possible. */
3947 sbuf = sbase;
3949 /* Check for overlapping surfaces. */
3950 if (!same_sub_resource || dst_box->top < src_box->top
3951 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
3953 /* No overlap, or dst above src, so copy from top downwards. */
3954 for (y = 0; y < dst_height; ++y)
3956 memcpy(dbuf, sbuf, row_byte_count);
3957 sbuf += src_map.row_pitch;
3958 dbuf += dst_map.row_pitch;
3961 else if (dst_box->top > src_box->top)
3963 /* Copy from bottom upwards. */
3964 sbuf += src_map.row_pitch * dst_height;
3965 dbuf += dst_map.row_pitch * dst_height;
3966 for (y = 0; y < dst_height; ++y)
3968 sbuf -= src_map.row_pitch;
3969 dbuf -= dst_map.row_pitch;
3970 memcpy(dbuf, sbuf, row_byte_count);
3973 else
3975 /* Src and dst overlapping on the same line, use memmove. */
3976 for (y = 0; y < dst_height; ++y)
3978 memmove(dbuf, sbuf, row_byte_count);
3979 sbuf += src_map.row_pitch;
3980 dbuf += dst_map.row_pitch;
3984 else
3986 /* Stretching in y direction only. */
3987 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3989 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3990 memcpy(dbuf, sbuf, row_byte_count);
3991 dbuf += dst_map.row_pitch;
3995 else
3997 /* Stretching in X direction. */
3998 int last_sy = -1;
3999 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
4001 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4003 if ((sy >> 16) == (last_sy >> 16))
4005 /* This source row is the same as last source row -
4006 * Copy the already stretched row. */
4007 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
4009 else
4011 #define STRETCH_ROW(type) \
4012 do { \
4013 const type *s = (const type *)sbuf; \
4014 type *d = (type *)dbuf; \
4015 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
4016 d[x] = s[sx >> 16]; \
4017 } while(0)
4019 switch(bpp)
4021 case 1:
4022 STRETCH_ROW(BYTE);
4023 break;
4024 case 2:
4025 STRETCH_ROW(WORD);
4026 break;
4027 case 4:
4028 STRETCH_ROW(DWORD);
4029 break;
4030 case 3:
4032 const BYTE *s;
4033 BYTE *d = dbuf;
4034 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
4036 DWORD pixel;
4038 s = sbuf + 3 * (sx >> 16);
4039 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4040 d[0] = (pixel ) & 0xff;
4041 d[1] = (pixel >> 8) & 0xff;
4042 d[2] = (pixel >> 16) & 0xff;
4043 d += 3;
4045 break;
4047 default:
4048 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
4049 hr = WINED3DERR_NOTAVAILABLE;
4050 goto error;
4052 #undef STRETCH_ROW
4054 dbuf += dst_map.row_pitch;
4055 last_sy = sy;
4059 else
4061 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
4062 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
4063 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
4064 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
4065 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
4067 /* The color keying flags are checked for correctness in ddraw */
4068 if (flags & WINED3D_BLT_SRC_CKEY)
4070 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
4071 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
4073 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
4075 keylow = fx->src_color_key.color_space_low_value;
4076 keyhigh = fx->src_color_key.color_space_high_value;
4079 if (flags & WINED3D_BLT_DST_CKEY)
4081 /* Destination color keys are taken from the source surface! */
4082 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
4083 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
4085 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
4087 destkeylow = fx->dst_color_key.color_space_low_value;
4088 destkeyhigh = fx->dst_color_key.color_space_high_value;
4091 if (bpp == 1)
4093 keymask = 0xff;
4095 else
4097 DWORD masks[3];
4098 get_color_masks(src_format, masks);
4099 keymask = masks[0]
4100 | masks[1]
4101 | masks[2];
4103 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
4104 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
4107 if (flags & WINED3D_BLT_FX)
4109 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
4110 LONG tmpxy;
4111 dTopLeft = dbuf;
4112 dTopRight = dbuf + ((dst_width - 1) * bpp);
4113 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
4114 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
4116 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
4118 /* I don't think we need to do anything about this flag */
4119 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
4121 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
4123 tmp = dTopRight;
4124 dTopRight = dTopLeft;
4125 dTopLeft = tmp;
4126 tmp = dBottomRight;
4127 dBottomRight = dBottomLeft;
4128 dBottomLeft = tmp;
4129 dstxinc = dstxinc * -1;
4131 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
4133 tmp = dTopLeft;
4134 dTopLeft = dBottomLeft;
4135 dBottomLeft = tmp;
4136 tmp = dTopRight;
4137 dTopRight = dBottomRight;
4138 dBottomRight = tmp;
4139 dstyinc = dstyinc * -1;
4141 if (fx->fx & WINEDDBLTFX_NOTEARING)
4143 /* I don't think we need to do anything about this flag */
4144 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
4146 if (fx->fx & WINEDDBLTFX_ROTATE180)
4148 tmp = dBottomRight;
4149 dBottomRight = dTopLeft;
4150 dTopLeft = tmp;
4151 tmp = dBottomLeft;
4152 dBottomLeft = dTopRight;
4153 dTopRight = tmp;
4154 dstxinc = dstxinc * -1;
4155 dstyinc = dstyinc * -1;
4157 if (fx->fx & WINEDDBLTFX_ROTATE270)
4159 tmp = dTopLeft;
4160 dTopLeft = dBottomLeft;
4161 dBottomLeft = dBottomRight;
4162 dBottomRight = dTopRight;
4163 dTopRight = tmp;
4164 tmpxy = dstxinc;
4165 dstxinc = dstyinc;
4166 dstyinc = tmpxy;
4167 dstxinc = dstxinc * -1;
4169 if (fx->fx & WINEDDBLTFX_ROTATE90)
4171 tmp = dTopLeft;
4172 dTopLeft = dTopRight;
4173 dTopRight = dBottomRight;
4174 dBottomRight = dBottomLeft;
4175 dBottomLeft = tmp;
4176 tmpxy = dstxinc;
4177 dstxinc = dstyinc;
4178 dstyinc = tmpxy;
4179 dstyinc = dstyinc * -1;
4181 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
4183 /* I don't think we need to do anything about this flag */
4184 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
4186 dbuf = dTopLeft;
4187 flags &= ~(WINED3D_BLT_FX);
4190 #define COPY_COLORKEY_FX(type) \
4191 do { \
4192 const type *s; \
4193 type *d = (type *)dbuf, *dx, tmp; \
4194 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
4196 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
4197 dx = d; \
4198 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
4200 tmp = s[sx >> 16]; \
4201 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
4202 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
4204 dx[0] = tmp; \
4206 dx = (type *)(((BYTE *)dx) + dstxinc); \
4208 d = (type *)(((BYTE *)d) + dstyinc); \
4210 } while(0)
4212 switch (bpp)
4214 case 1:
4215 COPY_COLORKEY_FX(BYTE);
4216 break;
4217 case 2:
4218 COPY_COLORKEY_FX(WORD);
4219 break;
4220 case 4:
4221 COPY_COLORKEY_FX(DWORD);
4222 break;
4223 case 3:
4225 const BYTE *s;
4226 BYTE *d = dbuf, *dx;
4227 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
4229 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4230 dx = d;
4231 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
4233 DWORD pixel, dpixel = 0;
4234 s = sbuf + 3 * (sx>>16);
4235 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4236 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
4237 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
4238 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
4240 dx[0] = (pixel ) & 0xff;
4241 dx[1] = (pixel >> 8) & 0xff;
4242 dx[2] = (pixel >> 16) & 0xff;
4244 dx += dstxinc;
4246 d += dstyinc;
4248 break;
4250 default:
4251 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
4252 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
4253 hr = WINED3DERR_NOTAVAILABLE;
4254 goto error;
4255 #undef COPY_COLORKEY_FX
4260 error:
4261 if (flags && FIXME_ON(d3d_surface))
4263 FIXME("\tUnsupported flags: %#x.\n", flags);
4266 release:
4267 wined3d_resource_unmap(&dst_texture->resource, dst_sub_resource_idx);
4268 if (src_texture && !same_sub_resource)
4269 wined3d_resource_unmap(&src_texture->resource, src_sub_resource_idx);
4270 if (converted_texture)
4271 wined3d_texture_decref(converted_texture);
4273 return hr;
4276 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
4277 const RECT *rect, const struct wined3d_color *color)
4279 const struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
4280 static const struct wined3d_box src_box;
4281 struct wined3d_blt_fx fx;
4283 fx.fill_color = wined3d_format_convert_from_float(view->format, color);
4284 return surface_cpu_blt(wined3d_texture_from_resource(view->resource), view->sub_resource_idx,
4285 &box, NULL, 0, &src_box, WINED3D_BLT_COLOR_FILL, &fx, WINED3D_TEXF_POINT);
4288 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
4289 struct wined3d_rendertarget_view *view, const RECT *rect, DWORD clear_flags,
4290 float depth, DWORD stencil)
4292 FIXME("Depth/stencil filling not implemented by cpu_blit.\n");
4293 return WINED3DERR_INVALIDCALL;
4296 static void cpu_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
4297 struct wined3d_surface *src_surface, const RECT *src_rect,
4298 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4299 const struct wined3d_color_key *color_key)
4301 /* FIXME: Remove error returns from surface_blt_cpu. */
4302 ERR("Blit method not implemented by cpu_blit.\n");
4305 const struct blit_shader cpu_blit = {
4306 cpu_blit_alloc,
4307 cpu_blit_free,
4308 cpu_blit_set,
4309 cpu_blit_unset,
4310 cpu_blit_supported,
4311 cpu_blit_color_fill,
4312 cpu_blit_depth_fill,
4313 cpu_blit_blit_surface,
4316 HRESULT wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4317 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4318 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
4320 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
4321 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
4322 struct wined3d_texture *dst_texture = dst_surface->container;
4323 struct wined3d_device *device = dst_texture->resource.device;
4324 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
4325 struct wined3d_texture *src_texture = NULL;
4326 DWORD src_ds_flags, dst_ds_flags;
4327 BOOL scale, convert;
4329 static const DWORD simple_blit = WINED3D_BLT_ASYNC
4330 | WINED3D_BLT_COLOR_FILL
4331 | WINED3D_BLT_SRC_CKEY
4332 | WINED3D_BLT_SRC_CKEY_OVERRIDE
4333 | WINED3D_BLT_WAIT
4334 | WINED3D_BLT_DEPTH_FILL
4335 | WINED3D_BLT_DO_NOT_WAIT
4336 | WINED3D_BLT_ALPHA_TEST;
4338 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4339 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4340 flags, fx, debug_d3dtexturefiltertype(filter));
4341 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
4343 if (fx)
4345 TRACE("fx %#x.\n", fx->fx);
4346 TRACE("fill_color 0x%08x.\n", fx->fill_color);
4347 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
4348 fx->dst_color_key.color_space_low_value,
4349 fx->dst_color_key.color_space_high_value);
4350 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
4351 fx->src_color_key.color_space_low_value,
4352 fx->src_color_key.color_space_high_value);
4355 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
4357 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
4358 return WINEDDERR_SURFACEBUSY;
4361 if (dst_rect->left >= dst_rect->right || dst_rect->top >= dst_rect->bottom
4362 || dst_rect->left > dst_surface->resource.width || dst_rect->left < 0
4363 || dst_rect->top > dst_surface->resource.height || dst_rect->top < 0
4364 || dst_rect->right > dst_surface->resource.width || dst_rect->right < 0
4365 || dst_rect->bottom > dst_surface->resource.height || dst_rect->bottom < 0)
4367 WARN("The application gave us a bad destination rectangle.\n");
4368 return WINEDDERR_INVALIDRECT;
4371 if (src_surface)
4373 if (src_rect->left >= src_rect->right || src_rect->top >= src_rect->bottom
4374 || src_rect->left > src_surface->resource.width || src_rect->left < 0
4375 || src_rect->top > src_surface->resource.height || src_rect->top < 0
4376 || src_rect->right > src_surface->resource.width || src_rect->right < 0
4377 || src_rect->bottom > src_surface->resource.height || src_rect->bottom < 0)
4379 WARN("The application gave us a bad source rectangle.\n");
4380 return WINEDDERR_INVALIDRECT;
4382 src_texture = src_surface->container;
4385 if (!fx || !(fx->fx))
4386 flags &= ~WINED3D_BLT_FX;
4388 if (flags & WINED3D_BLT_WAIT)
4389 flags &= ~WINED3D_BLT_WAIT;
4391 if (flags & WINED3D_BLT_ASYNC)
4393 static unsigned int once;
4395 if (!once++)
4396 FIXME("Can't handle WINED3D_BLT_ASYNC flag.\n");
4397 flags &= ~WINED3D_BLT_ASYNC;
4400 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
4401 if (flags & WINED3D_BLT_DO_NOT_WAIT)
4403 static unsigned int once;
4405 if (!once++)
4406 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
4407 flags &= ~WINED3D_BLT_DO_NOT_WAIT;
4410 if (!device->d3d_initialized)
4412 WARN("D3D not initialized, using fallback.\n");
4413 goto cpu;
4416 /* We want to avoid invalidating the sysmem location for converted
4417 * surfaces, since otherwise we'd have to convert the data back when
4418 * locking them. */
4419 if (dst_texture->flags & WINED3D_TEXTURE_CONVERTED || dst_texture->resource.format->convert
4420 || wined3d_format_get_color_key_conversion(dst_texture, TRUE))
4422 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
4423 goto cpu;
4426 if (flags & ~simple_blit)
4428 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
4429 goto fallback;
4432 if (src_surface)
4433 src_swapchain = src_texture->swapchain;
4434 else
4435 src_swapchain = NULL;
4437 dst_swapchain = dst_texture->swapchain;
4439 /* This isn't strictly needed. FBO blits for example could deal with
4440 * cross-swapchain blits by first downloading the source to a texture
4441 * before switching to the destination context. We just have this here to
4442 * not have to deal with the issue, since cross-swapchain blits should be
4443 * rare. */
4444 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
4446 FIXME("Using fallback for cross-swapchain blit.\n");
4447 goto fallback;
4450 scale = src_surface
4451 && (src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
4452 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top);
4453 convert = src_surface && src_texture->resource.format->id != dst_texture->resource.format->id;
4455 dst_ds_flags = dst_texture->resource.format_flags
4456 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4457 if (src_surface)
4458 src_ds_flags = src_texture->resource.format_flags
4459 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4460 else
4461 src_ds_flags = 0;
4463 if (src_ds_flags || dst_ds_flags)
4465 if (flags & WINED3D_BLT_DEPTH_FILL)
4467 float depth;
4469 TRACE("Depth fill.\n");
4471 if (!surface_convert_depth_to_float(dst_surface, fx->fill_color, &depth))
4472 return WINED3DERR_INVALIDCALL;
4474 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, dst_rect, depth)))
4475 return WINED3D_OK;
4477 else
4479 if (src_ds_flags != dst_ds_flags)
4481 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
4482 return WINED3DERR_INVALIDCALL;
4485 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_texture->resource.draw_binding,
4486 src_rect, dst_surface, dst_texture->resource.draw_binding, dst_rect)))
4487 return WINED3D_OK;
4490 else
4492 const struct blit_shader *blitter;
4494 /* In principle this would apply to depth blits as well, but we don't
4495 * implement those in the CPU blitter at the moment. */
4496 if ((dst_surface->locations & dst_surface->resource.map_binding)
4497 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
4499 if (scale)
4500 TRACE("Not doing sysmem blit because of scaling.\n");
4501 else if (convert)
4502 TRACE("Not doing sysmem blit because of format conversion.\n");
4503 else
4504 goto cpu;
4507 if (flags & WINED3D_BLT_COLOR_FILL)
4509 struct wined3d_color color;
4510 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
4512 TRACE("Color fill.\n");
4514 if (!wined3d_format_convert_color_to_float(dst_texture->resource.format,
4515 palette, fx->fill_color, &color))
4516 goto fallback;
4518 if (SUCCEEDED(surface_color_fill(dst_surface, dst_rect, &color)))
4519 return WINED3D_OK;
4521 else
4523 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
4524 const struct wined3d_color_key *color_key = NULL;
4526 TRACE("Color blit.\n");
4527 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
4529 color_key = &fx->src_color_key;
4530 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
4532 else if (flags & WINED3D_BLT_SRC_CKEY)
4534 color_key = &src_texture->async.src_blt_color_key;
4535 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
4537 else if (flags & WINED3D_BLT_ALPHA_TEST)
4539 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
4541 else if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
4542 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
4544 /* Upload */
4545 if (scale)
4546 TRACE("Not doing upload because of scaling.\n");
4547 else if (convert)
4548 TRACE("Not doing upload because of format conversion.\n");
4549 else
4551 POINT dst_point = {dst_rect->left, dst_rect->top};
4553 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
4555 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
4557 struct wined3d_context *context = context_acquire(device, dst_surface);
4558 surface_load_location(dst_surface, context, dst_texture->resource.draw_binding);
4559 context_release(context);
4561 return WINED3D_OK;
4565 else if (dst_swapchain && dst_swapchain->back_buffers
4566 && dst_texture == dst_swapchain->front_buffer
4567 && src_texture == dst_swapchain->back_buffers[0])
4569 /* Use present for back -> front blits. The idea behind this is
4570 * that present is potentially faster than a blit, in particular
4571 * when FBO blits aren't available. Some ddraw applications like
4572 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
4573 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
4574 * applications can't blit directly to the frontbuffer. */
4575 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
4577 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
4579 /* Set the swap effect to COPY, we don't want the backbuffer
4580 * to become undefined. */
4581 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
4582 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
4583 dst_swapchain->desc.swap_effect = swap_effect;
4585 return WINED3D_OK;
4588 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
4589 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
4590 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format))
4592 struct wined3d_context *context;
4593 TRACE("Using FBO blit.\n");
4595 context = context_acquire(device, NULL);
4596 surface_blt_fbo(device, context, filter,
4597 src_surface, src_texture->resource.draw_binding, src_rect,
4598 dst_surface, dst_texture->resource.draw_binding, dst_rect);
4599 context_release(context);
4601 surface_validate_location(dst_surface, dst_texture->resource.draw_binding);
4602 surface_invalidate_location(dst_surface, ~dst_texture->resource.draw_binding);
4604 return WINED3D_OK;
4607 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
4608 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
4609 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format);
4610 if (blitter)
4612 blitter->blit_surface(device, blit_op, filter, src_surface,
4613 src_rect, dst_surface, dst_rect, color_key);
4614 return WINED3D_OK;
4619 fallback:
4620 /* Special cases for render targets. */
4621 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
4622 return WINED3D_OK;
4624 cpu:
4625 return surface_cpu_blt(dst_texture, surface_get_sub_resource_idx(dst_surface), &dst_box,
4626 src_texture, src_texture ? surface_get_sub_resource_idx(src_surface) : 0, &src_box, flags, fx, filter);
4629 HRESULT wined3d_surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
4630 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
4632 struct wined3d_device *device = container->resource.device;
4633 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4634 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
4635 BOOL lockable = flags & WINED3D_TEXTURE_CREATE_MAPPABLE;
4636 UINT multisample_quality = desc->multisample_quality;
4637 unsigned int resource_size;
4638 HRESULT hr;
4640 /* Quick lockable sanity check.
4641 * TODO: remove this after surfaces, usage and lockability have been debugged properly
4642 * this function is too deep to need to care about things like this.
4643 * Levels need to be checked too, since they all affect what can be done. */
4644 switch (desc->pool)
4646 case WINED3D_POOL_MANAGED:
4647 if (desc->usage & WINED3DUSAGE_DYNAMIC)
4648 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
4649 break;
4651 case WINED3D_POOL_DEFAULT:
4652 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
4653 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
4654 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
4655 break;
4657 case WINED3D_POOL_SCRATCH:
4658 case WINED3D_POOL_SYSTEM_MEM:
4659 break;
4661 default:
4662 FIXME("Unknown pool %#x.\n", desc->pool);
4663 break;
4666 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
4667 FIXME("Trying to create a render target that isn't in the default pool.\n");
4669 /* FIXME: Check that the format is supported by the device. */
4671 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
4672 if (!resource_size)
4673 return WINED3DERR_INVALIDCALL;
4675 if (device->wined3d->flags & WINED3D_NO3D)
4676 surface->surface_ops = &gdi_surface_ops;
4677 else
4678 surface->surface_ops = &surface_ops;
4680 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE,
4681 format, desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height,
4682 1, resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
4684 WARN("Failed to initialize resource, returning %#x.\n", hr);
4685 return hr;
4688 surface->container = container;
4689 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
4690 list_init(&surface->renderbuffers);
4691 list_init(&surface->overlays);
4693 /* Flags */
4694 if (flags & WINED3D_TEXTURE_CREATE_DISCARD)
4695 surface->flags |= SFLAG_DISCARD;
4696 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
4697 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
4699 surface->texture_target = target;
4700 surface->texture_level = level;
4701 surface->texture_layer = layer;
4703 /* Call the private setup routine */
4704 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
4706 ERR("Private setup failed, hr %#x.\n", hr);
4707 wined3d_surface_cleanup(surface);
4708 return hr;
4711 /* Similar to lockable rendertargets above, creating the DIB section
4712 * during surface initialization prevents the sysmem pointer from changing
4713 * after a wined3d_texture_get_dc() call. */
4714 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
4715 && SUCCEEDED(surface_create_dib_section(surface)))
4716 surface->resource.map_binding = WINED3D_LOCATION_DIB;
4718 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
4720 wined3d_resource_free_sysmem(&surface->resource);
4721 surface_validate_location(surface, WINED3D_LOCATION_DIB);
4722 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
4725 return hr;
4728 /* Context activation is done by the caller. Context may be NULL in
4729 * WINED3D_NO3D mode. */
4730 void wined3d_surface_prepare(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4732 struct wined3d_texture *texture = surface->container;
4734 switch (location)
4736 case WINED3D_LOCATION_SYSMEM:
4737 surface_prepare_system_memory(surface);
4738 break;
4740 case WINED3D_LOCATION_USER_MEMORY:
4741 if (!texture->user_memory)
4742 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
4743 break;
4745 case WINED3D_LOCATION_DIB:
4746 if (!surface->dib.bitmap_data)
4747 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
4748 break;
4750 case WINED3D_LOCATION_BUFFER:
4751 wined3d_texture_prepare_buffer_object(texture,
4752 surface_get_sub_resource_idx(surface), context->gl_info);
4753 break;
4755 case WINED3D_LOCATION_TEXTURE_RGB:
4756 wined3d_texture_prepare_texture(texture, context, FALSE);
4757 break;
4759 case WINED3D_LOCATION_TEXTURE_SRGB:
4760 wined3d_texture_prepare_texture(texture, context, TRUE);
4761 break;
4763 case WINED3D_LOCATION_RB_MULTISAMPLE:
4764 surface_prepare_rb(surface, context->gl_info, TRUE);
4765 break;
4767 case WINED3D_LOCATION_RB_RESOLVED:
4768 surface_prepare_rb(surface, context->gl_info, FALSE);
4769 break;