wined3d: Use the texture draw binding instead of the surface draw binding.
[wine.git] / dlls / wined3d / surface.c
blob23c9fd30f38682d1a50fba624ab3f3f9dba6153a
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2011, 2013-2014 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d);
37 #define MAXLOCKCOUNT 50 /* After this amount of locks do not free the sysmem copy. */
39 static const DWORD surface_simple_locations =
40 WINED3D_LOCATION_SYSMEM | WINED3D_LOCATION_USER_MEMORY
41 | WINED3D_LOCATION_DIB | WINED3D_LOCATION_BUFFER;
43 static void surface_cleanup(struct wined3d_surface *surface)
45 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->pbo || surface->rb_multisample
50 || surface->rb_resolved || !list_empty(&surface->renderbuffers))
52 struct wined3d_renderbuffer_entry *entry, *entry2;
53 const struct wined3d_gl_info *gl_info;
54 struct wined3d_context *context;
56 context = context_acquire(surface->resource.device, NULL);
57 gl_info = context->gl_info;
59 if (surface->pbo)
61 TRACE("Deleting PBO %u.\n", surface->pbo);
62 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
65 if (surface->rb_multisample)
67 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
68 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
71 if (surface->rb_resolved)
73 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
74 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
77 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
79 TRACE("Deleting renderbuffer %u.\n", entry->id);
80 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
81 HeapFree(GetProcessHeap(), 0, entry);
84 context_release(context);
87 if (surface->flags & SFLAG_DIBSECTION)
89 DeleteDC(surface->hDC);
90 DeleteObject(surface->dib.DIBsection);
91 surface->dib.bitmap_data = NULL;
94 if (surface->overlay_dest)
95 list_remove(&surface->overlay_entry);
97 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
99 list_remove(&overlay->overlay_entry);
100 overlay->overlay_dest = NULL;
103 resource_cleanup(&surface->resource);
106 void wined3d_surface_destroy(struct wined3d_surface *surface)
108 TRACE("surface %p.\n", surface);
110 surface_cleanup(surface);
111 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
112 HeapFree(GetProcessHeap(), 0, surface);
115 void surface_get_drawable_size(const struct wined3d_surface *surface, const struct wined3d_context *context,
116 unsigned int *width, unsigned int *height)
118 if (surface->container->swapchain)
120 /* The drawable size of an onscreen drawable is the surface size.
121 * (Actually: The window size, but the surface is created in window
122 * size.) */
123 *width = context->current_rt->resource.width;
124 *height = context->current_rt->resource.height;
126 else if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER)
128 const struct wined3d_swapchain *swapchain = context->swapchain;
130 /* The drawable size of a backbuffer / aux buffer offscreen target is
131 * the size of the current context's drawable, which is the size of
132 * the back buffer of the swapchain the active context belongs to. */
133 *width = swapchain->desc.backbuffer_width;
134 *height = swapchain->desc.backbuffer_height;
136 else
138 /* The drawable size of an FBO target is the OpenGL texture size,
139 * which is the power of two size. */
140 *width = context->current_rt->pow2Width;
141 *height = context->current_rt->pow2Height;
145 struct blt_info
147 GLenum binding;
148 GLenum bind_target;
149 enum tex_types tex_type;
150 GLfloat coords[4][3];
153 struct float_rect
155 float l;
156 float t;
157 float r;
158 float b;
161 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
163 f->l = ((r->left * 2.0f) / w) - 1.0f;
164 f->t = ((r->top * 2.0f) / h) - 1.0f;
165 f->r = ((r->right * 2.0f) / w) - 1.0f;
166 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
169 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
171 GLfloat (*coords)[3] = info->coords;
172 struct float_rect f;
174 switch (target)
176 default:
177 FIXME("Unsupported texture target %#x\n", target);
178 /* Fall back to GL_TEXTURE_2D */
179 case GL_TEXTURE_2D:
180 info->binding = GL_TEXTURE_BINDING_2D;
181 info->bind_target = GL_TEXTURE_2D;
182 info->tex_type = tex_2d;
183 coords[0][0] = (float)rect->left / w;
184 coords[0][1] = (float)rect->top / h;
185 coords[0][2] = 0.0f;
187 coords[1][0] = (float)rect->right / w;
188 coords[1][1] = (float)rect->top / h;
189 coords[1][2] = 0.0f;
191 coords[2][0] = (float)rect->left / w;
192 coords[2][1] = (float)rect->bottom / h;
193 coords[2][2] = 0.0f;
195 coords[3][0] = (float)rect->right / w;
196 coords[3][1] = (float)rect->bottom / h;
197 coords[3][2] = 0.0f;
198 break;
200 case GL_TEXTURE_RECTANGLE_ARB:
201 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
202 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
203 info->tex_type = tex_rect;
204 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
205 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
206 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
207 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
208 break;
210 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
211 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
212 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
213 info->tex_type = tex_cube;
214 cube_coords_float(rect, w, h, &f);
216 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
217 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
218 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
219 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
220 break;
222 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
223 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
224 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
225 info->tex_type = tex_cube;
226 cube_coords_float(rect, w, h, &f);
228 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
229 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
230 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
231 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
232 break;
234 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
235 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
236 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
237 info->tex_type = tex_cube;
238 cube_coords_float(rect, w, h, &f);
240 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
241 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
242 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
243 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
244 break;
246 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
247 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
248 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
249 info->tex_type = tex_cube;
250 cube_coords_float(rect, w, h, &f);
252 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
253 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
254 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
255 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
256 break;
258 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
259 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
260 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
261 info->tex_type = tex_cube;
262 cube_coords_float(rect, w, h, &f);
264 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
265 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
266 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
267 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
268 break;
270 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
271 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
272 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
273 info->tex_type = tex_cube;
274 cube_coords_float(rect, w, h, &f);
276 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
277 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
278 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
279 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
280 break;
284 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
286 if (rect_in)
287 *rect_out = *rect_in;
288 else
290 rect_out->left = 0;
291 rect_out->top = 0;
292 rect_out->right = surface->resource.width;
293 rect_out->bottom = surface->resource.height;
297 /* Context activation is done by the caller. */
298 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
299 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
301 const struct wined3d_gl_info *gl_info = context->gl_info;
302 struct wined3d_texture *texture = src_surface->container;
303 struct blt_info info;
305 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
307 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
308 checkGLcall("glEnable(bind_target)");
310 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
312 /* Filtering for StretchRect */
313 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
314 wined3d_gl_mag_filter(magLookup, filter));
315 checkGLcall("glTexParameteri");
316 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
317 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
318 checkGLcall("glTexParameteri");
319 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
320 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
321 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
322 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
323 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
324 checkGLcall("glTexEnvi");
326 /* Draw a quad */
327 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
328 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
329 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
331 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
332 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
334 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
335 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
337 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
338 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
339 gl_info->gl_ops.gl.p_glEnd();
341 /* Unbind the texture */
342 context_bind_texture(context, info.bind_target, 0);
344 /* We changed the filtering settings on the texture. Inform the
345 * container about this to get the filters reset properly next draw. */
346 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_POINT;
347 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
348 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_NONE;
349 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
352 /* Works correctly only for <= 4 bpp formats. */
353 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
355 masks[0] = ((1 << format->red_size) - 1) << format->red_offset;
356 masks[1] = ((1 << format->green_size) - 1) << format->green_offset;
357 masks[2] = ((1 << format->blue_size) - 1) << format->blue_offset;
360 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
362 const struct wined3d_format *format = surface->resource.format;
363 SYSTEM_INFO sysInfo;
364 BITMAPINFO *b_info;
365 int extraline = 0;
366 DWORD *masks;
368 TRACE("surface %p.\n", surface);
370 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
372 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
373 return WINED3DERR_INVALIDCALL;
376 switch (format->byte_count)
378 case 2:
379 case 4:
380 /* Allocate extra space to store the RGB bit masks. */
381 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
382 break;
384 case 3:
385 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
386 break;
388 default:
389 /* Allocate extra space for a palette. */
390 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
391 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
392 break;
395 if (!b_info)
396 return E_OUTOFMEMORY;
398 /* Some applications access the surface in via DWORDs, and do not take
399 * the necessary care at the end of the surface. So we need at least
400 * 4 extra bytes at the end of the surface. Check against the page size,
401 * if the last page used for the surface has at least 4 spare bytes we're
402 * safe, otherwise add an extra line to the DIB section. */
403 GetSystemInfo(&sysInfo);
404 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
406 extraline = 1;
407 TRACE("Adding an extra line to the DIB section.\n");
410 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
411 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
412 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
413 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
414 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
415 * wined3d_surface_get_pitch(surface);
416 b_info->bmiHeader.biPlanes = 1;
417 b_info->bmiHeader.biBitCount = format->byte_count * 8;
419 b_info->bmiHeader.biXPelsPerMeter = 0;
420 b_info->bmiHeader.biYPelsPerMeter = 0;
421 b_info->bmiHeader.biClrUsed = 0;
422 b_info->bmiHeader.biClrImportant = 0;
424 /* Get the bit masks */
425 masks = (DWORD *)b_info->bmiColors;
426 switch (surface->resource.format->id)
428 case WINED3DFMT_B8G8R8_UNORM:
429 b_info->bmiHeader.biCompression = BI_RGB;
430 break;
432 case WINED3DFMT_B5G5R5X1_UNORM:
433 case WINED3DFMT_B5G5R5A1_UNORM:
434 case WINED3DFMT_B4G4R4A4_UNORM:
435 case WINED3DFMT_B4G4R4X4_UNORM:
436 case WINED3DFMT_B2G3R3_UNORM:
437 case WINED3DFMT_B2G3R3A8_UNORM:
438 case WINED3DFMT_R10G10B10A2_UNORM:
439 case WINED3DFMT_R8G8B8A8_UNORM:
440 case WINED3DFMT_R8G8B8X8_UNORM:
441 case WINED3DFMT_B10G10R10A2_UNORM:
442 case WINED3DFMT_B5G6R5_UNORM:
443 case WINED3DFMT_R16G16B16A16_UNORM:
444 b_info->bmiHeader.biCompression = BI_BITFIELDS;
445 get_color_masks(format, masks);
446 break;
448 default:
449 /* Don't know palette */
450 b_info->bmiHeader.biCompression = BI_RGB;
451 break;
454 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
455 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
456 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
457 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
459 if (!surface->dib.DIBsection)
461 ERR("Failed to create DIB section.\n");
462 HeapFree(GetProcessHeap(), 0, b_info);
463 return HRESULT_FROM_WIN32(GetLastError());
466 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
467 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
469 HeapFree(GetProcessHeap(), 0, b_info);
471 /* Now allocate a DC. */
472 surface->hDC = CreateCompatibleDC(0);
473 SelectObject(surface->hDC, surface->dib.DIBsection);
475 surface->flags |= SFLAG_DIBSECTION;
477 return WINED3D_OK;
480 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
481 DWORD location)
483 if (location & WINED3D_LOCATION_BUFFER)
485 data->addr = NULL;
486 data->buffer_object = surface->pbo;
487 return;
489 if (location & WINED3D_LOCATION_USER_MEMORY)
491 data->addr = surface->user_memory;
492 data->buffer_object = 0;
493 return;
495 if (location & WINED3D_LOCATION_DIB)
497 data->addr = surface->dib.bitmap_data;
498 data->buffer_object = 0;
499 return;
501 if (location & WINED3D_LOCATION_SYSMEM)
503 data->addr = surface->resource.heap_memory;
504 data->buffer_object = 0;
505 return;
508 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
509 data->addr = NULL;
510 data->buffer_object = 0;
513 static void surface_prepare_buffer(struct wined3d_surface *surface)
515 struct wined3d_context *context;
516 GLenum error;
517 const struct wined3d_gl_info *gl_info;
519 if (surface->pbo)
520 return;
522 context = context_acquire(surface->resource.device, NULL);
523 gl_info = context->gl_info;
525 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
526 error = gl_info->gl_ops.gl.p_glGetError();
527 if (!surface->pbo || error != GL_NO_ERROR)
528 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
530 TRACE("Binding PBO %u.\n", surface->pbo);
532 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
533 checkGLcall("glBindBufferARB");
535 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
536 NULL, GL_STREAM_DRAW_ARB));
537 checkGLcall("glBufferDataARB");
539 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
540 checkGLcall("glBindBufferARB");
542 context_release(context);
545 static void surface_prepare_system_memory(struct wined3d_surface *surface)
547 TRACE("surface %p.\n", surface);
549 if (surface->resource.heap_memory)
550 return;
552 /* Whatever surface we have, make sure that there is memory allocated
553 * for the downloaded copy, or a PBO to map. */
554 if (!wined3d_resource_allocate_sysmem(&surface->resource))
555 ERR("Failed to allocate system memory.\n");
557 if (surface->locations & WINED3D_LOCATION_SYSMEM)
558 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
561 void surface_prepare_map_memory(struct wined3d_surface *surface)
563 switch (surface->resource.map_binding)
565 case WINED3D_LOCATION_SYSMEM:
566 surface_prepare_system_memory(surface);
567 break;
569 case WINED3D_LOCATION_USER_MEMORY:
570 if (!surface->user_memory)
571 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
572 break;
574 case WINED3D_LOCATION_DIB:
575 if (!surface->dib.bitmap_data)
576 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
577 break;
579 case WINED3D_LOCATION_BUFFER:
580 surface_prepare_buffer(surface);
581 break;
583 default:
584 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
588 static void surface_evict_sysmem(struct wined3d_surface *surface)
590 if (surface->resource.map_count || surface->flags & SFLAG_DONOTFREE)
591 return;
593 wined3d_resource_free_sysmem(&surface->resource);
594 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
597 static void surface_force_reload(struct wined3d_surface *surface)
599 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
602 static void surface_release_client_storage(struct wined3d_surface *surface)
604 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
605 const struct wined3d_gl_info *gl_info = context->gl_info;
607 if (surface->container->texture_rgb.name)
609 wined3d_texture_bind_and_dirtify(surface->container, context, FALSE);
610 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
611 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
613 if (surface->container->texture_srgb.name)
615 wined3d_texture_bind_and_dirtify(surface->container, context, TRUE);
616 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
617 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
620 context_release(context);
622 surface_invalidate_location(surface, WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
623 surface_force_reload(surface);
626 static BOOL surface_use_pbo(const struct wined3d_surface *surface)
628 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
630 return surface->resource.pool == WINED3D_POOL_DEFAULT
631 && surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU
632 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
633 && !surface->resource.format->convert
634 && !(surface->flags & (SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM));
637 static HRESULT surface_private_setup(struct wined3d_surface *surface)
639 /* TODO: Check against the maximum texture sizes supported by the video card. */
640 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
641 unsigned int pow2Width, pow2Height;
643 TRACE("surface %p.\n", surface);
645 /* Non-power2 support */
646 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
647 || gl_info->supported[ARB_TEXTURE_RECTANGLE])
649 pow2Width = surface->resource.width;
650 pow2Height = surface->resource.height;
652 else
654 /* Find the nearest pow2 match */
655 pow2Width = pow2Height = 1;
656 while (pow2Width < surface->resource.width)
657 pow2Width <<= 1;
658 while (pow2Height < surface->resource.height)
659 pow2Height <<= 1;
661 surface->pow2Width = pow2Width;
662 surface->pow2Height = pow2Height;
664 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
666 /* TODO: Add support for non power two compressed textures. */
667 if (surface->resource.format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE))
669 FIXME("(%p) Compressed or height scaled non-power-two textures are not supported w(%d) h(%d)\n",
670 surface, surface->resource.width, surface->resource.height);
671 return WINED3DERR_NOTAVAILABLE;
675 if (pow2Width != surface->resource.width
676 || pow2Height != surface->resource.height)
678 surface->flags |= SFLAG_NONPOW2;
681 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
682 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
684 /* One of three options:
685 * 1: Do the same as we do with NPOT and scale the texture, (any
686 * texture ops would require the texture to be scaled which is
687 * potentially slow)
688 * 2: Set the texture to the maximum size (bad idea).
689 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
690 * 4: Create the surface, but allow it to be used only for DirectDraw
691 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
692 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
693 * the render target. */
694 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
696 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
697 return WINED3DERR_NOTAVAILABLE;
700 /* We should never use this surface in combination with OpenGL! */
701 TRACE("Creating an oversized surface: %ux%u.\n",
702 surface->pow2Width, surface->pow2Height);
705 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
706 surface->locations = WINED3D_LOCATION_DISCARDED;
708 if (surface_use_pbo(surface))
709 surface->resource.map_binding = WINED3D_LOCATION_BUFFER;
711 return WINED3D_OK;
714 static void surface_unmap(struct wined3d_surface *surface)
716 struct wined3d_device *device = surface->resource.device;
717 const struct wined3d_gl_info *gl_info;
718 struct wined3d_context *context;
720 TRACE("surface %p.\n", surface);
722 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
724 switch (surface->resource.map_binding)
726 case WINED3D_LOCATION_SYSMEM:
727 case WINED3D_LOCATION_USER_MEMORY:
728 case WINED3D_LOCATION_DIB:
729 break;
731 case WINED3D_LOCATION_BUFFER:
732 context = context_acquire(device, NULL);
733 gl_info = context->gl_info;
735 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
736 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
737 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
738 checkGLcall("glUnmapBufferARB");
739 context_release(context);
740 break;
742 default:
743 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
746 if (surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_TEXTURE_RGB))
748 TRACE("Not dirtified, nothing to do.\n");
749 return;
752 if (surface->container->swapchain && surface->container->swapchain->front_buffer == surface)
753 surface_load_location(surface, surface->container->resource.draw_binding);
754 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
755 FIXME("Depth / stencil buffer locking is not implemented.\n");
758 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
760 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
761 return FALSE;
762 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
763 return FALSE;
764 return TRUE;
767 static void surface_depth_blt_fbo(const struct wined3d_device *device,
768 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
769 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
771 const struct wined3d_gl_info *gl_info;
772 struct wined3d_context *context;
773 DWORD src_mask, dst_mask;
774 GLbitfield gl_mask;
776 TRACE("device %p\n", device);
777 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
778 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
779 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
780 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
782 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
783 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
785 if (src_mask != dst_mask)
787 ERR("Incompatible formats %s and %s.\n",
788 debug_d3dformat(src_surface->resource.format->id),
789 debug_d3dformat(dst_surface->resource.format->id));
790 return;
793 if (!src_mask)
795 ERR("Not a depth / stencil format: %s.\n",
796 debug_d3dformat(src_surface->resource.format->id));
797 return;
800 gl_mask = 0;
801 if (src_mask & WINED3DFMT_FLAG_DEPTH)
802 gl_mask |= GL_DEPTH_BUFFER_BIT;
803 if (src_mask & WINED3DFMT_FLAG_STENCIL)
804 gl_mask |= GL_STENCIL_BUFFER_BIT;
806 /* Make sure the locations are up-to-date. Loading the destination
807 * surface isn't required if the entire surface is overwritten. */
808 surface_load_location(src_surface, src_location);
809 if (!surface_is_full_rect(dst_surface, dst_rect))
810 surface_load_location(dst_surface, dst_location);
812 context = context_acquire(device, NULL);
813 if (!context->valid)
815 context_release(context);
816 WARN("Invalid context, skipping blit.\n");
817 return;
820 gl_info = context->gl_info;
822 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
823 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
825 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
826 context_set_draw_buffer(context, GL_NONE);
827 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
828 context_invalidate_state(context, STATE_FRAMEBUFFER);
830 if (gl_mask & GL_DEPTH_BUFFER_BIT)
832 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
833 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
835 if (gl_mask & GL_STENCIL_BUFFER_BIT)
837 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
839 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
840 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
842 gl_info->gl_ops.gl.p_glStencilMask(~0U);
843 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
846 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
847 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
849 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
850 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
851 checkGLcall("glBlitFramebuffer()");
853 if (wined3d_settings.strict_draw_ordering)
854 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
856 context_release(context);
859 /* Blit between surface locations. Onscreen on different swapchains is not supported.
860 * Depth / stencil is not supported. */
861 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
862 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
863 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
865 const struct wined3d_gl_info *gl_info;
866 struct wined3d_context *context;
867 RECT src_rect, dst_rect;
868 GLenum gl_filter;
869 GLenum buffer;
871 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
872 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
873 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
874 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
875 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
877 src_rect = *src_rect_in;
878 dst_rect = *dst_rect_in;
880 switch (filter)
882 case WINED3D_TEXF_LINEAR:
883 gl_filter = GL_LINEAR;
884 break;
886 default:
887 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
888 case WINED3D_TEXF_NONE:
889 case WINED3D_TEXF_POINT:
890 gl_filter = GL_NEAREST;
891 break;
894 /* Resolve the source surface first if needed. */
895 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
896 && (src_surface->resource.format->id != dst_surface->resource.format->id
897 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
898 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
899 src_location = WINED3D_LOCATION_RB_RESOLVED;
901 /* Make sure the locations are up-to-date. Loading the destination
902 * surface isn't required if the entire surface is overwritten. (And is
903 * in fact harmful if we're being called by surface_load_location() with
904 * the purpose of loading the destination surface.) */
905 surface_load_location(src_surface, src_location);
906 if (!surface_is_full_rect(dst_surface, &dst_rect))
907 surface_load_location(dst_surface, dst_location);
909 if (src_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, src_surface);
910 else if (dst_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, dst_surface);
911 else context = context_acquire(device, NULL);
913 if (!context->valid)
915 context_release(context);
916 WARN("Invalid context, skipping blit.\n");
917 return;
920 gl_info = context->gl_info;
922 if (src_location == WINED3D_LOCATION_DRAWABLE)
924 TRACE("Source surface %p is onscreen.\n", src_surface);
925 buffer = surface_get_gl_buffer(src_surface);
926 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
928 else
930 TRACE("Source surface %p is offscreen.\n", src_surface);
931 buffer = GL_COLOR_ATTACHMENT0;
934 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
935 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
936 checkGLcall("glReadBuffer()");
937 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
939 if (dst_location == WINED3D_LOCATION_DRAWABLE)
941 TRACE("Destination surface %p is onscreen.\n", dst_surface);
942 buffer = surface_get_gl_buffer(dst_surface);
943 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
945 else
947 TRACE("Destination surface %p is offscreen.\n", dst_surface);
948 buffer = GL_COLOR_ATTACHMENT0;
951 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
952 context_set_draw_buffer(context, buffer);
953 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
954 context_invalidate_state(context, STATE_FRAMEBUFFER);
956 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
957 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
958 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
959 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
960 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
962 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
963 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
965 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
966 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
967 checkGLcall("glBlitFramebuffer()");
969 if (wined3d_settings.strict_draw_ordering
970 || (dst_location == WINED3D_LOCATION_DRAWABLE
971 && dst_surface->container->swapchain->front_buffer == dst_surface))
972 gl_info->gl_ops.gl.p_glFlush();
974 context_release(context);
977 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
978 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
979 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
981 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
982 return FALSE;
984 /* Source and/or destination need to be on the GL side */
985 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
986 return FALSE;
988 switch (blit_op)
990 case WINED3D_BLIT_OP_COLOR_BLIT:
991 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
992 return FALSE;
993 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
994 return FALSE;
995 break;
997 case WINED3D_BLIT_OP_DEPTH_BLIT:
998 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
999 return FALSE;
1000 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1001 return FALSE;
1002 break;
1004 default:
1005 return FALSE;
1008 if (!(src_format->id == dst_format->id
1009 || (is_identity_fixup(src_format->color_fixup)
1010 && is_identity_fixup(dst_format->color_fixup))))
1011 return FALSE;
1013 return TRUE;
1016 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1017 DWORD color, struct wined3d_color *float_color)
1019 const struct wined3d_format *format = surface->resource.format;
1020 const struct wined3d_palette *palette;
1022 switch (format->id)
1024 case WINED3DFMT_P8_UINT:
1025 palette = surface->container->swapchain ? surface->container->swapchain->palette : NULL;
1027 if (palette)
1029 float_color->r = palette->colors[color].rgbRed / 255.0f;
1030 float_color->g = palette->colors[color].rgbGreen / 255.0f;
1031 float_color->b = palette->colors[color].rgbBlue / 255.0f;
1033 else
1035 float_color->r = 0.0f;
1036 float_color->g = 0.0f;
1037 float_color->b = 0.0f;
1039 float_color->a = color / 255.0f;
1040 break;
1042 case WINED3DFMT_B5G6R5_UNORM:
1043 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1044 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1045 float_color->b = (color & 0x1f) / 31.0f;
1046 float_color->a = 1.0f;
1047 break;
1049 case WINED3DFMT_B8G8R8_UNORM:
1050 case WINED3DFMT_B8G8R8X8_UNORM:
1051 float_color->r = D3DCOLOR_R(color);
1052 float_color->g = D3DCOLOR_G(color);
1053 float_color->b = D3DCOLOR_B(color);
1054 float_color->a = 1.0f;
1055 break;
1057 case WINED3DFMT_B8G8R8A8_UNORM:
1058 float_color->r = D3DCOLOR_R(color);
1059 float_color->g = D3DCOLOR_G(color);
1060 float_color->b = D3DCOLOR_B(color);
1061 float_color->a = D3DCOLOR_A(color);
1062 break;
1064 default:
1065 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1066 return FALSE;
1069 return TRUE;
1072 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1074 const struct wined3d_format *format = surface->resource.format;
1076 switch (format->id)
1078 case WINED3DFMT_S1_UINT_D15_UNORM:
1079 *float_depth = depth / (float)0x00007fff;
1080 break;
1082 case WINED3DFMT_D16_UNORM:
1083 *float_depth = depth / (float)0x0000ffff;
1084 break;
1086 case WINED3DFMT_D24_UNORM_S8_UINT:
1087 case WINED3DFMT_X8D24_UNORM:
1088 *float_depth = depth / (float)0x00ffffff;
1089 break;
1091 case WINED3DFMT_D32_UNORM:
1092 *float_depth = depth / (float)0xffffffff;
1093 break;
1095 default:
1096 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1097 return FALSE;
1100 return TRUE;
1103 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1105 const struct wined3d_resource *resource = &surface->resource;
1106 struct wined3d_device *device = resource->device;
1107 const struct blit_shader *blitter;
1109 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1110 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1111 if (!blitter)
1113 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1114 return WINED3DERR_INVALIDCALL;
1117 return blitter->depth_fill(device, surface, rect, depth);
1120 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1121 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1123 struct wined3d_device *device = src_surface->resource.device;
1125 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1126 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1127 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1128 return WINED3DERR_INVALIDCALL;
1130 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1132 surface_modify_ds_location(dst_surface, dst_location,
1133 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1135 return WINED3D_OK;
1138 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1139 struct wined3d_surface *render_target)
1141 TRACE("surface %p, render_target %p.\n", surface, render_target);
1143 /* TODO: Check surface sizes, pools, etc. */
1145 if (render_target->resource.multisample_type)
1146 return WINED3DERR_INVALIDCALL;
1148 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1151 /* Context activation is done by the caller. */
1152 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1154 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1155 checkGLcall("glDeleteBuffersARB(1, &surface->pbo)");
1157 surface->pbo = 0;
1158 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1161 static void surface_unload(struct wined3d_resource *resource)
1163 struct wined3d_surface *surface = surface_from_resource(resource);
1164 struct wined3d_renderbuffer_entry *entry, *entry2;
1165 struct wined3d_device *device = resource->device;
1166 const struct wined3d_gl_info *gl_info;
1167 struct wined3d_context *context;
1169 TRACE("surface %p.\n", surface);
1171 if (resource->pool == WINED3D_POOL_DEFAULT)
1173 /* Default pool resources are supposed to be destroyed before Reset is called.
1174 * Implicit resources stay however. So this means we have an implicit render target
1175 * or depth stencil. The content may be destroyed, but we still have to tear down
1176 * opengl resources, so we cannot leave early.
1178 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1179 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1180 * or the depth stencil into an FBO the texture or render buffer will be removed
1181 * and all flags get lost */
1182 surface_prepare_system_memory(surface);
1183 memset(surface->resource.heap_memory, 0, surface->resource.size);
1184 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1185 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1187 /* We also get here when the ddraw swapchain is destroyed, for example
1188 * for a mode switch. In this case this surface won't necessarily be
1189 * an implicit surface. We have to mark it lost so that the
1190 * application can restore it after the mode switch. */
1191 surface->flags |= SFLAG_LOST;
1193 else
1195 surface_prepare_map_memory(surface);
1196 surface_load_location(surface, surface->resource.map_binding);
1197 surface_invalidate_location(surface, ~surface->resource.map_binding);
1199 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1201 context = context_acquire(device, NULL);
1202 gl_info = context->gl_info;
1204 /* Destroy PBOs, but load them into real sysmem before */
1205 if (surface->pbo)
1206 surface_remove_pbo(surface, gl_info);
1208 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1209 * all application-created targets the application has to release the surface
1210 * before calling _Reset
1212 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1214 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1215 list_remove(&entry->entry);
1216 HeapFree(GetProcessHeap(), 0, entry);
1218 list_init(&surface->renderbuffers);
1219 surface->current_renderbuffer = NULL;
1221 if (surface->rb_multisample)
1223 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1224 surface->rb_multisample = 0;
1226 if (surface->rb_resolved)
1228 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1229 surface->rb_resolved = 0;
1232 context_release(context);
1234 resource_unload(resource);
1237 static const struct wined3d_resource_ops surface_resource_ops =
1239 surface_unload,
1242 static const struct wined3d_surface_ops surface_ops =
1244 surface_private_setup,
1245 surface_unmap,
1248 /*****************************************************************************
1249 * Initializes the GDI surface, aka creates the DIB section we render to
1250 * The DIB section creation is done by calling GetDC, which will create the
1251 * section and releasing the dc to allow the app to use it. The dib section
1252 * will stay until the surface is released
1254 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1255 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1256 * avoid confusion in the shared surface code.
1258 * Returns:
1259 * WINED3D_OK on success
1260 * The return values of called methods on failure
1262 *****************************************************************************/
1263 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1265 HRESULT hr;
1267 TRACE("surface %p.\n", surface);
1269 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1271 ERR("Overlays not yet supported by GDI surfaces.\n");
1272 return WINED3DERR_INVALIDCALL;
1275 /* Sysmem textures have memory already allocated - release it,
1276 * this avoids an unnecessary memcpy. */
1277 hr = surface_create_dib_section(surface);
1278 if (FAILED(hr))
1279 return hr;
1280 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1282 /* We don't mind the nonpow2 stuff in GDI. */
1283 surface->pow2Width = surface->resource.width;
1284 surface->pow2Height = surface->resource.height;
1286 return WINED3D_OK;
1289 static void gdi_surface_unmap(struct wined3d_surface *surface)
1291 TRACE("surface %p.\n", surface);
1293 /* Tell the swapchain to update the screen. */
1294 if (surface->container->swapchain && surface == surface->container->swapchain->front_buffer)
1295 x11_copy_to_screen(surface->container->swapchain, &surface->lockedRect);
1297 memset(&surface->lockedRect, 0, sizeof(RECT));
1300 static const struct wined3d_surface_ops gdi_surface_ops =
1302 gdi_surface_private_setup,
1303 gdi_surface_unmap,
1306 /* This call just downloads data, the caller is responsible for binding the
1307 * correct texture. */
1308 /* Context activation is done by the caller. */
1309 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1310 DWORD dst_location)
1312 const struct wined3d_format *format = surface->resource.format;
1313 struct wined3d_bo_address data;
1315 /* Only support read back of converted P8 surfaces. */
1316 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1318 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1319 return;
1322 surface_get_memory(surface, &data, dst_location);
1324 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1326 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
1327 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1329 if (data.buffer_object)
1331 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1332 checkGLcall("glBindBufferARB");
1333 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
1334 checkGLcall("glGetCompressedTexImageARB");
1335 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1336 checkGLcall("glBindBufferARB");
1338 else
1340 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
1341 surface->texture_level, data.addr));
1342 checkGLcall("glGetCompressedTexImageARB");
1345 else
1347 void *mem;
1348 GLenum gl_format = format->glFormat;
1349 GLenum gl_type = format->glType;
1350 int src_pitch = 0;
1351 int dst_pitch = 0;
1353 if (surface->flags & SFLAG_NONPOW2)
1355 unsigned char alignment = surface->resource.device->surface_alignment;
1356 src_pitch = format->byte_count * surface->pow2Width;
1357 dst_pitch = wined3d_surface_get_pitch(surface);
1358 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1359 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1361 else
1363 mem = data.addr;
1366 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1367 surface, surface->texture_level, gl_format, gl_type, mem);
1369 if (data.buffer_object)
1371 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1372 checkGLcall("glBindBufferARB");
1374 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1375 gl_format, gl_type, NULL);
1376 checkGLcall("glGetTexImage");
1378 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1379 checkGLcall("glBindBufferARB");
1381 else
1383 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1384 gl_format, gl_type, mem);
1385 checkGLcall("glGetTexImage");
1388 if (surface->flags & SFLAG_NONPOW2)
1390 const BYTE *src_data;
1391 BYTE *dst_data;
1392 UINT y;
1394 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1395 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1396 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1398 * We're doing this...
1400 * instead of boxing the texture :
1401 * |<-texture width ->| -->pow2width| /\
1402 * |111111111111111111| | |
1403 * |222 Texture 222222| boxed empty | texture height
1404 * |3333 Data 33333333| | |
1405 * |444444444444444444| | \/
1406 * ----------------------------------- |
1407 * | boxed empty | boxed empty | pow2height
1408 * | | | \/
1409 * -----------------------------------
1412 * we're repacking the data to the expected texture width
1414 * |<-texture width ->| -->pow2width| /\
1415 * |111111111111111111222222222222222| |
1416 * |222333333333333333333444444444444| texture height
1417 * |444444 | |
1418 * | | \/
1419 * | | |
1420 * | empty | pow2height
1421 * | | \/
1422 * -----------------------------------
1424 * == is the same as
1426 * |<-texture width ->| /\
1427 * |111111111111111111|
1428 * |222222222222222222|texture height
1429 * |333333333333333333|
1430 * |444444444444444444| \/
1431 * --------------------
1433 * This also means that any references to surface memory should work with the data as if it were a
1434 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1436 * internally the texture is still stored in a boxed format so any references to textureName will
1437 * get a boxed texture with width pow2width and not a texture of width resource.width.
1439 * Performance should not be an issue, because applications normally do not lock the surfaces when
1440 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
1441 * and doesn't have to be re-read. */
1442 src_data = mem;
1443 dst_data = data.addr;
1444 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1445 for (y = 0; y < surface->resource.height; ++y)
1447 memcpy(dst_data, src_data, dst_pitch);
1448 src_data += src_pitch;
1449 dst_data += dst_pitch;
1452 HeapFree(GetProcessHeap(), 0, mem);
1457 /* This call just uploads data, the caller is responsible for binding the
1458 * correct texture. */
1459 /* Context activation is done by the caller. */
1460 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1461 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1462 BOOL srgb, const struct wined3d_bo_address *data)
1464 UINT update_w = src_rect->right - src_rect->left;
1465 UINT update_h = src_rect->bottom - src_rect->top;
1467 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1468 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1469 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1471 if (surface->resource.map_count)
1473 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
1474 surface->flags |= SFLAG_PIN_SYSMEM;
1477 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1479 update_h *= format->height_scale.numerator;
1480 update_h /= format->height_scale.denominator;
1483 if (data->buffer_object)
1485 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
1486 checkGLcall("glBindBufferARB");
1489 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1491 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1492 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1493 const BYTE *addr = data->addr;
1494 GLenum internal;
1496 addr += (src_rect->top / format->block_height) * src_pitch;
1497 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1499 if (srgb)
1500 internal = format->glGammaInternal;
1501 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1502 && wined3d_resource_is_offscreen(&surface->resource))
1503 internal = format->rtInternal;
1504 else
1505 internal = format->glInternal;
1507 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
1508 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1509 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1511 if (row_length == src_pitch)
1513 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1514 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1516 else
1518 UINT row, y;
1520 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
1521 * can't use the unpack row length like below. */
1522 for (row = 0, y = dst_point->y; row < row_count; ++row)
1524 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1525 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1526 y += format->block_height;
1527 addr += src_pitch;
1530 checkGLcall("glCompressedTexSubImage2DARB");
1532 else
1534 const BYTE *addr = data->addr;
1536 addr += src_rect->top * src_pitch;
1537 addr += src_rect->left * format->byte_count;
1539 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1540 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1541 update_w, update_h, format->glFormat, format->glType, addr);
1543 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1544 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1545 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1546 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1547 checkGLcall("glTexSubImage2D");
1550 if (data->buffer_object)
1552 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1553 checkGLcall("glBindBufferARB");
1556 if (wined3d_settings.strict_draw_ordering)
1557 gl_info->gl_ops.gl.p_glFlush();
1559 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1561 struct wined3d_device *device = surface->resource.device;
1562 unsigned int i;
1564 for (i = 0; i < device->context_count; ++i)
1566 context_surface_update(device->contexts[i], surface);
1571 static HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck, BOOL use_texturing,
1572 struct wined3d_format *format, enum wined3d_conversion_type *conversion_type)
1574 BOOL colorkey_active = need_alpha_ck && (surface->container->color_key_flags & WINEDDSD_CKSRCBLT);
1575 const struct wined3d_device *device = surface->resource.device;
1576 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1577 BOOL blit_supported = FALSE;
1579 /* Copy the default values from the surface. Below we might perform fixups */
1580 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
1581 *format = *surface->resource.format;
1582 *conversion_type = WINED3D_CT_NONE;
1584 /* Ok, now look if we have to do any conversion */
1585 switch (surface->resource.format->id)
1587 case WINED3DFMT_P8_UINT:
1588 /* Below the call to blit_supported is disabled for Wine 1.2
1589 * because the function isn't operating correctly yet. At the
1590 * moment 8-bit blits are handled in software and if certain GL
1591 * extensions are around, surface conversion is performed at
1592 * upload time. The blit_supported call recognizes it as a
1593 * destination fixup. This type of upload 'fixup' and 8-bit to
1594 * 8-bit blits need to be handled by the blit_shader.
1595 * TODO: get rid of this #if 0. */
1596 #if 0
1597 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1598 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
1599 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
1600 #endif
1601 blit_supported = gl_info->supported[ARB_FRAGMENT_PROGRAM];
1603 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
1604 * texturing. Further also use conversion in case of color keying.
1605 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1606 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1607 * conflicts with this.
1609 if (!((blit_supported && surface->container->swapchain
1610 && surface == surface->container->swapchain->front_buffer))
1611 || colorkey_active || !use_texturing)
1613 format->glFormat = GL_RGBA;
1614 format->glInternal = GL_RGBA;
1615 format->glType = GL_UNSIGNED_BYTE;
1616 format->conv_byte_count = 4;
1617 *conversion_type = WINED3D_CT_PALETTED;
1619 break;
1621 case WINED3DFMT_B2G3R3_UNORM:
1622 /* **********************
1623 GL_UNSIGNED_BYTE_3_3_2
1624 ********************** */
1625 if (colorkey_active) {
1626 /* This texture format will never be used.. So do not care about color keying
1627 up until the point in time it will be needed :-) */
1628 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1630 break;
1632 case WINED3DFMT_B5G6R5_UNORM:
1633 if (colorkey_active)
1635 *conversion_type = WINED3D_CT_CK_565;
1636 format->glFormat = GL_RGBA;
1637 format->glInternal = GL_RGB5_A1;
1638 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
1639 format->conv_byte_count = 2;
1641 break;
1643 case WINED3DFMT_B5G5R5X1_UNORM:
1644 if (colorkey_active)
1646 *conversion_type = WINED3D_CT_CK_5551;
1647 format->glFormat = GL_BGRA;
1648 format->glInternal = GL_RGB5_A1;
1649 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1650 format->conv_byte_count = 2;
1652 break;
1654 case WINED3DFMT_B8G8R8_UNORM:
1655 if (colorkey_active)
1657 *conversion_type = WINED3D_CT_CK_RGB24;
1658 format->glFormat = GL_RGBA;
1659 format->glInternal = GL_RGBA8;
1660 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1661 format->conv_byte_count = 4;
1663 break;
1665 case WINED3DFMT_B8G8R8X8_UNORM:
1666 if (colorkey_active)
1668 *conversion_type = WINED3D_CT_RGB32_888;
1669 format->glFormat = GL_RGBA;
1670 format->glInternal = GL_RGBA8;
1671 format->glType = GL_UNSIGNED_INT_8_8_8_8;
1672 format->conv_byte_count = 4;
1674 break;
1676 case WINED3DFMT_B8G8R8A8_UNORM:
1677 if (colorkey_active)
1679 *conversion_type = WINED3D_CT_CK_ARGB32;
1680 format->conv_byte_count = 4;
1682 break;
1684 default:
1685 break;
1688 if (*conversion_type != WINED3D_CT_NONE)
1690 format->rtInternal = format->glInternal;
1691 format->glGammaInternal = format->glInternal;
1694 return WINED3D_OK;
1697 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
1699 UINT width_mask, height_mask;
1701 if (!rect->left && !rect->top
1702 && rect->right == surface->resource.width
1703 && rect->bottom == surface->resource.height)
1704 return TRUE;
1706 /* This assumes power of two block sizes, but NPOT block sizes would be
1707 * silly anyway. */
1708 width_mask = surface->resource.format->block_width - 1;
1709 height_mask = surface->resource.format->block_height - 1;
1711 if (!(rect->left & width_mask) && !(rect->top & height_mask)
1712 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
1713 return TRUE;
1715 return FALSE;
1718 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1719 struct wined3d_surface *src_surface, const RECT *src_rect)
1721 const struct wined3d_format *src_format;
1722 const struct wined3d_format *dst_format;
1723 const struct wined3d_gl_info *gl_info;
1724 enum wined3d_conversion_type convert;
1725 struct wined3d_context *context;
1726 struct wined3d_bo_address data;
1727 struct wined3d_format format;
1728 UINT update_w, update_h;
1729 UINT dst_w, dst_h;
1730 RECT r, dst_rect;
1731 UINT src_pitch;
1732 POINT p;
1734 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1735 dst_surface, wine_dbgstr_point(dst_point),
1736 src_surface, wine_dbgstr_rect(src_rect));
1738 src_format = src_surface->resource.format;
1739 dst_format = dst_surface->resource.format;
1741 if (src_format->id != dst_format->id)
1743 WARN("Source and destination surfaces should have the same format.\n");
1744 return WINED3DERR_INVALIDCALL;
1747 if (!dst_point)
1749 p.x = 0;
1750 p.y = 0;
1751 dst_point = &p;
1753 else if (dst_point->x < 0 || dst_point->y < 0)
1755 WARN("Invalid destination point.\n");
1756 return WINED3DERR_INVALIDCALL;
1759 if (!src_rect)
1761 r.left = 0;
1762 r.top = 0;
1763 r.right = src_surface->resource.width;
1764 r.bottom = src_surface->resource.height;
1765 src_rect = &r;
1767 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1768 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1770 WARN("Invalid source rectangle.\n");
1771 return WINED3DERR_INVALIDCALL;
1774 dst_w = dst_surface->resource.width;
1775 dst_h = dst_surface->resource.height;
1777 update_w = src_rect->right - src_rect->left;
1778 update_h = src_rect->bottom - src_rect->top;
1780 if (update_w > dst_w || dst_point->x > dst_w - update_w
1781 || update_h > dst_h || dst_point->y > dst_h - update_h)
1783 WARN("Destination out of bounds.\n");
1784 return WINED3DERR_INVALIDCALL;
1787 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
1789 WARN("Source rectangle not block-aligned.\n");
1790 return WINED3DERR_INVALIDCALL;
1793 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1794 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
1796 WARN("Destination rectangle not block-aligned.\n");
1797 return WINED3DERR_INVALIDCALL;
1800 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1801 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
1802 if (convert != WINED3D_CT_NONE || format.convert)
1803 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1805 context = context_acquire(dst_surface->resource.device, NULL);
1806 gl_info = context->gl_info;
1808 /* Only load the surface for partial updates. For newly allocated texture
1809 * the texture wouldn't be the current location, and we'd upload zeroes
1810 * just to overwrite them again. */
1811 if (update_w == dst_w && update_h == dst_h)
1812 surface_prepare_texture(dst_surface, context, FALSE);
1813 else
1814 surface_load_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1815 wined3d_texture_bind(dst_surface->container, context, FALSE);
1817 surface_get_memory(src_surface, &data, src_surface->locations);
1818 src_pitch = wined3d_surface_get_pitch(src_surface);
1820 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
1822 context_invalidate_active_texture(context);
1824 context_release(context);
1826 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1827 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1829 return WINED3D_OK;
1832 /* This call just allocates the texture, the caller is responsible for binding
1833 * the correct texture. */
1834 /* Context activation is done by the caller. */
1835 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1836 const struct wined3d_format *format, BOOL srgb)
1838 BOOL disable_client_storage = FALSE;
1839 GLsizei width = surface->pow2Width;
1840 GLsizei height = surface->pow2Height;
1841 const BYTE *mem = NULL;
1842 GLenum internal;
1844 if (srgb)
1845 internal = format->glGammaInternal;
1846 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1847 && wined3d_resource_is_offscreen(&surface->resource))
1848 internal = format->rtInternal;
1849 else
1850 internal = format->glInternal;
1852 if (!internal)
1853 FIXME("No GL internal format for format %s.\n", debug_d3dformat(format->id));
1855 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1857 height *= format->height_scale.numerator;
1858 height /= format->height_scale.denominator;
1861 TRACE("(%p) : Creating surface (target %#x) level %d, d3d format %s, internal format %#x, width %d, height %d, gl format %#x, gl type=%#x\n",
1862 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
1863 internal, width, height, format->glFormat, format->glType);
1865 if (gl_info->supported[APPLE_CLIENT_STORAGE])
1867 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
1868 || !surface->resource.heap_memory)
1870 /* In some cases we want to disable client storage.
1871 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
1872 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
1873 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
1874 * heap_memory == NULL: Not defined in the extension. Seems to disable client storage effectively
1876 surface->flags &= ~SFLAG_CLIENT;
1878 else
1880 surface->flags |= SFLAG_CLIENT;
1881 mem = surface->resource.heap_memory;
1883 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
1884 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
1885 disable_client_storage = TRUE;
1889 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
1891 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
1892 internal, width, height, 0, surface->resource.size, mem));
1893 checkGLcall("glCompressedTexImage2DARB");
1895 else
1897 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
1898 internal, width, height, 0, format->glFormat, format->glType, mem);
1899 checkGLcall("glTexImage2D");
1902 if (disable_client_storage)
1904 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
1905 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
1909 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1910 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1911 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1912 /* Context activation is done by the caller. */
1913 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1915 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1916 struct wined3d_renderbuffer_entry *entry;
1917 GLuint renderbuffer = 0;
1918 unsigned int src_width, src_height;
1919 unsigned int width, height;
1921 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1923 width = rt->pow2Width;
1924 height = rt->pow2Height;
1926 else
1928 width = surface->pow2Width;
1929 height = surface->pow2Height;
1932 src_width = surface->pow2Width;
1933 src_height = surface->pow2Height;
1935 /* A depth stencil smaller than the render target is not valid */
1936 if (width > src_width || height > src_height) return;
1938 /* Remove any renderbuffer set if the sizes match */
1939 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1940 || (width == src_width && height == src_height))
1942 surface->current_renderbuffer = NULL;
1943 return;
1946 /* Look if we've already got a renderbuffer of the correct dimensions */
1947 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1949 if (entry->width == width && entry->height == height)
1951 renderbuffer = entry->id;
1952 surface->current_renderbuffer = entry;
1953 break;
1957 if (!renderbuffer)
1959 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1960 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1961 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1962 surface->resource.format->glInternal, width, height);
1964 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1965 entry->width = width;
1966 entry->height = height;
1967 entry->id = renderbuffer;
1968 list_add_head(&surface->renderbuffers, &entry->entry);
1970 surface->current_renderbuffer = entry;
1973 checkGLcall("set_compatible_renderbuffer");
1976 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1978 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1980 TRACE("surface %p.\n", surface);
1982 if (!swapchain)
1984 ERR("Surface %p is not on a swapchain.\n", surface);
1985 return GL_NONE;
1988 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
1990 if (swapchain->render_to_fbo)
1992 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1993 return GL_COLOR_ATTACHMENT0;
1995 TRACE("Returning GL_BACK\n");
1996 return GL_BACK;
1998 else if (surface == swapchain->front_buffer)
2000 TRACE("Returning GL_FRONT\n");
2001 return GL_FRONT;
2004 FIXME("Higher back buffer, returning GL_BACK\n");
2005 return GL_BACK;
2008 void surface_load(struct wined3d_surface *surface, BOOL srgb)
2010 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2011 BOOL ck_changed;
2013 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2015 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
2016 ERR("Not supported on scratch surfaces.\n");
2018 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->container->color_key_flags & WINEDDSD_CKSRCBLT);
2020 /* Reload if either the texture and sysmem have different ideas about the
2021 * color key, or the actual key values changed. */
2022 if (ck_changed || ((surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
2023 && (surface->gl_color_key.color_space_low_value
2024 != surface->container->src_blt_color_key.color_space_low_value
2025 || surface->gl_color_key.color_space_high_value
2026 != surface->container->src_blt_color_key.color_space_high_value)))
2028 TRACE("Reloading because of color keying\n");
2029 /* To perform the color key conversion we need a sysmem copy of
2030 * the surface. Make sure we have it. */
2032 surface_prepare_map_memory(surface);
2033 surface_load_location(surface, surface->resource.map_binding);
2034 surface_invalidate_location(surface, ~surface->resource.map_binding);
2035 /* Switching color keying on / off may change the internal format. */
2036 if (ck_changed)
2037 surface_force_reload(surface);
2039 else if (!(surface->locations & location))
2041 TRACE("Reloading because surface is dirty.\n");
2043 else
2045 TRACE("surface is already in texture\n");
2046 return;
2049 surface_load_location(surface, location);
2050 surface_evict_sysmem(surface);
2053 /* See also float_16_to_32() in wined3d_private.h */
2054 static inline unsigned short float_32_to_16(const float *in)
2056 int exp = 0;
2057 float tmp = fabsf(*in);
2058 unsigned int mantissa;
2059 unsigned short ret;
2061 /* Deal with special numbers */
2062 if (*in == 0.0f)
2063 return 0x0000;
2064 if (isnan(*in))
2065 return 0x7c01;
2066 if (isinf(*in))
2067 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2069 if (tmp < powf(2, 10))
2073 tmp = tmp * 2.0f;
2074 exp--;
2075 } while (tmp < powf(2, 10));
2077 else if (tmp >= powf(2, 11))
2081 tmp /= 2.0f;
2082 exp++;
2083 } while (tmp >= powf(2, 11));
2086 mantissa = (unsigned int)tmp;
2087 if (tmp - mantissa >= 0.5f)
2088 ++mantissa; /* Round to nearest, away from zero. */
2090 exp += 10; /* Normalize the mantissa. */
2091 exp += 15; /* Exponent is encoded with excess 15. */
2093 if (exp > 30) /* too big */
2095 ret = 0x7c00; /* INF */
2097 else if (exp <= 0)
2099 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2100 while (exp <= 0)
2102 mantissa = mantissa >> 1;
2103 ++exp;
2105 ret = mantissa & 0x3ff;
2107 else
2109 ret = (exp << 10) | (mantissa & 0x3ff);
2112 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2113 return ret;
2116 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2118 TRACE("surface %p, container %p.\n", surface, surface->container);
2120 return wined3d_texture_incref(surface->container);
2123 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2125 TRACE("surface %p, container %p.\n", surface, surface->container);
2127 return wined3d_texture_decref(surface->container);
2130 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2132 TRACE("surface %p.\n", surface);
2134 if (!surface->resource.device->d3d_initialized)
2136 ERR("D3D not initialized.\n");
2137 return;
2140 wined3d_texture_preload(surface->container);
2143 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2145 TRACE("surface %p.\n", surface);
2147 return surface->resource.parent;
2150 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2152 TRACE("surface %p.\n", surface);
2154 return &surface->resource;
2157 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2159 TRACE("surface %p, flags %#x.\n", surface, flags);
2161 switch (flags)
2163 case WINEDDGBS_CANBLT:
2164 case WINEDDGBS_ISBLTDONE:
2165 return WINED3D_OK;
2167 default:
2168 return WINED3DERR_INVALIDCALL;
2172 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2174 TRACE("surface %p, flags %#x.\n", surface, flags);
2176 /* XXX: DDERR_INVALIDSURFACETYPE */
2178 switch (flags)
2180 case WINEDDGFS_CANFLIP:
2181 case WINEDDGFS_ISFLIPDONE:
2182 return WINED3D_OK;
2184 default:
2185 return WINED3DERR_INVALIDCALL;
2189 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2191 TRACE("surface %p.\n", surface);
2193 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2194 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2197 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2199 TRACE("surface %p.\n", surface);
2201 surface->flags &= ~SFLAG_LOST;
2202 return WINED3D_OK;
2205 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2207 unsigned int alignment;
2208 DWORD pitch;
2210 TRACE("surface %p.\n", surface);
2212 if (surface->pitch)
2213 return surface->pitch;
2215 alignment = surface->resource.device->surface_alignment;
2216 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
2217 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2219 TRACE("Returning %u.\n", pitch);
2221 return pitch;
2224 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2226 LONG w, h;
2228 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2230 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2232 WARN("Not an overlay surface.\n");
2233 return WINEDDERR_NOTAOVERLAYSURFACE;
2236 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2237 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2238 surface->overlay_destrect.left = x;
2239 surface->overlay_destrect.top = y;
2240 surface->overlay_destrect.right = x + w;
2241 surface->overlay_destrect.bottom = y + h;
2243 return WINED3D_OK;
2246 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2248 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2250 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2252 TRACE("Not an overlay surface.\n");
2253 return WINEDDERR_NOTAOVERLAYSURFACE;
2256 if (!surface->overlay_dest)
2258 TRACE("Overlay not visible.\n");
2259 *x = 0;
2260 *y = 0;
2261 return WINEDDERR_OVERLAYNOTVISIBLE;
2264 *x = surface->overlay_destrect.left;
2265 *y = surface->overlay_destrect.top;
2267 TRACE("Returning position %d, %d.\n", *x, *y);
2269 return WINED3D_OK;
2272 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2273 DWORD flags, struct wined3d_surface *ref)
2275 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2277 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2279 TRACE("Not an overlay surface.\n");
2280 return WINEDDERR_NOTAOVERLAYSURFACE;
2283 return WINED3D_OK;
2286 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2287 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2289 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2290 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2292 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2294 WARN("Not an overlay surface.\n");
2295 return WINEDDERR_NOTAOVERLAYSURFACE;
2297 else if (!dst_surface)
2299 WARN("Dest surface is NULL.\n");
2300 return WINED3DERR_INVALIDCALL;
2303 if (src_rect)
2305 surface->overlay_srcrect = *src_rect;
2307 else
2309 surface->overlay_srcrect.left = 0;
2310 surface->overlay_srcrect.top = 0;
2311 surface->overlay_srcrect.right = surface->resource.width;
2312 surface->overlay_srcrect.bottom = surface->resource.height;
2315 if (dst_rect)
2317 surface->overlay_destrect = *dst_rect;
2319 else
2321 surface->overlay_destrect.left = 0;
2322 surface->overlay_destrect.top = 0;
2323 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2324 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2327 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2329 surface->overlay_dest = NULL;
2330 list_remove(&surface->overlay_entry);
2333 if (flags & WINEDDOVER_SHOW)
2335 if (surface->overlay_dest != dst_surface)
2337 surface->overlay_dest = dst_surface;
2338 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2341 else if (flags & WINEDDOVER_HIDE)
2343 /* tests show that the rectangles are erased on hide */
2344 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2345 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2346 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2347 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2348 surface->overlay_dest = NULL;
2351 return WINED3D_OK;
2354 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
2355 UINT width, UINT height, enum wined3d_format_id format_id,
2356 enum wined3d_multisample_type multisample_type, UINT multisample_quality,
2357 void *mem, UINT pitch)
2359 struct wined3d_device *device = surface->resource.device;
2360 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2361 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
2362 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height, 1);
2363 BOOL create_dib = FALSE;
2364 HRESULT hr;
2365 DWORD valid_location = 0;
2367 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u, "
2368 "mem %p, pitch %u.\n",
2369 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type, mem, pitch);
2371 if (!resource_size)
2372 return WINED3DERR_INVALIDCALL;
2374 if (surface->resource.map_count || (surface->flags & SFLAG_DCINUSE))
2376 WARN("Surface is mapped or the DC is in use.\n");
2377 return WINED3DERR_INVALIDCALL;
2380 if (device->d3d_initialized)
2381 surface->resource.resource_ops->resource_unload(&surface->resource);
2383 if (surface->flags & SFLAG_DIBSECTION)
2385 DeleteDC(surface->hDC);
2386 DeleteObject(surface->dib.DIBsection);
2387 surface->dib.bitmap_data = NULL;
2388 surface->flags &= ~SFLAG_DIBSECTION;
2389 create_dib = TRUE;
2392 surface->locations = 0;
2393 wined3d_resource_free_sysmem(&surface->resource);
2395 surface->resource.width = width;
2396 surface->resource.height = height;
2397 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2398 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2400 surface->pow2Width = width;
2401 surface->pow2Height = height;
2403 else
2405 surface->pow2Width = surface->pow2Height = 1;
2406 while (surface->pow2Width < width)
2407 surface->pow2Width <<= 1;
2408 while (surface->pow2Height < height)
2409 surface->pow2Height <<= 1;
2412 if (surface->pow2Width != width || surface->pow2Height != height)
2413 surface->flags |= SFLAG_NONPOW2;
2414 else
2415 surface->flags &= ~SFLAG_NONPOW2;
2417 surface->user_memory = mem;
2418 if (surface->user_memory)
2420 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
2421 valid_location = WINED3D_LOCATION_USER_MEMORY;
2423 surface->pitch = pitch;
2424 surface->resource.format = format;
2425 surface->resource.multisample_type = multisample_type;
2426 surface->resource.multisample_quality = multisample_quality;
2427 if (surface->pitch)
2428 surface->resource.size = height * surface->pitch;
2429 else
2430 surface->resource.size = resource_size;
2432 /* The format might be changed to a format that needs conversion.
2433 * If the surface didn't use PBOs previously but could now, don't
2434 * change it - whatever made us not use PBOs might come back, e.g.
2435 * color keys. */
2436 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2437 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2439 if (create_dib)
2441 if (FAILED(hr = surface_create_dib_section(surface)))
2443 ERR("Failed to create dib section, hr %#x.\n", hr);
2444 return hr;
2446 if (!valid_location)
2447 valid_location = WINED3D_LOCATION_DIB;
2450 if (!valid_location)
2452 surface_prepare_system_memory(surface);
2453 valid_location = WINED3D_LOCATION_SYSMEM;
2456 surface_validate_location(surface, valid_location);
2458 return WINED3D_OK;
2461 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2462 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2464 unsigned short *dst_s;
2465 const float *src_f;
2466 unsigned int x, y;
2468 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2470 for (y = 0; y < h; ++y)
2472 src_f = (const float *)(src + y * pitch_in);
2473 dst_s = (unsigned short *) (dst + y * pitch_out);
2474 for (x = 0; x < w; ++x)
2476 dst_s[x] = float_32_to_16(src_f + x);
2481 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2482 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2484 static const unsigned char convert_5to8[] =
2486 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2487 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2488 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2489 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2491 static const unsigned char convert_6to8[] =
2493 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2494 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2495 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2496 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2497 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2498 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2499 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2500 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2502 unsigned int x, y;
2504 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2506 for (y = 0; y < h; ++y)
2508 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2509 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2510 for (x = 0; x < w; ++x)
2512 WORD pixel = src_line[x];
2513 dst_line[x] = 0xff000000
2514 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2515 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2516 | convert_5to8[(pixel & 0x001f)];
2521 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2522 * in both cases we're just setting the X / Alpha channel to 0xff. */
2523 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2524 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2526 unsigned int x, y;
2528 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2530 for (y = 0; y < h; ++y)
2532 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2533 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2535 for (x = 0; x < w; ++x)
2537 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2542 static inline BYTE cliptobyte(int x)
2544 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2547 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2548 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2550 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2551 unsigned int x, y;
2553 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2555 for (y = 0; y < h; ++y)
2557 const BYTE *src_line = src + y * pitch_in;
2558 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2559 for (x = 0; x < w; ++x)
2561 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2562 * C = Y - 16; D = U - 128; E = V - 128;
2563 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2564 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2565 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2566 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2567 * U and V are shared between the pixels. */
2568 if (!(x & 1)) /* For every even pixel, read new U and V. */
2570 d = (int) src_line[1] - 128;
2571 e = (int) src_line[3] - 128;
2572 r2 = 409 * e + 128;
2573 g2 = - 100 * d - 208 * e + 128;
2574 b2 = 516 * d + 128;
2576 c2 = 298 * ((int) src_line[0] - 16);
2577 dst_line[x] = 0xff000000
2578 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2579 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2580 | cliptobyte((c2 + b2) >> 8); /* blue */
2581 /* Scale RGB values to 0..255 range,
2582 * then clip them if still not in range (may be negative),
2583 * then shift them within DWORD if necessary. */
2584 src_line += 2;
2589 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2590 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2592 unsigned int x, y;
2593 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2595 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2597 for (y = 0; y < h; ++y)
2599 const BYTE *src_line = src + y * pitch_in;
2600 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2601 for (x = 0; x < w; ++x)
2603 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2604 * C = Y - 16; D = U - 128; E = V - 128;
2605 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2606 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2607 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2608 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2609 * U and V are shared between the pixels. */
2610 if (!(x & 1)) /* For every even pixel, read new U and V. */
2612 d = (int) src_line[1] - 128;
2613 e = (int) src_line[3] - 128;
2614 r2 = 409 * e + 128;
2615 g2 = - 100 * d - 208 * e + 128;
2616 b2 = 516 * d + 128;
2618 c2 = 298 * ((int) src_line[0] - 16);
2619 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2620 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2621 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2622 /* Scale RGB values to 0..255 range,
2623 * then clip them if still not in range (may be negative),
2624 * then shift them within DWORD if necessary. */
2625 src_line += 2;
2630 struct d3dfmt_converter_desc
2632 enum wined3d_format_id from, to;
2633 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2636 static const struct d3dfmt_converter_desc converters[] =
2638 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2639 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2640 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2641 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2642 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2643 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2646 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2647 enum wined3d_format_id to)
2649 unsigned int i;
2651 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2653 if (converters[i].from == from && converters[i].to == to)
2654 return &converters[i];
2657 return NULL;
2660 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2662 struct wined3d_map_desc src_map, dst_map;
2663 const struct d3dfmt_converter_desc *conv;
2664 struct wined3d_texture *ret = NULL;
2665 struct wined3d_resource_desc desc;
2666 struct wined3d_surface *dst;
2668 conv = find_converter(source->resource.format->id, to_fmt);
2669 if (!conv)
2671 FIXME("Cannot find a conversion function from format %s to %s.\n",
2672 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2673 return NULL;
2676 /* FIXME: Multisampled conversion? */
2677 wined3d_resource_get_desc(&source->resource, &desc);
2678 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2679 desc.format = to_fmt;
2680 desc.usage = 0;
2681 desc.pool = WINED3D_POOL_SCRATCH;
2682 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2683 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, &wined3d_null_parent_ops, &ret)))
2685 ERR("Failed to create a destination surface for conversion.\n");
2686 return NULL;
2688 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2690 memset(&src_map, 0, sizeof(src_map));
2691 memset(&dst_map, 0, sizeof(dst_map));
2693 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2695 ERR("Failed to lock the source surface.\n");
2696 wined3d_texture_decref(ret);
2697 return NULL;
2699 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2701 ERR("Failed to lock the destination surface.\n");
2702 wined3d_surface_unmap(source);
2703 wined3d_texture_decref(ret);
2704 return NULL;
2707 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2708 source->resource.width, source->resource.height);
2710 wined3d_surface_unmap(dst);
2711 wined3d_surface_unmap(source);
2713 return ret;
2716 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2717 unsigned int bpp, UINT pitch, DWORD color)
2719 BYTE *first;
2720 unsigned int x, y;
2722 /* Do first row */
2724 #define COLORFILL_ROW(type) \
2725 do { \
2726 type *d = (type *)buf; \
2727 for (x = 0; x < width; ++x) \
2728 d[x] = (type)color; \
2729 } while(0)
2731 switch (bpp)
2733 case 1:
2734 COLORFILL_ROW(BYTE);
2735 break;
2737 case 2:
2738 COLORFILL_ROW(WORD);
2739 break;
2741 case 3:
2743 BYTE *d = buf;
2744 for (x = 0; x < width; ++x, d += 3)
2746 d[0] = (color ) & 0xff;
2747 d[1] = (color >> 8) & 0xff;
2748 d[2] = (color >> 16) & 0xff;
2750 break;
2752 case 4:
2753 COLORFILL_ROW(DWORD);
2754 break;
2756 default:
2757 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2758 return WINED3DERR_NOTAVAILABLE;
2761 #undef COLORFILL_ROW
2763 /* Now copy first row. */
2764 first = buf;
2765 for (y = 1; y < height; ++y)
2767 buf += pitch;
2768 memcpy(buf, first, width * bpp);
2771 return WINED3D_OK;
2774 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2776 return surface_from_resource(resource);
2779 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2781 TRACE("surface %p.\n", surface);
2783 if (!surface->resource.map_count)
2785 WARN("Trying to unmap unmapped surface.\n");
2786 return WINEDDERR_NOTLOCKED;
2788 --surface->resource.map_count;
2790 surface->surface_ops->surface_unmap(surface);
2792 return WINED3D_OK;
2795 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2796 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
2798 const struct wined3d_format *format = surface->resource.format;
2799 struct wined3d_device *device = surface->resource.device;
2800 struct wined3d_context *context;
2801 const struct wined3d_gl_info *gl_info;
2802 BYTE *base_memory;
2804 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
2805 surface, map_desc, wine_dbgstr_rect(rect), flags);
2807 if (surface->resource.map_count)
2809 WARN("Surface is already mapped.\n");
2810 return WINED3DERR_INVALIDCALL;
2813 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
2814 && !surface_check_block_align(surface, rect))
2816 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
2817 wine_dbgstr_rect(rect), format->block_width, format->block_height);
2819 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2820 return WINED3DERR_INVALIDCALL;
2823 ++surface->resource.map_count;
2825 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2826 WARN("Trying to lock unlockable surface.\n");
2828 /* Performance optimization: Count how often a surface is mapped, if it is
2829 * mapped regularly do not throw away the system memory copy. This avoids
2830 * the need to download the surface from OpenGL all the time. The surface
2831 * is still downloaded if the OpenGL texture is changed. */
2832 if (!(surface->flags & SFLAG_DYNLOCK) && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2834 if (++surface->lockCount > MAXLOCKCOUNT)
2836 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2837 surface->flags |= SFLAG_DYNLOCK;
2841 surface_prepare_map_memory(surface);
2842 if (flags & WINED3D_MAP_DISCARD)
2844 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2845 wined3d_debug_location(surface->resource.map_binding));
2846 surface_validate_location(surface, surface->resource.map_binding);
2848 else
2850 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2851 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2853 surface_load_location(surface, surface->resource.map_binding);
2856 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2857 surface_invalidate_location(surface, ~surface->resource.map_binding);
2859 switch (surface->resource.map_binding)
2861 case WINED3D_LOCATION_SYSMEM:
2862 base_memory = surface->resource.heap_memory;
2863 break;
2865 case WINED3D_LOCATION_USER_MEMORY:
2866 base_memory = surface->user_memory;
2867 break;
2869 case WINED3D_LOCATION_DIB:
2870 base_memory = surface->dib.bitmap_data;
2871 break;
2873 case WINED3D_LOCATION_BUFFER:
2874 context = context_acquire(device, NULL);
2875 gl_info = context->gl_info;
2877 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
2878 base_memory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
2879 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2880 checkGLcall("map PBO");
2882 context_release(context);
2883 break;
2885 default:
2886 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2887 base_memory = NULL;
2890 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2891 map_desc->row_pitch = surface->resource.width * format->byte_count;
2892 else
2893 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2894 map_desc->slice_pitch = 0;
2896 if (!rect)
2898 map_desc->data = base_memory;
2899 surface->lockedRect.left = 0;
2900 surface->lockedRect.top = 0;
2901 surface->lockedRect.right = surface->resource.width;
2902 surface->lockedRect.bottom = surface->resource.height;
2904 else
2906 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2908 /* Compressed textures are block based, so calculate the offset of
2909 * the block that contains the top-left pixel of the locked rectangle. */
2910 map_desc->data = base_memory
2911 + ((rect->top / format->block_height) * map_desc->row_pitch)
2912 + ((rect->left / format->block_width) * format->block_byte_count);
2914 else
2916 map_desc->data = base_memory
2917 + (map_desc->row_pitch * rect->top)
2918 + (rect->left * format->byte_count);
2920 surface->lockedRect.left = rect->left;
2921 surface->lockedRect.top = rect->top;
2922 surface->lockedRect.right = rect->right;
2923 surface->lockedRect.bottom = rect->bottom;
2926 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2927 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2929 return WINED3D_OK;
2932 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
2934 HRESULT hr;
2936 TRACE("surface %p, dc %p.\n", surface, dc);
2938 /* Give more detailed info for ddraw. */
2939 if (surface->flags & SFLAG_DCINUSE)
2940 return WINEDDERR_DCALREADYCREATED;
2942 /* Can't GetDC if the surface is locked. */
2943 if (surface->resource.map_count)
2944 return WINED3DERR_INVALIDCALL;
2946 /* Create a DIB section if there isn't a dc yet. */
2947 if (!surface->hDC)
2949 if (surface->flags & SFLAG_CLIENT)
2951 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
2952 surface_release_client_storage(surface);
2954 hr = surface_create_dib_section(surface);
2955 if (FAILED(hr))
2956 return WINED3DERR_INVALIDCALL;
2957 if (!(surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2958 || surface->flags & SFLAG_PIN_SYSMEM
2959 || surface->pbo))
2960 surface->resource.map_binding = WINED3D_LOCATION_DIB;
2963 surface_load_location(surface, WINED3D_LOCATION_DIB);
2964 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
2966 surface->flags |= SFLAG_DCINUSE;
2967 surface->resource.map_count++;
2969 *dc = surface->hDC;
2970 TRACE("Returning dc %p.\n", *dc);
2972 return WINED3D_OK;
2975 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
2977 TRACE("surface %p, dc %p.\n", surface, dc);
2979 if (!(surface->flags & SFLAG_DCINUSE))
2980 return WINEDDERR_NODC;
2982 if (surface->hDC != dc)
2984 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
2985 dc, surface->hDC);
2986 return WINEDDERR_NODC;
2989 surface->resource.map_count--;
2990 surface->flags &= ~SFLAG_DCINUSE;
2992 if (surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY || (surface->flags & SFLAG_PIN_SYSMEM
2993 && surface->resource.map_binding != WINED3D_LOCATION_DIB))
2995 /* The game Salammbo modifies the surface contents without mapping the surface between
2996 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
2997 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
2998 * Do not only copy the DIB to the map location, but also make sure the map location is
2999 * copied back to the DIB in the next getdc call.
3001 * The same consideration applies to user memory surfaces. */
3002 surface_load_location(surface, surface->resource.map_binding);
3003 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
3006 return WINED3D_OK;
3009 static void read_from_framebuffer(struct wined3d_surface *surface, DWORD dst_location)
3011 struct wined3d_device *device = surface->resource.device;
3012 const struct wined3d_gl_info *gl_info;
3013 struct wined3d_context *context;
3014 BYTE *mem;
3015 BYTE *row, *top, *bottom;
3016 int i;
3017 BOOL srcIsUpsideDown;
3018 struct wined3d_bo_address data;
3020 surface_get_memory(surface, &data, dst_location);
3022 context = context_acquire(device, surface);
3023 context_apply_blit_state(context, device);
3024 gl_info = context->gl_info;
3026 /* Select the correct read buffer, and give some debug output.
3027 * There is no need to keep track of the current read buffer or reset it, every part of the code
3028 * that reads sets the read buffer as desired.
3030 if (wined3d_resource_is_offscreen(&surface->resource))
3032 /* Mapping the primary render target which is not on a swapchain.
3033 * Read from the back buffer. */
3034 TRACE("Mapping offscreen render target.\n");
3035 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3036 srcIsUpsideDown = TRUE;
3038 else
3040 /* Onscreen surfaces are always part of a swapchain */
3041 GLenum buffer = surface_get_gl_buffer(surface);
3042 TRACE("Mapping %#x buffer.\n", buffer);
3043 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
3044 checkGLcall("glReadBuffer");
3045 srcIsUpsideDown = FALSE;
3048 if (data.buffer_object)
3050 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
3051 checkGLcall("glBindBufferARB");
3054 /* Setup pixel store pack state -- to glReadPixels into the correct place */
3055 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
3056 checkGLcall("glPixelStorei");
3058 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
3059 surface->resource.width, surface->resource.height,
3060 surface->resource.format->glFormat,
3061 surface->resource.format->glType, data.addr);
3062 checkGLcall("glReadPixels");
3064 /* Reset previous pixel store pack state */
3065 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
3066 checkGLcall("glPixelStorei");
3068 if (!srcIsUpsideDown)
3070 /* glReadPixels returns the image upside down, and there is no way to prevent this.
3071 * Flip the lines in software. */
3072 UINT pitch = wined3d_surface_get_pitch(surface);
3074 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
3075 goto error;
3077 if (data.buffer_object)
3079 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB));
3080 checkGLcall("glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB)");
3082 else
3083 mem = data.addr;
3085 top = mem;
3086 bottom = mem + pitch * (surface->resource.height - 1);
3087 for (i = 0; i < surface->resource.height / 2; i++)
3089 memcpy(row, top, pitch);
3090 memcpy(top, bottom, pitch);
3091 memcpy(bottom, row, pitch);
3092 top += pitch;
3093 bottom -= pitch;
3095 HeapFree(GetProcessHeap(), 0, row);
3097 if (data.buffer_object)
3098 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB));
3101 error:
3102 if (data.buffer_object)
3104 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
3105 checkGLcall("glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0)");
3108 context_release(context);
3111 /* Read the framebuffer contents into a texture. Note that this function
3112 * doesn't do any kind of flipping. Using this on an onscreen surface will
3113 * result in a flipped D3D texture. */
3114 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
3116 struct wined3d_device *device = surface->resource.device;
3117 const struct wined3d_gl_info *gl_info;
3118 struct wined3d_context *context;
3120 context = context_acquire(device, surface);
3121 gl_info = context->gl_info;
3122 device_invalidate_state(device, STATE_FRAMEBUFFER);
3124 surface_prepare_texture(surface, context, srgb);
3125 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3127 TRACE("Reading back offscreen render target %p.\n", surface);
3129 if (wined3d_resource_is_offscreen(&surface->resource))
3130 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3131 else
3132 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
3133 checkGLcall("glReadBuffer");
3135 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
3136 0, 0, 0, 0, surface->resource.width, surface->resource.height);
3137 checkGLcall("glCopyTexSubImage2D");
3139 context_release(context);
3142 /* Context activation is done by the caller. */
3143 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
3144 struct wined3d_context *context, BOOL srgb)
3146 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
3147 enum wined3d_conversion_type convert;
3148 struct wined3d_format format;
3150 if (surface->flags & alloc_flag) return;
3152 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
3153 if (convert != WINED3D_CT_NONE || format.convert)
3154 surface->flags |= SFLAG_CONVERTED;
3155 else surface->flags &= ~SFLAG_CONVERTED;
3157 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
3158 surface_allocate_surface(surface, context->gl_info, &format, srgb);
3159 surface->flags |= alloc_flag;
3162 /* Context activation is done by the caller. */
3163 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
3165 struct wined3d_texture *texture = surface->container;
3166 UINT sub_count = texture->level_count * texture->layer_count;
3167 UINT i;
3169 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
3171 for (i = 0; i < sub_count; ++i)
3173 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
3174 surface_prepare_texture_internal(s, context, srgb);
3177 return;
3180 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
3182 if (multisample)
3184 if (surface->rb_multisample)
3185 return;
3187 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
3188 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
3189 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
3190 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
3191 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
3193 else
3195 if (surface->rb_resolved)
3196 return;
3198 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
3199 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
3200 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
3201 surface->pow2Width, surface->pow2Height);
3202 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
3206 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
3208 /* FIXME: Is this really how color keys are supposed to work? I think it
3209 * makes more sense to compare the individual channels. */
3210 return color >= color_key->color_space_low_value
3211 && color <= color_key->color_space_high_value;
3214 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
3215 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
3217 const BYTE *source;
3218 BYTE *dest;
3220 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
3221 src, dst, pitch, width, height, outpitch, conversion_type, surface);
3223 switch (conversion_type)
3225 case WINED3D_CT_NONE:
3227 memcpy(dst, src, pitch * height);
3228 break;
3231 case WINED3D_CT_PALETTED:
3232 if (surface->container->swapchain && surface->container->swapchain->palette)
3234 unsigned int x, y;
3235 const struct wined3d_palette *palette = surface->container->swapchain->palette;
3236 for (y = 0; y < height; y++)
3238 source = src + pitch * y;
3239 dest = dst + outpitch * y;
3240 for (x = 0; x < width; x++)
3242 BYTE color = *source++;
3243 *dest++ = palette->colors[color].rgbRed;
3244 *dest++ = palette->colors[color].rgbGreen;
3245 *dest++ = palette->colors[color].rgbBlue;
3246 *dest++ = 0;
3250 else
3252 /* This should probably use the system palette, but unless
3253 * the X server is running in P8 mode there is no such thing.
3254 * The probably best solution is to set the fixed 20 colors
3255 * from the default windows palette and set the rest to black,
3256 * white, or some ugly pink. For now use black for the entire
3257 * palette. Don't use pink everywhere. Age of Empires 2 draws
3258 * a front buffer filled with zeroes without a palette when
3259 * starting and we don't want the screen to flash in an ugly
3260 * color. */
3261 FIXME("P8 surface loaded without a palette.\n");
3262 memset(dst, 0, height * outpitch);
3265 break;
3267 case WINED3D_CT_CK_565:
3269 /* Converting the 565 format in 5551 packed to emulate color-keying.
3271 Note : in all these conversion, it would be best to average the averaging
3272 pixels to get the color of the pixel that will be color-keyed to
3273 prevent 'color bleeding'. This will be done later on if ever it is
3274 too visible.
3276 Note2: Nvidia documents say that their driver does not support alpha + color keying
3277 on the same surface and disables color keying in such a case
3279 unsigned int x, y;
3280 const WORD *Source;
3281 WORD *Dest;
3283 TRACE("Color keyed 565\n");
3285 for (y = 0; y < height; y++) {
3286 Source = (const WORD *)(src + y * pitch);
3287 Dest = (WORD *) (dst + y * outpitch);
3288 for (x = 0; x < width; x++ ) {
3289 WORD color = *Source++;
3290 *Dest = ((color & 0xffc0) | ((color & 0x1f) << 1));
3291 if (!color_in_range(&surface->container->src_blt_color_key, color))
3292 *Dest |= 0x0001;
3293 Dest++;
3297 break;
3299 case WINED3D_CT_CK_5551:
3301 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
3302 unsigned int x, y;
3303 const WORD *Source;
3304 WORD *Dest;
3305 TRACE("Color keyed 5551\n");
3306 for (y = 0; y < height; y++) {
3307 Source = (const WORD *)(src + y * pitch);
3308 Dest = (WORD *) (dst + y * outpitch);
3309 for (x = 0; x < width; x++ ) {
3310 WORD color = *Source++;
3311 *Dest = color;
3312 if (!color_in_range(&surface->container->src_blt_color_key, color))
3313 *Dest |= (1 << 15);
3314 else
3315 *Dest &= ~(1 << 15);
3316 Dest++;
3320 break;
3322 case WINED3D_CT_CK_RGB24:
3324 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
3325 unsigned int x, y;
3326 for (y = 0; y < height; y++)
3328 source = src + pitch * y;
3329 dest = dst + outpitch * y;
3330 for (x = 0; x < width; x++) {
3331 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
3332 DWORD dstcolor = color << 8;
3333 if (!color_in_range(&surface->container->src_blt_color_key, color))
3334 dstcolor |= 0xff;
3335 *(DWORD*)dest = dstcolor;
3336 source += 3;
3337 dest += 4;
3341 break;
3343 case WINED3D_CT_RGB32_888:
3345 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
3346 unsigned int x, y;
3347 for (y = 0; y < height; y++)
3349 source = src + pitch * y;
3350 dest = dst + outpitch * y;
3351 for (x = 0; x < width; x++) {
3352 DWORD color = 0xffffff & *(const DWORD*)source;
3353 DWORD dstcolor = color << 8;
3354 if (!color_in_range(&surface->container->src_blt_color_key, color))
3355 dstcolor |= 0xff;
3356 *(DWORD*)dest = dstcolor;
3357 source += 4;
3358 dest += 4;
3362 break;
3364 case WINED3D_CT_CK_ARGB32:
3366 unsigned int x, y;
3367 for (y = 0; y < height; ++y)
3369 source = src + pitch * y;
3370 dest = dst + outpitch * y;
3371 for (x = 0; x < width; ++x)
3373 DWORD color = *(const DWORD *)source;
3374 if (color_in_range(&surface->container->src_blt_color_key, color))
3375 color &= ~0xff000000;
3376 *(DWORD*)dest = color;
3377 source += 4;
3378 dest += 4;
3382 break;
3384 default:
3385 ERR("Unsupported conversion type %#x.\n", conversion_type);
3387 return WINED3D_OK;
3390 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
3392 if (front->container->level_count != 1 || front->container->layer_count != 1
3393 || back->container->level_count != 1 || back->container->layer_count != 1)
3394 ERR("Flip between surfaces %p and %p not supported.\n", front, back);
3396 /* Flip the surface contents */
3397 /* Flip the DC */
3399 HDC tmp;
3400 tmp = front->hDC;
3401 front->hDC = back->hDC;
3402 back->hDC = tmp;
3405 /* Flip the DIBsection */
3407 HBITMAP tmp = front->dib.DIBsection;
3408 front->dib.DIBsection = back->dib.DIBsection;
3409 back->dib.DIBsection = tmp;
3412 /* Flip the surface data */
3414 void* tmp;
3416 tmp = front->dib.bitmap_data;
3417 front->dib.bitmap_data = back->dib.bitmap_data;
3418 back->dib.bitmap_data = tmp;
3420 tmp = front->resource.heap_memory;
3421 front->resource.heap_memory = back->resource.heap_memory;
3422 back->resource.heap_memory = tmp;
3425 /* Flip the PBO */
3427 GLuint tmp_pbo = front->pbo;
3428 front->pbo = back->pbo;
3429 back->pbo = tmp_pbo;
3432 /* Flip the opengl texture */
3434 GLuint tmp;
3436 tmp = back->container->texture_rgb.name;
3437 back->container->texture_rgb.name = front->container->texture_rgb.name;
3438 front->container->texture_rgb.name = tmp;
3440 tmp = back->container->texture_srgb.name;
3441 back->container->texture_srgb.name = front->container->texture_srgb.name;
3442 front->container->texture_srgb.name = tmp;
3444 tmp = back->rb_multisample;
3445 back->rb_multisample = front->rb_multisample;
3446 front->rb_multisample = tmp;
3448 tmp = back->rb_resolved;
3449 back->rb_resolved = front->rb_resolved;
3450 front->rb_resolved = tmp;
3452 resource_unload(&back->resource);
3453 resource_unload(&front->resource);
3457 DWORD tmp_flags = back->flags;
3458 back->flags = front->flags;
3459 front->flags = tmp_flags;
3461 tmp_flags = back->locations;
3462 back->locations = front->locations;
3463 front->locations = tmp_flags;
3467 /* Does a direct frame buffer -> texture copy. Stretching is done with single
3468 * pixel copy calls. */
3469 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3470 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3472 struct wined3d_device *device = dst_surface->resource.device;
3473 const struct wined3d_gl_info *gl_info;
3474 float xrel, yrel;
3475 struct wined3d_context *context;
3476 BOOL upsidedown = FALSE;
3477 RECT dst_rect = *dst_rect_in;
3479 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3480 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3482 if(dst_rect.top > dst_rect.bottom) {
3483 UINT tmp = dst_rect.bottom;
3484 dst_rect.bottom = dst_rect.top;
3485 dst_rect.top = tmp;
3486 upsidedown = TRUE;
3489 context = context_acquire(device, src_surface);
3490 gl_info = context->gl_info;
3491 context_apply_blit_state(context, device);
3492 wined3d_texture_load(dst_surface->container, context, FALSE);
3494 /* Bind the target texture */
3495 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
3496 if (wined3d_resource_is_offscreen(&src_surface->resource))
3498 TRACE("Reading from an offscreen target\n");
3499 upsidedown = !upsidedown;
3500 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3502 else
3504 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3506 checkGLcall("glReadBuffer");
3508 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3509 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3511 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3513 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3515 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3516 ERR("Texture filtering not supported in direct blit.\n");
3518 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3519 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3521 ERR("Texture filtering not supported in direct blit\n");
3524 if (upsidedown
3525 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3526 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3528 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
3529 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3530 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3531 src_rect->left, src_surface->resource.height - src_rect->bottom,
3532 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3534 else
3536 LONG row;
3537 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
3538 /* I have to process this row by row to swap the image,
3539 * otherwise it would be upside down, so stretching in y direction
3540 * doesn't cost extra time
3542 * However, stretching in x direction can be avoided if not necessary
3544 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3545 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3547 /* Well, that stuff works, but it's very slow.
3548 * find a better way instead
3550 LONG col;
3552 for (col = dst_rect.left; col < dst_rect.right; ++col)
3554 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3555 dst_rect.left + col /* x offset */, row /* y offset */,
3556 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3559 else
3561 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3562 dst_rect.left /* x offset */, row /* y offset */,
3563 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3567 checkGLcall("glCopyTexSubImage2D");
3569 context_release(context);
3571 /* The texture is now most up to date - If the surface is a render target
3572 * and has a drawable, this path is never entered. */
3573 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3574 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3577 /* Uses the hardware to stretch and flip the image */
3578 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3579 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3581 struct wined3d_device *device = dst_surface->resource.device;
3582 GLuint src, backup = 0;
3583 float left, right, top, bottom; /* Texture coordinates */
3584 UINT fbwidth = src_surface->resource.width;
3585 UINT fbheight = src_surface->resource.height;
3586 const struct wined3d_gl_info *gl_info;
3587 struct wined3d_context *context;
3588 GLenum drawBuffer = GL_BACK;
3589 GLenum texture_target;
3590 BOOL noBackBufferBackup;
3591 BOOL src_offscreen;
3592 BOOL upsidedown = FALSE;
3593 RECT dst_rect = *dst_rect_in;
3595 TRACE("Using hwstretch blit\n");
3596 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3597 context = context_acquire(device, src_surface);
3598 gl_info = context->gl_info;
3599 context_apply_blit_state(context, device);
3600 wined3d_texture_load(dst_surface->container, context, FALSE);
3602 src_offscreen = wined3d_resource_is_offscreen(&src_surface->resource);
3603 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3604 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3606 /* Get it a description */
3607 wined3d_texture_load(src_surface->container, context, FALSE);
3610 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3611 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3613 if (context->aux_buffers >= 2)
3615 /* Got more than one aux buffer? Use the 2nd aux buffer */
3616 drawBuffer = GL_AUX1;
3618 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3620 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3621 drawBuffer = GL_AUX0;
3624 if (noBackBufferBackup)
3626 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3627 checkGLcall("glGenTextures");
3628 context_bind_texture(context, GL_TEXTURE_2D, backup);
3629 texture_target = GL_TEXTURE_2D;
3631 else
3633 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3634 * we are reading from the back buffer, the backup can be used as source texture
3636 texture_target = src_surface->texture_target;
3637 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3638 gl_info->gl_ops.gl.p_glEnable(texture_target);
3639 checkGLcall("glEnable(texture_target)");
3641 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3642 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3645 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3646 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3648 if(dst_rect.top > dst_rect.bottom) {
3649 UINT tmp = dst_rect.bottom;
3650 dst_rect.bottom = dst_rect.top;
3651 dst_rect.top = tmp;
3652 upsidedown = TRUE;
3655 if (src_offscreen)
3657 TRACE("Reading from an offscreen target\n");
3658 upsidedown = !upsidedown;
3659 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3661 else
3663 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3666 /* TODO: Only back up the part that will be overwritten */
3667 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3669 checkGLcall("glCopyTexSubImage2D");
3671 /* No issue with overriding these - the sampler is dirty due to blit usage */
3672 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3673 wined3d_gl_mag_filter(magLookup, filter));
3674 checkGLcall("glTexParameteri");
3675 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3676 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
3677 checkGLcall("glTexParameteri");
3679 if (!src_surface->container->swapchain || src_surface == src_surface->container->swapchain->back_buffers[0])
3681 src = backup ? backup : src_surface->container->texture_rgb.name;
3683 else
3685 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3686 checkGLcall("glReadBuffer(GL_FRONT)");
3688 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3689 checkGLcall("glGenTextures(1, &src)");
3690 context_bind_texture(context, GL_TEXTURE_2D, src);
3692 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3693 * out for power of 2 sizes
3695 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3696 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3697 checkGLcall("glTexImage2D");
3698 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3700 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3701 checkGLcall("glTexParameteri");
3702 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3703 checkGLcall("glTexParameteri");
3705 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3706 checkGLcall("glReadBuffer(GL_BACK)");
3708 if (texture_target != GL_TEXTURE_2D)
3710 gl_info->gl_ops.gl.p_glDisable(texture_target);
3711 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3712 texture_target = GL_TEXTURE_2D;
3715 checkGLcall("glEnd and previous");
3717 left = src_rect->left;
3718 right = src_rect->right;
3720 if (!upsidedown)
3722 top = src_surface->resource.height - src_rect->top;
3723 bottom = src_surface->resource.height - src_rect->bottom;
3725 else
3727 top = src_surface->resource.height - src_rect->bottom;
3728 bottom = src_surface->resource.height - src_rect->top;
3731 if (src_surface->flags & SFLAG_NORMCOORD)
3733 left /= src_surface->pow2Width;
3734 right /= src_surface->pow2Width;
3735 top /= src_surface->pow2Height;
3736 bottom /= src_surface->pow2Height;
3739 /* draw the source texture stretched and upside down. The correct surface is bound already */
3740 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3741 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3743 context_set_draw_buffer(context, drawBuffer);
3744 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3746 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3747 /* bottom left */
3748 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3749 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3751 /* top left */
3752 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3753 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3755 /* top right */
3756 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3757 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3759 /* bottom right */
3760 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3761 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3762 gl_info->gl_ops.gl.p_glEnd();
3763 checkGLcall("glEnd and previous");
3765 if (texture_target != dst_surface->texture_target)
3767 gl_info->gl_ops.gl.p_glDisable(texture_target);
3768 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3769 texture_target = dst_surface->texture_target;
3772 /* Now read the stretched and upside down image into the destination texture */
3773 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
3774 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
3776 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3777 0, 0, /* We blitted the image to the origin */
3778 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3779 checkGLcall("glCopyTexSubImage2D");
3781 if (drawBuffer == GL_BACK)
3783 /* Write the back buffer backup back. */
3784 if (backup)
3786 if (texture_target != GL_TEXTURE_2D)
3788 gl_info->gl_ops.gl.p_glDisable(texture_target);
3789 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3790 texture_target = GL_TEXTURE_2D;
3792 context_bind_texture(context, GL_TEXTURE_2D, backup);
3794 else
3796 if (texture_target != src_surface->texture_target)
3798 gl_info->gl_ops.gl.p_glDisable(texture_target);
3799 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3800 texture_target = src_surface->texture_target;
3802 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3805 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3806 /* top left */
3807 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3808 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3810 /* bottom left */
3811 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3812 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3814 /* bottom right */
3815 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3816 (float)fbheight / (float)src_surface->pow2Height);
3817 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3819 /* top right */
3820 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3821 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3822 gl_info->gl_ops.gl.p_glEnd();
3824 gl_info->gl_ops.gl.p_glDisable(texture_target);
3825 checkGLcall("glDisable(texture_target)");
3827 /* Cleanup */
3828 if (src != src_surface->container->texture_rgb.name && src != backup)
3830 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3831 checkGLcall("glDeleteTextures(1, &src)");
3833 if (backup)
3835 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3836 checkGLcall("glDeleteTextures(1, &backup)");
3839 if (wined3d_settings.strict_draw_ordering)
3840 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3842 context_release(context);
3844 /* The texture is now most up to date - If the surface is a render target
3845 * and has a drawable, this path is never entered. */
3846 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3847 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3850 /* Front buffer coordinates are always full screen coordinates, but our GL
3851 * drawable is limited to the window's client area. The sysmem and texture
3852 * copies do have the full screen size. Note that GL has a bottom-left
3853 * origin, while D3D has a top-left origin. */
3854 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3856 UINT drawable_height;
3858 if (surface->container->swapchain && surface == surface->container->swapchain->front_buffer)
3860 POINT offset = {0, 0};
3861 RECT windowsize;
3863 ScreenToClient(window, &offset);
3864 OffsetRect(rect, offset.x, offset.y);
3866 GetClientRect(window, &windowsize);
3867 drawable_height = windowsize.bottom - windowsize.top;
3869 else
3871 drawable_height = surface->resource.height;
3874 rect->top = drawable_height - rect->top;
3875 rect->bottom = drawable_height - rect->bottom;
3878 static void surface_blt_to_drawable(const struct wined3d_device *device,
3879 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3880 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3881 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3883 const struct wined3d_gl_info *gl_info;
3884 struct wined3d_context *context;
3885 RECT src_rect, dst_rect;
3887 src_rect = *src_rect_in;
3888 dst_rect = *dst_rect_in;
3890 context = context_acquire(device, dst_surface);
3891 gl_info = context->gl_info;
3893 /* Make sure the surface is up-to-date. This should probably use
3894 * surface_load_location() and worry about the destination surface too,
3895 * unless we're overwriting it completely. */
3896 wined3d_texture_load(src_surface->container, context, FALSE);
3898 /* Activate the destination context, set it up for blitting */
3899 context_apply_blit_state(context, device);
3901 if (!wined3d_resource_is_offscreen(&dst_surface->resource))
3902 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3904 device->blitter->set_shader(device->blit_priv, context, src_surface);
3906 if (alpha_test)
3908 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3909 checkGLcall("glEnable(GL_ALPHA_TEST)");
3911 /* For P8 surfaces, the alpha component contains the palette index.
3912 * Which means that the colorkey is one of the palette entries. In
3913 * other cases pixels that should be masked away have alpha set to 0. */
3914 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3915 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3916 (float)src_surface->container->src_blt_color_key.color_space_low_value / 256.0f);
3917 else
3918 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3919 checkGLcall("glAlphaFunc");
3921 else
3923 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3924 checkGLcall("glDisable(GL_ALPHA_TEST)");
3927 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3929 if (alpha_test)
3931 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3932 checkGLcall("glDisable(GL_ALPHA_TEST)");
3935 /* Leave the opengl state valid for blitting */
3936 device->blitter->unset_shader(context->gl_info);
3938 if (wined3d_settings.strict_draw_ordering
3939 || (dst_surface->container->swapchain && dst_surface->container->swapchain->front_buffer == dst_surface))
3940 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3942 context_release(context);
3945 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3947 struct wined3d_device *device = s->resource.device;
3948 const struct blit_shader *blitter;
3950 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
3951 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
3952 if (!blitter)
3954 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3955 return WINED3DERR_INVALIDCALL;
3958 return blitter->color_fill(device, s, rect, color);
3961 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3962 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3963 enum wined3d_texture_filter_type filter)
3965 struct wined3d_device *device = dst_surface->resource.device;
3966 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3967 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3969 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3970 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3971 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3973 /* Get the swapchain. One of the surfaces has to be a primary surface */
3974 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3976 WARN("Destination is in sysmem, rejecting gl blt\n");
3977 return WINED3DERR_INVALIDCALL;
3980 dst_swapchain = dst_surface->container->swapchain;
3982 if (src_surface)
3984 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3986 WARN("Src is in sysmem, rejecting gl blt\n");
3987 return WINED3DERR_INVALIDCALL;
3990 src_swapchain = src_surface->container->swapchain;
3992 else
3994 src_swapchain = NULL;
3997 /* Early sort out of cases where no render target is used */
3998 if (!dst_swapchain && !src_swapchain
3999 && src_surface != device->fb.render_targets[0]
4000 && dst_surface != device->fb.render_targets[0])
4002 TRACE("No surface is render target, not using hardware blit.\n");
4003 return WINED3DERR_INVALIDCALL;
4006 /* No destination color keying supported */
4007 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
4009 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
4010 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
4011 return WINED3DERR_INVALIDCALL;
4014 if (dst_swapchain && dst_swapchain == src_swapchain)
4016 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
4017 return WINED3DERR_INVALIDCALL;
4020 if (dst_swapchain && src_swapchain)
4022 FIXME("Implement hardware blit between two different swapchains\n");
4023 return WINED3DERR_INVALIDCALL;
4026 if (dst_swapchain)
4028 /* Handled with regular texture -> swapchain blit */
4029 if (src_surface == device->fb.render_targets[0])
4030 TRACE("Blit from active render target to a swapchain\n");
4032 else if (src_swapchain && dst_surface == device->fb.render_targets[0])
4034 FIXME("Implement blit from a swapchain to the active render target\n");
4035 return WINED3DERR_INVALIDCALL;
4038 if ((src_swapchain || src_surface == device->fb.render_targets[0]) && !dst_swapchain)
4040 /* Blit from render target to texture */
4041 BOOL stretchx;
4043 /* P8 read back is not implemented */
4044 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
4045 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
4047 TRACE("P8 read back not supported by frame buffer to texture blit\n");
4048 return WINED3DERR_INVALIDCALL;
4051 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
4053 TRACE("Color keying not supported by frame buffer to texture blit\n");
4054 return WINED3DERR_INVALIDCALL;
4055 /* Destination color key is checked above */
4058 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
4059 stretchx = TRUE;
4060 else
4061 stretchx = FALSE;
4063 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
4064 * flip the image nor scale it.
4066 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
4067 * -> If the app wants an image width an unscaled width, copy it line per line
4068 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
4069 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
4070 * back buffer. This is slower than reading line per line, thus not used for flipping
4071 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
4072 * pixel by pixel. */
4073 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
4074 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
4076 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
4077 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
4079 else
4081 TRACE("Using hardware stretching to flip / stretch the texture.\n");
4082 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
4085 surface_evict_sysmem(dst_surface);
4087 return WINED3D_OK;
4089 else if (src_surface)
4091 /* Blit from offscreen surface to render target */
4092 struct wined3d_color_key old_blt_key = src_surface->container->src_blt_color_key;
4093 DWORD old_color_key_flags = src_surface->container->color_key_flags;
4095 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4097 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4098 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
4099 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
4101 FIXME("Unsupported blit operation falling back to software\n");
4102 return WINED3DERR_INVALIDCALL;
4105 /* Color keying: Check if we have to do a color keyed blt,
4106 * and if not check if a color key is activated.
4108 * Just modify the color keying parameters in the surface and restore them afterwards
4109 * The surface keeps track of the color key last used to load the opengl surface.
4110 * PreLoad will catch the change to the flags and color key and reload if necessary.
4112 if (flags & WINEDDBLT_KEYSRC)
4114 /* Use color key from surface */
4116 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4118 /* Use color key from DDBltFx */
4119 src_surface->container->color_key_flags |= WINEDDSD_CKSRCBLT;
4120 src_surface->container->src_blt_color_key = DDBltFx->ddckSrcColorkey;
4122 else
4124 /* Do not use color key */
4125 src_surface->container->color_key_flags &= ~WINEDDSD_CKSRCBLT;
4128 surface_blt_to_drawable(device, filter,
4129 flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_ALPHATEST),
4130 src_surface, src_rect, dst_surface, dst_rect);
4132 /* Restore the color key parameters */
4133 src_surface->container->color_key_flags = old_color_key_flags;
4134 src_surface->container->src_blt_color_key = old_blt_key;
4136 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
4137 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
4139 return WINED3D_OK;
4142 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
4143 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
4144 return WINED3DERR_INVALIDCALL;
4147 /* Context activation is done by the caller. */
4148 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
4149 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
4151 struct wined3d_device *device = surface->resource.device;
4152 const struct wined3d_gl_info *gl_info = context->gl_info;
4153 GLint compare_mode = GL_NONE;
4154 struct blt_info info;
4155 GLint old_binding = 0;
4156 RECT rect;
4158 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4160 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
4161 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
4162 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
4163 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
4164 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
4165 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
4166 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
4167 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
4168 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4169 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
4170 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
4172 SetRect(&rect, 0, h, w, 0);
4173 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
4174 context_active_texture(context, context->gl_info, 0);
4175 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
4176 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
4177 if (gl_info->supported[ARB_SHADOW])
4179 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
4180 if (compare_mode != GL_NONE)
4181 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
4184 device->shader_backend->shader_select_depth_blt(device->shader_priv,
4185 gl_info, info.tex_type, &surface->ds_current_size);
4187 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
4188 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
4189 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
4190 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
4191 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
4192 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
4193 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
4194 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
4195 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
4196 gl_info->gl_ops.gl.p_glEnd();
4198 if (compare_mode != GL_NONE)
4199 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
4200 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
4202 gl_info->gl_ops.gl.p_glPopAttrib();
4204 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
4207 void surface_modify_ds_location(struct wined3d_surface *surface,
4208 DWORD location, UINT w, UINT h)
4210 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
4212 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
4213 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
4214 wined3d_texture_set_dirty(surface->container);
4216 surface->ds_current_size.cx = w;
4217 surface->ds_current_size.cy = h;
4218 surface->locations = location;
4221 /* Context activation is done by the caller. */
4222 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4224 const struct wined3d_gl_info *gl_info = context->gl_info;
4225 struct wined3d_device *device = surface->resource.device;
4226 GLsizei w, h;
4228 TRACE("surface %p, new location %#x.\n", surface, location);
4230 /* TODO: Make this work for modes other than FBO */
4231 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4233 if (!(surface->locations & location))
4235 w = surface->ds_current_size.cx;
4236 h = surface->ds_current_size.cy;
4237 surface->ds_current_size.cx = 0;
4238 surface->ds_current_size.cy = 0;
4240 else
4242 w = surface->resource.width;
4243 h = surface->resource.height;
4246 if (surface->ds_current_size.cx == surface->resource.width
4247 && surface->ds_current_size.cy == surface->resource.height)
4249 TRACE("Location (%#x) is already up to date.\n", location);
4250 return;
4253 if (surface->current_renderbuffer)
4255 FIXME("Not supported with fixed up depth stencil.\n");
4256 return;
4259 if (surface->locations & WINED3D_LOCATION_DISCARDED)
4261 TRACE("Surface was discarded, no need copy data.\n");
4262 switch (location)
4264 case WINED3D_LOCATION_TEXTURE_RGB:
4265 surface_prepare_texture(surface, context, FALSE);
4266 break;
4267 case WINED3D_LOCATION_RB_MULTISAMPLE:
4268 surface_prepare_rb(surface, gl_info, TRUE);
4269 break;
4270 case WINED3D_LOCATION_DRAWABLE:
4271 /* Nothing to do */
4272 break;
4273 default:
4274 FIXME("Unhandled location %#x\n", location);
4276 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
4277 surface->locations |= location;
4278 surface->ds_current_size.cx = surface->resource.width;
4279 surface->ds_current_size.cy = surface->resource.height;
4280 return;
4283 if (!surface->locations)
4285 FIXME("No up to date depth stencil location.\n");
4286 surface->locations |= location;
4287 surface->ds_current_size.cx = surface->resource.width;
4288 surface->ds_current_size.cy = surface->resource.height;
4289 return;
4292 if (location == WINED3D_LOCATION_TEXTURE_RGB)
4294 GLint old_binding = 0;
4295 GLenum bind_target;
4297 /* The render target is allowed to be smaller than the depth/stencil
4298 * buffer, so the onscreen depth/stencil buffer is potentially smaller
4299 * than the offscreen surface. Don't overwrite the offscreen surface
4300 * with undefined data. */
4301 w = min(w, context->swapchain->desc.backbuffer_width);
4302 h = min(h, context->swapchain->desc.backbuffer_height);
4304 TRACE("Copying onscreen depth buffer to depth texture.\n");
4306 if (!device->depth_blt_texture)
4307 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
4309 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4310 * directly on the FBO texture. That's because we need to flip. */
4311 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4312 context->swapchain->front_buffer, NULL, WINED3D_LOCATION_DRAWABLE);
4313 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4315 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4316 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4318 else
4320 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4321 bind_target = GL_TEXTURE_2D;
4323 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
4324 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
4325 * internal format, because the internal format might include stencil
4326 * data. In principle we should copy stencil data as well, but unless
4327 * the driver supports stencil export it's hard to do, and doesn't
4328 * seem to be needed in practice. If the hardware doesn't support
4329 * writing stencil data, the glCopyTexImage2D() call might trigger
4330 * software fallbacks. */
4331 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
4332 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4333 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4334 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4335 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4336 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4337 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4338 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
4340 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4341 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
4342 context_set_draw_buffer(context, GL_NONE);
4344 /* Do the actual blit */
4345 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
4346 checkGLcall("depth_blt");
4348 context_invalidate_state(context, STATE_FRAMEBUFFER);
4350 if (wined3d_settings.strict_draw_ordering)
4351 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4353 else if (location == WINED3D_LOCATION_DRAWABLE)
4355 TRACE("Copying depth texture to onscreen depth buffer.\n");
4357 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
4358 context->swapchain->front_buffer, NULL, WINED3D_LOCATION_DRAWABLE);
4359 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
4360 0, surface->pow2Height - h, w, h, surface->texture_target);
4361 checkGLcall("depth_blt");
4363 context_invalidate_state(context, STATE_FRAMEBUFFER);
4365 if (wined3d_settings.strict_draw_ordering)
4366 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
4368 else
4370 ERR("Invalid location (%#x) specified.\n", location);
4373 surface->locations |= location;
4374 surface->ds_current_size.cx = surface->resource.width;
4375 surface->ds_current_size.cy = surface->resource.height;
4378 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
4380 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4382 surface->locations |= location;
4385 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
4387 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4389 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4390 wined3d_texture_set_dirty(surface->container);
4391 surface->locations &= ~location;
4393 if (!surface->locations)
4394 ERR("Surface %p does not have any up to date location.\n", surface);
4397 static DWORD resource_access_from_location(DWORD location)
4399 switch (location)
4401 case WINED3D_LOCATION_SYSMEM:
4402 case WINED3D_LOCATION_USER_MEMORY:
4403 case WINED3D_LOCATION_DIB:
4404 case WINED3D_LOCATION_BUFFER:
4405 return WINED3D_RESOURCE_ACCESS_CPU;
4407 case WINED3D_LOCATION_DRAWABLE:
4408 case WINED3D_LOCATION_TEXTURE_SRGB:
4409 case WINED3D_LOCATION_TEXTURE_RGB:
4410 case WINED3D_LOCATION_RB_MULTISAMPLE:
4411 case WINED3D_LOCATION_RB_RESOLVED:
4412 return WINED3D_RESOURCE_ACCESS_GPU;
4414 default:
4415 FIXME("Unhandled location %#x.\n", location);
4416 return 0;
4420 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
4422 struct wined3d_device *device = surface->resource.device;
4423 struct wined3d_context *context;
4424 const struct wined3d_gl_info *gl_info;
4425 struct wined3d_bo_address dst, src;
4426 UINT size = surface->resource.size;
4428 surface_get_memory(surface, &dst, location);
4429 surface_get_memory(surface, &src, surface->locations);
4431 if (dst.buffer_object)
4433 context = context_acquire(device, NULL);
4434 gl_info = context->gl_info;
4435 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, dst.buffer_object));
4436 GL_EXTCALL(glBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, size, src.addr));
4437 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4438 checkGLcall("Upload PBO");
4439 context_release(context);
4440 return;
4442 if (src.buffer_object)
4444 context = context_acquire(device, NULL);
4445 gl_info = context->gl_info;
4446 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, src.buffer_object));
4447 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_PACK_BUFFER_ARB, 0, size, dst.addr));
4448 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4449 checkGLcall("Download PBO");
4450 context_release(context);
4451 return;
4453 memcpy(dst.addr, src.addr, size);
4456 static void surface_load_sysmem(struct wined3d_surface *surface,
4457 const struct wined3d_gl_info *gl_info, DWORD dst_location)
4459 if (surface->locations & surface_simple_locations)
4461 surface_copy_simple_location(surface, dst_location);
4462 return;
4465 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
4466 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4468 /* Download the surface to system memory. */
4469 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4471 struct wined3d_device *device = surface->resource.device;
4472 struct wined3d_context *context;
4474 /* TODO: Use already acquired context when possible. */
4475 context = context_acquire(device, NULL);
4477 wined3d_texture_bind_and_dirtify(surface->container, context,
4478 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
4479 surface_download_data(surface, gl_info, dst_location);
4481 context_release(context);
4483 return;
4486 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
4488 read_from_framebuffer(surface, dst_location);
4489 return;
4492 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
4493 surface, wined3d_debug_location(surface->locations));
4496 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
4497 const struct wined3d_gl_info *gl_info)
4499 RECT r;
4501 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
4502 && wined3d_resource_is_offscreen(&surface->resource))
4504 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
4505 return WINED3DERR_INVALIDCALL;
4508 surface_get_rect(surface, NULL, &r);
4509 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4510 surface_blt_to_drawable(surface->resource.device,
4511 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
4513 return WINED3D_OK;
4516 static HRESULT surface_load_texture(struct wined3d_surface *surface,
4517 const struct wined3d_gl_info *gl_info, BOOL srgb)
4519 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
4520 struct wined3d_device *device = surface->resource.device;
4521 enum wined3d_conversion_type convert;
4522 struct wined3d_context *context;
4523 UINT width, src_pitch, dst_pitch;
4524 struct wined3d_bo_address data;
4525 struct wined3d_format format;
4526 POINT dst_point = {0, 0};
4527 BYTE *mem = NULL;
4529 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
4530 && wined3d_resource_is_offscreen(&surface->resource)
4531 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
4533 surface_load_fb_texture(surface, srgb);
4535 return WINED3D_OK;
4538 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
4539 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
4540 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4541 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4542 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4544 if (srgb)
4545 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
4546 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
4547 else
4548 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
4549 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
4551 return WINED3D_OK;
4554 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
4555 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
4556 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4557 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4558 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4560 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
4561 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
4562 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
4563 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4565 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
4566 &rect, surface, dst_location, &rect);
4568 return WINED3D_OK;
4571 /* Upload from system memory */
4573 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
4574 TRUE /* We will use textures */, &format, &convert);
4576 if (srgb)
4578 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
4579 == WINED3D_LOCATION_TEXTURE_RGB)
4581 /* Performance warning... */
4582 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
4583 surface_prepare_map_memory(surface);
4584 surface_load_location(surface, surface->resource.map_binding);
4587 else
4589 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
4590 == WINED3D_LOCATION_TEXTURE_SRGB)
4592 /* Performance warning... */
4593 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
4594 surface_prepare_map_memory(surface);
4595 surface_load_location(surface, surface->resource.map_binding);
4599 if (!(surface->locations & surface_simple_locations))
4601 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
4602 /* Lets hope we get it from somewhere... */
4603 surface_prepare_system_memory(surface);
4604 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
4607 /* TODO: Use already acquired context when possible. */
4608 context = context_acquire(device, NULL);
4610 surface_prepare_texture(surface, context, srgb);
4611 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
4613 if (surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
4615 surface->flags |= SFLAG_GLCKEY;
4616 surface->gl_color_key = surface->container->src_blt_color_key;
4618 else surface->flags &= ~SFLAG_GLCKEY;
4620 width = surface->resource.width;
4621 src_pitch = wined3d_surface_get_pitch(surface);
4623 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
4624 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
4625 * called. */
4626 if ((convert != WINED3D_CT_NONE || format.convert) && surface->pbo)
4628 TRACE("Removing the pbo attached to surface %p.\n", surface);
4630 if (surface->flags & SFLAG_DIBSECTION)
4631 surface->resource.map_binding = WINED3D_LOCATION_DIB;
4632 else
4633 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
4635 surface_prepare_map_memory(surface);
4636 surface_load_location(surface, surface->resource.map_binding);
4637 surface_remove_pbo(surface, gl_info);
4640 surface_get_memory(surface, &data, surface->locations);
4641 if (format.convert)
4643 /* This code is entered for texture formats which need a fixup. */
4644 UINT height = surface->resource.height;
4646 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4647 dst_pitch = width * format.conv_byte_count;
4648 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4650 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4652 ERR("Out of memory (%u).\n", dst_pitch * height);
4653 context_release(context);
4654 return E_OUTOFMEMORY;
4656 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4657 dst_pitch, dst_pitch * height, width, height, 1);
4658 format.byte_count = format.conv_byte_count;
4659 src_pitch = dst_pitch;
4660 data.addr = mem;
4662 else if (convert != WINED3D_CT_NONE)
4664 /* This code is only entered for color keying fixups */
4665 UINT height = surface->resource.height;
4667 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4668 dst_pitch = width * format.conv_byte_count;
4669 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4671 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4673 ERR("Out of memory (%u).\n", dst_pitch * height);
4674 context_release(context);
4675 return E_OUTOFMEMORY;
4677 d3dfmt_convert_surface(data.addr, mem, src_pitch,
4678 width, height, dst_pitch, convert, surface);
4679 format.byte_count = format.conv_byte_count;
4680 src_pitch = dst_pitch;
4681 data.addr = mem;
4684 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
4686 context_release(context);
4688 HeapFree(GetProcessHeap(), 0, mem);
4690 return WINED3D_OK;
4693 static void surface_multisample_resolve(struct wined3d_surface *surface)
4695 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4697 if (!(surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE))
4698 ERR("Trying to resolve multisampled surface %p, but location WINED3D_LOCATION_RB_MULTISAMPLE not current.\n",
4699 surface);
4701 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
4702 surface, WINED3D_LOCATION_RB_MULTISAMPLE, &rect, surface, WINED3D_LOCATION_RB_RESOLVED, &rect);
4705 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
4707 struct wined3d_device *device = surface->resource.device;
4708 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4709 HRESULT hr;
4711 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4713 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4715 if (location == WINED3D_LOCATION_TEXTURE_RGB
4716 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4718 struct wined3d_context *context = context_acquire(device, NULL);
4719 surface_load_ds_location(surface, context, location);
4720 context_release(context);
4721 return WINED3D_OK;
4723 else if (location & surface->locations
4724 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
4726 /* Already up to date, nothing to do. */
4727 return WINED3D_OK;
4729 else
4731 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4732 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4733 return WINED3DERR_INVALIDCALL;
4737 if (surface->locations & location)
4739 TRACE("Location already up to date.\n");
4740 return WINED3D_OK;
4743 if (WARN_ON(d3d_surface))
4745 DWORD required_access = resource_access_from_location(location);
4746 if ((surface->resource.access_flags & required_access) != required_access)
4747 WARN("Operation requires %#x access, but surface only has %#x.\n",
4748 required_access, surface->resource.access_flags);
4751 if (!surface->locations)
4753 ERR("Surface %p does not have any up to date location.\n", surface);
4754 surface->flags |= SFLAG_LOST;
4755 return WINED3DERR_DEVICELOST;
4758 switch (location)
4760 case WINED3D_LOCATION_DIB:
4761 case WINED3D_LOCATION_USER_MEMORY:
4762 case WINED3D_LOCATION_SYSMEM:
4763 case WINED3D_LOCATION_BUFFER:
4764 surface_load_sysmem(surface, gl_info, location);
4765 break;
4767 case WINED3D_LOCATION_DRAWABLE:
4768 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
4769 return hr;
4770 break;
4772 case WINED3D_LOCATION_RB_RESOLVED:
4773 surface_multisample_resolve(surface);
4774 break;
4776 case WINED3D_LOCATION_TEXTURE_RGB:
4777 case WINED3D_LOCATION_TEXTURE_SRGB:
4778 if (FAILED(hr = surface_load_texture(surface, gl_info, location == WINED3D_LOCATION_TEXTURE_SRGB)))
4779 return hr;
4780 break;
4782 default:
4783 ERR("Don't know how to handle location %#x.\n", location);
4784 break;
4787 surface_validate_location(surface, location);
4789 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
4790 surface_evict_sysmem(surface);
4792 return WINED3D_OK;
4795 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
4796 /* Context activation is done by the caller. */
4797 static void ffp_blit_free(struct wined3d_device *device) { }
4799 /* Context activation is done by the caller. */
4800 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4802 const struct wined3d_gl_info *gl_info = context->gl_info;
4804 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
4805 checkGLcall("glEnable(target)");
4807 return WINED3D_OK;
4810 /* Context activation is done by the caller. */
4811 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
4813 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
4814 checkGLcall("glDisable(GL_TEXTURE_2D)");
4815 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4817 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4818 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4820 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4822 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
4823 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4827 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4828 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4829 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4831 switch (blit_op)
4833 case WINED3D_BLIT_OP_COLOR_BLIT:
4834 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4835 return FALSE;
4837 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4839 TRACE("Checking support for fixup:\n");
4840 dump_color_fixup_desc(src_format->color_fixup);
4843 /* We only support identity conversions. */
4844 if (!is_identity_fixup(src_format->color_fixup)
4845 || !is_identity_fixup(dst_format->color_fixup))
4847 TRACE("Fixups are not supported.\n");
4848 return FALSE;
4851 return TRUE;
4853 case WINED3D_BLIT_OP_COLOR_FILL:
4854 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
4855 return FALSE;
4857 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4859 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4860 return FALSE;
4862 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4864 TRACE("Color fill not supported\n");
4865 return FALSE;
4868 /* FIXME: We should reject color fills on formats with fixups,
4869 * but this would break P8 color fills for example. */
4871 return TRUE;
4873 case WINED3D_BLIT_OP_DEPTH_FILL:
4874 return TRUE;
4876 default:
4877 TRACE("Unsupported blit_op=%d\n", blit_op);
4878 return FALSE;
4882 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4883 const RECT *dst_rect, const struct wined3d_color *color)
4885 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4886 struct wined3d_fb_state fb = {&dst_surface, NULL};
4888 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4890 return WINED3D_OK;
4893 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
4894 struct wined3d_surface *surface, const RECT *rect, float depth)
4896 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
4897 struct wined3d_fb_state fb = {NULL, surface};
4899 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4901 return WINED3D_OK;
4904 const struct blit_shader ffp_blit = {
4905 ffp_blit_alloc,
4906 ffp_blit_free,
4907 ffp_blit_set,
4908 ffp_blit_unset,
4909 ffp_blit_supported,
4910 ffp_blit_color_fill,
4911 ffp_blit_depth_fill,
4914 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4916 return WINED3D_OK;
4919 /* Context activation is done by the caller. */
4920 static void cpu_blit_free(struct wined3d_device *device)
4924 /* Context activation is done by the caller. */
4925 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4927 return WINED3D_OK;
4930 /* Context activation is done by the caller. */
4931 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4935 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4936 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4937 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4939 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4941 return TRUE;
4944 return FALSE;
4947 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4948 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4949 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4951 UINT row_block_count;
4952 const BYTE *src_row;
4953 BYTE *dst_row;
4954 UINT x, y;
4956 src_row = src_data;
4957 dst_row = dst_data;
4959 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4961 if (!flags)
4963 for (y = 0; y < update_h; y += format->block_height)
4965 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4966 src_row += src_pitch;
4967 dst_row += dst_pitch;
4970 return WINED3D_OK;
4973 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4975 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4977 switch (format->id)
4979 case WINED3DFMT_DXT1:
4980 for (y = 0; y < update_h; y += format->block_height)
4982 struct block
4984 WORD color[2];
4985 BYTE control_row[4];
4988 const struct block *s = (const struct block *)src_row;
4989 struct block *d = (struct block *)dst_row;
4991 for (x = 0; x < row_block_count; ++x)
4993 d[x].color[0] = s[x].color[0];
4994 d[x].color[1] = s[x].color[1];
4995 d[x].control_row[0] = s[x].control_row[3];
4996 d[x].control_row[1] = s[x].control_row[2];
4997 d[x].control_row[2] = s[x].control_row[1];
4998 d[x].control_row[3] = s[x].control_row[0];
5000 src_row -= src_pitch;
5001 dst_row += dst_pitch;
5003 return WINED3D_OK;
5005 case WINED3DFMT_DXT2:
5006 case WINED3DFMT_DXT3:
5007 for (y = 0; y < update_h; y += format->block_height)
5009 struct block
5011 WORD alpha_row[4];
5012 WORD color[2];
5013 BYTE control_row[4];
5016 const struct block *s = (const struct block *)src_row;
5017 struct block *d = (struct block *)dst_row;
5019 for (x = 0; x < row_block_count; ++x)
5021 d[x].alpha_row[0] = s[x].alpha_row[3];
5022 d[x].alpha_row[1] = s[x].alpha_row[2];
5023 d[x].alpha_row[2] = s[x].alpha_row[1];
5024 d[x].alpha_row[3] = s[x].alpha_row[0];
5025 d[x].color[0] = s[x].color[0];
5026 d[x].color[1] = s[x].color[1];
5027 d[x].control_row[0] = s[x].control_row[3];
5028 d[x].control_row[1] = s[x].control_row[2];
5029 d[x].control_row[2] = s[x].control_row[1];
5030 d[x].control_row[3] = s[x].control_row[0];
5032 src_row -= src_pitch;
5033 dst_row += dst_pitch;
5035 return WINED3D_OK;
5037 default:
5038 FIXME("Compressed flip not implemented for format %s.\n",
5039 debug_d3dformat(format->id));
5040 return E_NOTIMPL;
5044 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
5045 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
5047 return E_NOTIMPL;
5050 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5051 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
5052 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5054 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
5055 const struct wined3d_format *src_format, *dst_format;
5056 struct wined3d_texture *src_texture = NULL;
5057 struct wined3d_map_desc dst_map, src_map;
5058 const BYTE *sbase = NULL;
5059 HRESULT hr = WINED3D_OK;
5060 const BYTE *sbuf;
5061 BYTE *dbuf;
5062 int x, y;
5064 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5065 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5066 flags, fx, debug_d3dtexturefiltertype(filter));
5068 if (src_surface == dst_surface)
5070 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
5071 src_map = dst_map;
5072 src_format = dst_surface->resource.format;
5073 dst_format = src_format;
5075 else
5077 dst_format = dst_surface->resource.format;
5078 if (src_surface)
5080 if (dst_surface->resource.format->id != src_surface->resource.format->id)
5082 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
5084 /* The conv function writes a FIXME */
5085 WARN("Cannot convert source surface format to dest format.\n");
5086 goto release;
5088 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
5090 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
5091 src_format = src_surface->resource.format;
5093 else
5095 src_format = dst_format;
5098 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
5101 bpp = dst_surface->resource.format->byte_count;
5102 srcheight = src_rect->bottom - src_rect->top;
5103 srcwidth = src_rect->right - src_rect->left;
5104 dstheight = dst_rect->bottom - dst_rect->top;
5105 dstwidth = dst_rect->right - dst_rect->left;
5106 width = (dst_rect->right - dst_rect->left) * bpp;
5108 if (src_surface)
5109 sbase = (BYTE *)src_map.data
5110 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
5111 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
5112 if (src_surface != dst_surface)
5113 dbuf = dst_map.data;
5114 else
5115 dbuf = (BYTE *)dst_map.data
5116 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
5117 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
5119 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
5121 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
5123 if (src_surface == dst_surface)
5125 FIXME("Only plain blits supported on compressed surfaces.\n");
5126 hr = E_NOTIMPL;
5127 goto release;
5130 if (srcheight != dstheight || srcwidth != dstwidth)
5132 WARN("Stretching not supported on compressed surfaces.\n");
5133 hr = WINED3DERR_INVALIDCALL;
5134 goto release;
5137 if (!surface_check_block_align(src_surface, src_rect))
5139 WARN("Source rectangle not block-aligned.\n");
5140 hr = WINED3DERR_INVALIDCALL;
5141 goto release;
5144 if (!surface_check_block_align(dst_surface, dst_rect))
5146 WARN("Destination rectangle not block-aligned.\n");
5147 hr = WINED3DERR_INVALIDCALL;
5148 goto release;
5151 hr = surface_cpu_blt_compressed(sbase, dbuf,
5152 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
5153 src_format, flags, fx);
5154 goto release;
5157 /* First, all the 'source-less' blits */
5158 if (flags & WINEDDBLT_COLORFILL)
5160 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
5161 flags &= ~WINEDDBLT_COLORFILL;
5164 if (flags & WINEDDBLT_DEPTHFILL)
5166 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
5168 if (flags & WINEDDBLT_ROP)
5170 /* Catch some degenerate cases here. */
5171 switch (fx->dwROP)
5173 case BLACKNESS:
5174 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
5175 break;
5176 case 0xaa0029: /* No-op */
5177 break;
5178 case WHITENESS:
5179 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
5180 break;
5181 case SRCCOPY: /* Well, we do that below? */
5182 break;
5183 default:
5184 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
5185 goto error;
5187 flags &= ~WINEDDBLT_ROP;
5189 if (flags & WINEDDBLT_DDROPS)
5191 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
5193 /* Now the 'with source' blits. */
5194 if (src_surface)
5196 int sx, xinc, sy, yinc;
5198 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
5199 goto release;
5201 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
5202 && (srcwidth != dstwidth || srcheight != dstheight))
5204 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
5205 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
5208 xinc = (srcwidth << 16) / dstwidth;
5209 yinc = (srcheight << 16) / dstheight;
5211 if (!flags)
5213 /* No effects, we can cheat here. */
5214 if (dstwidth == srcwidth)
5216 if (dstheight == srcheight)
5218 /* No stretching in either direction. This needs to be as
5219 * fast as possible. */
5220 sbuf = sbase;
5222 /* Check for overlapping surfaces. */
5223 if (src_surface != dst_surface || dst_rect->top < src_rect->top
5224 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
5226 /* No overlap, or dst above src, so copy from top downwards. */
5227 for (y = 0; y < dstheight; ++y)
5229 memcpy(dbuf, sbuf, width);
5230 sbuf += src_map.row_pitch;
5231 dbuf += dst_map.row_pitch;
5234 else if (dst_rect->top > src_rect->top)
5236 /* Copy from bottom upwards. */
5237 sbuf += src_map.row_pitch * dstheight;
5238 dbuf += dst_map.row_pitch * dstheight;
5239 for (y = 0; y < dstheight; ++y)
5241 sbuf -= src_map.row_pitch;
5242 dbuf -= dst_map.row_pitch;
5243 memcpy(dbuf, sbuf, width);
5246 else
5248 /* Src and dst overlapping on the same line, use memmove. */
5249 for (y = 0; y < dstheight; ++y)
5251 memmove(dbuf, sbuf, width);
5252 sbuf += src_map.row_pitch;
5253 dbuf += dst_map.row_pitch;
5257 else
5259 /* Stretching in y direction only. */
5260 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5262 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5263 memcpy(dbuf, sbuf, width);
5264 dbuf += dst_map.row_pitch;
5268 else
5270 /* Stretching in X direction. */
5271 int last_sy = -1;
5272 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5274 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5276 if ((sy >> 16) == (last_sy >> 16))
5278 /* This source row is the same as last source row -
5279 * Copy the already stretched row. */
5280 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
5282 else
5284 #define STRETCH_ROW(type) \
5285 do { \
5286 const type *s = (const type *)sbuf; \
5287 type *d = (type *)dbuf; \
5288 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5289 d[x] = s[sx >> 16]; \
5290 } while(0)
5292 switch(bpp)
5294 case 1:
5295 STRETCH_ROW(BYTE);
5296 break;
5297 case 2:
5298 STRETCH_ROW(WORD);
5299 break;
5300 case 4:
5301 STRETCH_ROW(DWORD);
5302 break;
5303 case 3:
5305 const BYTE *s;
5306 BYTE *d = dbuf;
5307 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
5309 DWORD pixel;
5311 s = sbuf + 3 * (sx >> 16);
5312 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5313 d[0] = (pixel ) & 0xff;
5314 d[1] = (pixel >> 8) & 0xff;
5315 d[2] = (pixel >> 16) & 0xff;
5316 d += 3;
5318 break;
5320 default:
5321 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
5322 hr = WINED3DERR_NOTAVAILABLE;
5323 goto error;
5325 #undef STRETCH_ROW
5327 dbuf += dst_map.row_pitch;
5328 last_sy = sy;
5332 else
5334 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
5335 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
5336 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
5337 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
5339 /* The color keying flags are checked for correctness in ddraw */
5340 if (flags & WINEDDBLT_KEYSRC)
5342 keylow = src_surface->container->src_blt_color_key.color_space_low_value;
5343 keyhigh = src_surface->container->src_blt_color_key.color_space_high_value;
5345 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5347 keylow = fx->ddckSrcColorkey.color_space_low_value;
5348 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
5351 if (flags & WINEDDBLT_KEYDEST)
5353 /* Destination color keys are taken from the source surface! */
5354 destkeylow = src_surface->container->dst_blt_color_key.color_space_low_value;
5355 destkeyhigh = src_surface->container->dst_blt_color_key.color_space_high_value;
5357 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
5359 destkeylow = fx->ddckDestColorkey.color_space_low_value;
5360 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
5363 if (bpp == 1)
5365 keymask = 0xff;
5367 else
5369 DWORD masks[3];
5370 get_color_masks(src_format, masks);
5371 keymask = masks[0]
5372 | masks[1]
5373 | masks[2];
5375 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
5378 if (flags & WINEDDBLT_DDFX)
5380 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
5381 LONG tmpxy;
5382 dTopLeft = dbuf;
5383 dTopRight = dbuf + ((dstwidth - 1) * bpp);
5384 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
5385 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
5387 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
5389 /* I don't think we need to do anything about this flag */
5390 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
5392 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
5394 tmp = dTopRight;
5395 dTopRight = dTopLeft;
5396 dTopLeft = tmp;
5397 tmp = dBottomRight;
5398 dBottomRight = dBottomLeft;
5399 dBottomLeft = tmp;
5400 dstxinc = dstxinc * -1;
5402 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
5404 tmp = dTopLeft;
5405 dTopLeft = dBottomLeft;
5406 dBottomLeft = tmp;
5407 tmp = dTopRight;
5408 dTopRight = dBottomRight;
5409 dBottomRight = tmp;
5410 dstyinc = dstyinc * -1;
5412 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
5414 /* I don't think we need to do anything about this flag */
5415 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
5417 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
5419 tmp = dBottomRight;
5420 dBottomRight = dTopLeft;
5421 dTopLeft = tmp;
5422 tmp = dBottomLeft;
5423 dBottomLeft = dTopRight;
5424 dTopRight = tmp;
5425 dstxinc = dstxinc * -1;
5426 dstyinc = dstyinc * -1;
5428 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
5430 tmp = dTopLeft;
5431 dTopLeft = dBottomLeft;
5432 dBottomLeft = dBottomRight;
5433 dBottomRight = dTopRight;
5434 dTopRight = tmp;
5435 tmpxy = dstxinc;
5436 dstxinc = dstyinc;
5437 dstyinc = tmpxy;
5438 dstxinc = dstxinc * -1;
5440 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
5442 tmp = dTopLeft;
5443 dTopLeft = dTopRight;
5444 dTopRight = dBottomRight;
5445 dBottomRight = dBottomLeft;
5446 dBottomLeft = tmp;
5447 tmpxy = dstxinc;
5448 dstxinc = dstyinc;
5449 dstyinc = tmpxy;
5450 dstyinc = dstyinc * -1;
5452 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
5454 /* I don't think we need to do anything about this flag */
5455 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
5457 dbuf = dTopLeft;
5458 flags &= ~(WINEDDBLT_DDFX);
5461 #define COPY_COLORKEY_FX(type) \
5462 do { \
5463 const type *s; \
5464 type *d = (type *)dbuf, *dx, tmp; \
5465 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
5467 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
5468 dx = d; \
5469 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5471 tmp = s[sx >> 16]; \
5472 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
5473 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
5475 dx[0] = tmp; \
5477 dx = (type *)(((BYTE *)dx) + dstxinc); \
5479 d = (type *)(((BYTE *)d) + dstyinc); \
5481 } while(0)
5483 switch (bpp)
5485 case 1:
5486 COPY_COLORKEY_FX(BYTE);
5487 break;
5488 case 2:
5489 COPY_COLORKEY_FX(WORD);
5490 break;
5491 case 4:
5492 COPY_COLORKEY_FX(DWORD);
5493 break;
5494 case 3:
5496 const BYTE *s;
5497 BYTE *d = dbuf, *dx;
5498 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5500 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5501 dx = d;
5502 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
5504 DWORD pixel, dpixel = 0;
5505 s = sbuf + 3 * (sx>>16);
5506 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5507 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
5508 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
5509 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
5511 dx[0] = (pixel ) & 0xff;
5512 dx[1] = (pixel >> 8) & 0xff;
5513 dx[2] = (pixel >> 16) & 0xff;
5515 dx += dstxinc;
5517 d += dstyinc;
5519 break;
5521 default:
5522 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
5523 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
5524 hr = WINED3DERR_NOTAVAILABLE;
5525 goto error;
5526 #undef COPY_COLORKEY_FX
5531 error:
5532 if (flags && FIXME_ON(d3d_surface))
5534 FIXME("\tUnsupported flags: %#x.\n", flags);
5537 release:
5538 wined3d_surface_unmap(dst_surface);
5539 if (src_surface && src_surface != dst_surface)
5540 wined3d_surface_unmap(src_surface);
5541 /* Release the converted surface, if any. */
5542 if (src_texture)
5543 wined3d_texture_decref(src_texture);
5545 return hr;
5548 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5549 const RECT *dst_rect, const struct wined3d_color *color)
5551 static const RECT src_rect;
5552 WINEDDBLTFX BltFx;
5554 memset(&BltFx, 0, sizeof(BltFx));
5555 BltFx.dwSize = sizeof(BltFx);
5556 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
5557 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
5558 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
5561 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
5562 struct wined3d_surface *surface, const RECT *rect, float depth)
5564 FIXME("Depth filling not implemented by cpu_blit.\n");
5565 return WINED3DERR_INVALIDCALL;
5568 const struct blit_shader cpu_blit = {
5569 cpu_blit_alloc,
5570 cpu_blit_free,
5571 cpu_blit_set,
5572 cpu_blit_unset,
5573 cpu_blit_supported,
5574 cpu_blit_color_fill,
5575 cpu_blit_depth_fill,
5578 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5579 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5580 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5582 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5583 struct wined3d_device *device = dst_surface->resource.device;
5584 DWORD src_ds_flags, dst_ds_flags;
5585 RECT src_rect, dst_rect;
5586 BOOL scale, convert;
5587 enum wined3d_conversion_type dst_convert_type;
5588 struct wined3d_format dst_conv_fmt;
5590 static const DWORD simple_blit = WINEDDBLT_ASYNC
5591 | WINEDDBLT_COLORFILL
5592 | WINEDDBLT_WAIT
5593 | WINEDDBLT_DEPTHFILL
5594 | WINEDDBLT_DONOTWAIT;
5596 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5597 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
5598 flags, fx, debug_d3dtexturefiltertype(filter));
5599 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5601 if (fx)
5603 TRACE("dwSize %#x.\n", fx->dwSize);
5604 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5605 TRACE("dwROP %#x.\n", fx->dwROP);
5606 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5607 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5608 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5609 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5610 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5611 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5612 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5613 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5614 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5615 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5616 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5617 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5618 TRACE("dwReserved %#x.\n", fx->dwReserved);
5619 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5620 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5621 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5622 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5623 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5624 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5625 fx->ddckDestColorkey.color_space_low_value,
5626 fx->ddckDestColorkey.color_space_high_value);
5627 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5628 fx->ddckSrcColorkey.color_space_low_value,
5629 fx->ddckSrcColorkey.color_space_high_value);
5632 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5634 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5635 return WINEDDERR_SURFACEBUSY;
5638 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5640 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5641 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5642 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5643 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5644 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5646 WARN("The application gave us a bad destination rectangle.\n");
5647 return WINEDDERR_INVALIDRECT;
5650 if (src_surface)
5652 surface_get_rect(src_surface, src_rect_in, &src_rect);
5654 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5655 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5656 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5657 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5658 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5660 WARN("Application gave us bad source rectangle for Blt.\n");
5661 return WINEDDERR_INVALIDRECT;
5664 else
5666 memset(&src_rect, 0, sizeof(src_rect));
5669 if (!fx || !(fx->dwDDFX))
5670 flags &= ~WINEDDBLT_DDFX;
5672 if (flags & WINEDDBLT_WAIT)
5673 flags &= ~WINEDDBLT_WAIT;
5675 if (flags & WINEDDBLT_ASYNC)
5677 static unsigned int once;
5679 if (!once++)
5680 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5681 flags &= ~WINEDDBLT_ASYNC;
5684 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5685 if (flags & WINEDDBLT_DONOTWAIT)
5687 static unsigned int once;
5689 if (!once++)
5690 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5691 flags &= ~WINEDDBLT_DONOTWAIT;
5694 if (!device->d3d_initialized)
5696 WARN("D3D not initialized, using fallback.\n");
5697 goto cpu;
5700 /* We want to avoid invalidating the sysmem location for converted
5701 * surfaces, since otherwise we'd have to convert the data back when
5702 * locking them. */
5703 d3dfmt_get_conv(dst_surface, TRUE, TRUE, &dst_conv_fmt, &dst_convert_type);
5704 if (dst_convert_type != WINED3D_CT_NONE || dst_conv_fmt.convert || dst_surface->flags & SFLAG_CONVERTED)
5706 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5707 goto cpu;
5710 if (flags & ~simple_blit)
5712 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5713 goto fallback;
5716 if (src_surface)
5717 src_swapchain = src_surface->container->swapchain;
5718 else
5719 src_swapchain = NULL;
5721 dst_swapchain = dst_surface->container->swapchain;
5723 /* This isn't strictly needed. FBO blits for example could deal with
5724 * cross-swapchain blits by first downloading the source to a texture
5725 * before switching to the destination context. We just have this here to
5726 * not have to deal with the issue, since cross-swapchain blits should be
5727 * rare. */
5728 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5730 FIXME("Using fallback for cross-swapchain blit.\n");
5731 goto fallback;
5734 scale = src_surface
5735 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5736 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5737 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5739 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5740 if (src_surface)
5741 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5742 else
5743 src_ds_flags = 0;
5745 if (src_ds_flags || dst_ds_flags)
5747 if (flags & WINEDDBLT_DEPTHFILL)
5749 float depth;
5751 TRACE("Depth fill.\n");
5753 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5754 return WINED3DERR_INVALIDCALL;
5756 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
5757 return WINED3D_OK;
5759 else
5761 if (src_ds_flags != dst_ds_flags)
5763 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
5764 return WINED3DERR_INVALIDCALL;
5767 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
5768 &src_rect, dst_surface, dst_surface->container->resource.draw_binding, &dst_rect)))
5769 return WINED3D_OK;
5772 else
5774 /* In principle this would apply to depth blits as well, but we don't
5775 * implement those in the CPU blitter at the moment. */
5776 if ((dst_surface->locations & dst_surface->resource.map_binding)
5777 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
5779 if (scale)
5780 TRACE("Not doing sysmem blit because of scaling.\n");
5781 else if (convert)
5782 TRACE("Not doing sysmem blit because of format conversion.\n");
5783 else
5784 goto cpu;
5787 if (flags & WINEDDBLT_COLORFILL)
5789 struct wined3d_color color;
5791 TRACE("Color fill.\n");
5793 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
5794 goto fallback;
5796 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
5797 return WINED3D_OK;
5799 else
5801 TRACE("Color blit.\n");
5803 /* Upload */
5804 if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5805 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5807 if (scale)
5808 TRACE("Not doing upload because of scaling.\n");
5809 else if (convert)
5810 TRACE("Not doing upload because of format conversion.\n");
5811 else
5813 POINT dst_point = {dst_rect.left, dst_rect.top};
5815 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
5817 if (!wined3d_resource_is_offscreen(&dst_surface->resource))
5818 surface_load_location(dst_surface, dst_surface->container->resource.draw_binding);
5819 return WINED3D_OK;
5824 /* Use present for back -> front blits. The idea behind this is
5825 * that present is potentially faster than a blit, in particular
5826 * when FBO blits aren't available. Some ddraw applications like
5827 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5828 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5829 * applications can't blit directly to the frontbuffer. */
5830 if (dst_swapchain && dst_swapchain->back_buffers
5831 && dst_surface == dst_swapchain->front_buffer
5832 && src_surface == dst_swapchain->back_buffers[0])
5834 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5836 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5838 /* Set the swap effect to COPY, we don't want the backbuffer
5839 * to become undefined. */
5840 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5841 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5842 dst_swapchain->desc.swap_effect = swap_effect;
5844 return WINED3D_OK;
5847 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5848 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5849 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5851 TRACE("Using FBO blit.\n");
5853 surface_blt_fbo(device, filter,
5854 src_surface, src_surface->container->resource.draw_binding, &src_rect,
5855 dst_surface, dst_surface->container->resource.draw_binding, &dst_rect);
5856 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5857 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5859 return WINED3D_OK;
5862 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5863 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5864 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5866 TRACE("Using arbfp blit.\n");
5868 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
5869 return WINED3D_OK;
5874 fallback:
5875 /* Special cases for render targets. */
5876 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
5877 return WINED3D_OK;
5879 cpu:
5881 /* For the rest call the X11 surface implementation. For render targets
5882 * this should be implemented OpenGL accelerated in surface_blt_special(),
5883 * other blits are rather rare. */
5884 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
5887 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5888 const struct wined3d_resource_desc *desc, GLenum target, GLint level, DWORD flags)
5890 struct wined3d_device *device = container->resource.device;
5891 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5892 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5893 UINT multisample_quality = desc->multisample_quality;
5894 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
5895 unsigned int resource_size;
5896 HRESULT hr;
5898 if (multisample_quality > 0)
5900 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
5901 multisample_quality = 0;
5904 /* Quick lockable sanity check.
5905 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5906 * this function is too deep to need to care about things like this.
5907 * Levels need to be checked too, since they all affect what can be done. */
5908 switch (desc->pool)
5910 case WINED3D_POOL_MANAGED:
5911 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5912 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5913 break;
5915 case WINED3D_POOL_DEFAULT:
5916 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5917 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5918 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5919 break;
5921 case WINED3D_POOL_SCRATCH:
5922 case WINED3D_POOL_SYSTEM_MEM:
5923 break;
5925 default:
5926 FIXME("Unknown pool %#x.\n", desc->pool);
5927 break;
5930 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5931 FIXME("Trying to create a render target that isn't in the default pool.\n");
5933 /* FIXME: Check that the format is supported by the device. */
5935 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5936 if (!resource_size)
5937 return WINED3DERR_INVALIDCALL;
5939 if (device->wined3d->flags & WINED3D_NO3D)
5940 surface->surface_ops = &gdi_surface_ops;
5941 else
5942 surface->surface_ops = &surface_ops;
5944 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
5945 desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height, 1,
5946 resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5948 WARN("Failed to initialize resource, returning %#x.\n", hr);
5949 return hr;
5952 surface->container = container;
5953 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5954 list_init(&surface->renderbuffers);
5955 list_init(&surface->overlays);
5957 /* Flags */
5958 if (target != GL_TEXTURE_RECTANGLE_ARB)
5959 surface->flags |= SFLAG_NORMCOORD;
5960 if (flags & WINED3D_SURFACE_DISCARD)
5961 surface->flags |= SFLAG_DISCARD;
5962 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
5963 surface->flags |= SFLAG_PIN_SYSMEM;
5964 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5965 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5967 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
5968 surface->texture_target = target;
5969 surface->texture_level = level;
5971 /* Call the private setup routine */
5972 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5974 ERR("Private setup failed, hr %#x.\n", hr);
5975 surface_cleanup(surface);
5976 return hr;
5979 /* Similar to lockable rendertargets above, creating the DIB section
5980 * during surface initialization prevents the sysmem pointer from changing
5981 * after a wined3d_surface_getdc() call. */
5982 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5983 && SUCCEEDED(surface_create_dib_section(surface)))
5984 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5986 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5988 wined3d_resource_free_sysmem(&surface->resource);
5989 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5990 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5993 return hr;
5996 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5997 GLenum target, GLint level, DWORD flags, struct wined3d_surface **surface)
5999 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
6000 const struct wined3d_parent_ops *parent_ops;
6001 struct wined3d_surface *object;
6002 void *parent;
6003 HRESULT hr;
6005 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
6006 "multisample_type %#x, multisample_quality %u, target %#x, level %d, flags %#x, surface %p.\n",
6007 container, desc->width, desc->height, debug_d3dformat(desc->format),
6008 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
6009 desc->multisample_type, desc->multisample_quality, target, level, flags, surface);
6011 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
6012 return E_OUTOFMEMORY;
6014 if (FAILED(hr = surface_init(object, container, desc, target, level, flags)))
6016 WARN("Failed to initialize surface, returning %#x.\n", hr);
6017 HeapFree(GetProcessHeap(), 0, object);
6018 return hr;
6021 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
6022 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
6024 WARN("Failed to create surface parent, hr %#x.\n", hr);
6025 wined3d_surface_destroy(object);
6026 return hr;
6029 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
6031 object->resource.parent = parent;
6032 object->resource.parent_ops = parent_ops;
6033 *surface = object;
6035 return hr;