po: Fix Finnish translation.
[wine/multimedia.git] / dlls / wined3d / surface.c
blob30dc63eeefac1b986cbeabc618ccca81821a0dde
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 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);
36 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
37 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
38 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter);
39 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
40 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *fx,
41 enum wined3d_texture_filter_type filter);
43 static void surface_cleanup(struct wined3d_surface *surface)
45 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->texture_name || (surface->flags & SFLAG_PBO)
50 || surface->rb_multisample || surface->rb_resolved
51 || !list_empty(&surface->renderbuffers))
53 struct wined3d_renderbuffer_entry *entry, *entry2;
54 const struct wined3d_gl_info *gl_info;
55 struct wined3d_context *context;
57 context = context_acquire(surface->resource.device, NULL);
58 gl_info = context->gl_info;
60 ENTER_GL();
62 if (surface->texture_name)
64 TRACE("Deleting texture %u.\n", surface->texture_name);
65 glDeleteTextures(1, &surface->texture_name);
68 if (surface->flags & SFLAG_PBO)
70 TRACE("Deleting PBO %u.\n", surface->pbo);
71 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
74 if (surface->rb_multisample)
76 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
77 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
80 if (surface->rb_resolved)
82 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
83 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
86 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
88 TRACE("Deleting renderbuffer %u.\n", entry->id);
89 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
90 HeapFree(GetProcessHeap(), 0, entry);
93 LEAVE_GL();
95 context_release(context);
98 if (surface->flags & SFLAG_DIBSECTION)
100 DeleteDC(surface->hDC);
101 DeleteObject(surface->dib.DIBsection);
102 surface->dib.bitmap_data = NULL;
103 surface->resource.allocatedMemory = NULL;
106 if (surface->flags & SFLAG_USERPTR)
107 wined3d_surface_set_mem(surface, NULL);
108 if (surface->overlay_dest)
109 list_remove(&surface->overlay_entry);
111 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
113 list_remove(&overlay->overlay_entry);
114 overlay->overlay_dest = NULL;
117 resource_cleanup(&surface->resource);
120 void surface_update_draw_binding(struct wined3d_surface *surface)
122 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
123 surface->draw_binding = SFLAG_INDRAWABLE;
124 else if (surface->resource.multisample_type)
125 surface->draw_binding = SFLAG_INRB_MULTISAMPLE;
126 else
127 surface->draw_binding = SFLAG_INTEXTURE;
130 void surface_set_container(struct wined3d_surface *surface, enum wined3d_container_type type, void *container)
132 TRACE("surface %p, container %p.\n", surface, container);
134 if (!container && type != WINED3D_CONTAINER_NONE)
135 ERR("Setting NULL container of type %#x.\n", type);
137 if (type == WINED3D_CONTAINER_SWAPCHAIN)
139 surface->get_drawable_size = get_drawable_size_swapchain;
141 else
143 switch (wined3d_settings.offscreen_rendering_mode)
145 case ORM_FBO:
146 surface->get_drawable_size = get_drawable_size_fbo;
147 break;
149 case ORM_BACKBUFFER:
150 surface->get_drawable_size = get_drawable_size_backbuffer;
151 break;
153 default:
154 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
155 return;
159 surface->container.type = type;
160 surface->container.u.base = container;
161 surface_update_draw_binding(surface);
164 struct blt_info
166 GLenum binding;
167 GLenum bind_target;
168 enum tex_types tex_type;
169 GLfloat coords[4][3];
172 struct float_rect
174 float l;
175 float t;
176 float r;
177 float b;
180 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
182 f->l = ((r->left * 2.0f) / w) - 1.0f;
183 f->t = ((r->top * 2.0f) / h) - 1.0f;
184 f->r = ((r->right * 2.0f) / w) - 1.0f;
185 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
188 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
190 GLfloat (*coords)[3] = info->coords;
191 struct float_rect f;
193 switch (target)
195 default:
196 FIXME("Unsupported texture target %#x\n", target);
197 /* Fall back to GL_TEXTURE_2D */
198 case GL_TEXTURE_2D:
199 info->binding = GL_TEXTURE_BINDING_2D;
200 info->bind_target = GL_TEXTURE_2D;
201 info->tex_type = tex_2d;
202 coords[0][0] = (float)rect->left / w;
203 coords[0][1] = (float)rect->top / h;
204 coords[0][2] = 0.0f;
206 coords[1][0] = (float)rect->right / w;
207 coords[1][1] = (float)rect->top / h;
208 coords[1][2] = 0.0f;
210 coords[2][0] = (float)rect->left / w;
211 coords[2][1] = (float)rect->bottom / h;
212 coords[2][2] = 0.0f;
214 coords[3][0] = (float)rect->right / w;
215 coords[3][1] = (float)rect->bottom / h;
216 coords[3][2] = 0.0f;
217 break;
219 case GL_TEXTURE_RECTANGLE_ARB:
220 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
221 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
222 info->tex_type = tex_rect;
223 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
224 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
225 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
226 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
227 break;
229 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
230 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
231 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
232 info->tex_type = tex_cube;
233 cube_coords_float(rect, w, h, &f);
235 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
236 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
237 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
238 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
239 break;
241 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
242 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
243 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
244 info->tex_type = tex_cube;
245 cube_coords_float(rect, w, h, &f);
247 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
248 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
249 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
250 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
251 break;
253 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
254 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
255 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
256 info->tex_type = tex_cube;
257 cube_coords_float(rect, w, h, &f);
259 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
260 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
261 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
262 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
263 break;
265 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
266 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
267 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
268 info->tex_type = tex_cube;
269 cube_coords_float(rect, w, h, &f);
271 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
272 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
273 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
274 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
275 break;
277 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
278 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
279 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
280 info->tex_type = tex_cube;
281 cube_coords_float(rect, w, h, &f);
283 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
284 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
285 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
286 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
287 break;
289 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
290 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
291 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
292 info->tex_type = tex_cube;
293 cube_coords_float(rect, w, h, &f);
295 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
296 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
297 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
298 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
299 break;
303 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
305 if (rect_in)
306 *rect_out = *rect_in;
307 else
309 rect_out->left = 0;
310 rect_out->top = 0;
311 rect_out->right = surface->resource.width;
312 rect_out->bottom = surface->resource.height;
316 /* GL locking and context activation is done by the caller */
317 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
318 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
320 struct blt_info info;
322 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
324 glEnable(info.bind_target);
325 checkGLcall("glEnable(bind_target)");
327 context_bind_texture(context, info.bind_target, src_surface->texture_name);
329 /* Filtering for StretchRect */
330 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
331 wined3d_gl_mag_filter(magLookup, filter));
332 checkGLcall("glTexParameteri");
333 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
334 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
335 checkGLcall("glTexParameteri");
336 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
337 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
338 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
339 glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
340 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
341 checkGLcall("glTexEnvi");
343 /* Draw a quad */
344 glBegin(GL_TRIANGLE_STRIP);
345 glTexCoord3fv(info.coords[0]);
346 glVertex2i(dst_rect->left, dst_rect->top);
348 glTexCoord3fv(info.coords[1]);
349 glVertex2i(dst_rect->right, dst_rect->top);
351 glTexCoord3fv(info.coords[2]);
352 glVertex2i(dst_rect->left, dst_rect->bottom);
354 glTexCoord3fv(info.coords[3]);
355 glVertex2i(dst_rect->right, dst_rect->bottom);
356 glEnd();
358 /* Unbind the texture */
359 context_bind_texture(context, info.bind_target, 0);
361 /* We changed the filtering settings on the texture. Inform the
362 * container about this to get the filters reset properly next draw. */
363 if (src_surface->container.type == WINED3D_CONTAINER_TEXTURE)
365 struct wined3d_texture *texture = src_surface->container.u.texture;
366 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_POINT;
367 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
368 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_NONE;
369 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
373 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
375 const struct wined3d_format *format = surface->resource.format;
376 SYSTEM_INFO sysInfo;
377 BITMAPINFO *b_info;
378 int extraline = 0;
379 DWORD *masks;
380 UINT usage;
381 HDC dc;
383 TRACE("surface %p.\n", surface);
385 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
387 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
388 return WINED3DERR_INVALIDCALL;
391 switch (format->byte_count)
393 case 2:
394 case 4:
395 /* Allocate extra space to store the RGB bit masks. */
396 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
397 break;
399 case 3:
400 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
401 break;
403 default:
404 /* Allocate extra space for a palette. */
405 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
406 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
407 break;
410 if (!b_info)
411 return E_OUTOFMEMORY;
413 /* Some applications access the surface in via DWORDs, and do not take
414 * the necessary care at the end of the surface. So we need at least
415 * 4 extra bytes at the end of the surface. Check against the page size,
416 * if the last page used for the surface has at least 4 spare bytes we're
417 * safe, otherwise add an extra line to the DIB section. */
418 GetSystemInfo(&sysInfo);
419 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
421 extraline = 1;
422 TRACE("Adding an extra line to the DIB section.\n");
425 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
426 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
427 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
428 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
429 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
430 * wined3d_surface_get_pitch(surface);
431 b_info->bmiHeader.biPlanes = 1;
432 b_info->bmiHeader.biBitCount = format->byte_count * 8;
434 b_info->bmiHeader.biXPelsPerMeter = 0;
435 b_info->bmiHeader.biYPelsPerMeter = 0;
436 b_info->bmiHeader.biClrUsed = 0;
437 b_info->bmiHeader.biClrImportant = 0;
439 /* Get the bit masks */
440 masks = (DWORD *)b_info->bmiColors;
441 switch (surface->resource.format->id)
443 case WINED3DFMT_B8G8R8_UNORM:
444 usage = DIB_RGB_COLORS;
445 b_info->bmiHeader.biCompression = BI_RGB;
446 break;
448 case WINED3DFMT_B5G5R5X1_UNORM:
449 case WINED3DFMT_B5G5R5A1_UNORM:
450 case WINED3DFMT_B4G4R4A4_UNORM:
451 case WINED3DFMT_B4G4R4X4_UNORM:
452 case WINED3DFMT_B2G3R3_UNORM:
453 case WINED3DFMT_B2G3R3A8_UNORM:
454 case WINED3DFMT_R10G10B10A2_UNORM:
455 case WINED3DFMT_R8G8B8A8_UNORM:
456 case WINED3DFMT_R8G8B8X8_UNORM:
457 case WINED3DFMT_B10G10R10A2_UNORM:
458 case WINED3DFMT_B5G6R5_UNORM:
459 case WINED3DFMT_R16G16B16A16_UNORM:
460 usage = 0;
461 b_info->bmiHeader.biCompression = BI_BITFIELDS;
462 masks[0] = format->red_mask;
463 masks[1] = format->green_mask;
464 masks[2] = format->blue_mask;
465 break;
467 default:
468 /* Don't know palette */
469 b_info->bmiHeader.biCompression = BI_RGB;
470 usage = 0;
471 break;
474 if (!(dc = GetDC(0)))
476 HeapFree(GetProcessHeap(), 0, b_info);
477 return HRESULT_FROM_WIN32(GetLastError());
480 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
481 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
482 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
483 surface->dib.DIBsection = CreateDIBSection(dc, b_info, usage, &surface->dib.bitmap_data, 0, 0);
484 ReleaseDC(0, dc);
486 if (!surface->dib.DIBsection)
488 ERR("Failed to create DIB section.\n");
489 HeapFree(GetProcessHeap(), 0, b_info);
490 return HRESULT_FROM_WIN32(GetLastError());
493 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
494 /* Copy the existing surface to the dib section. */
495 if (surface->resource.allocatedMemory)
497 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
498 surface->resource.height * wined3d_surface_get_pitch(surface));
500 else
502 /* This is to make maps read the GL texture although memory is allocated. */
503 surface->flags &= ~SFLAG_INSYSMEM;
505 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
507 HeapFree(GetProcessHeap(), 0, b_info);
509 /* Now allocate a DC. */
510 surface->hDC = CreateCompatibleDC(0);
511 SelectObject(surface->hDC, surface->dib.DIBsection);
512 TRACE("Using wined3d palette %p.\n", surface->palette);
513 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
515 surface->flags |= SFLAG_DIBSECTION;
517 return WINED3D_OK;
520 static BOOL surface_need_pbo(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
522 if (surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
523 return FALSE;
524 if (!(surface->flags & SFLAG_DYNLOCK))
525 return FALSE;
526 if (surface->flags & (SFLAG_CONVERTED | SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM))
527 return FALSE;
528 if (!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT])
529 return FALSE;
531 return TRUE;
534 static void surface_load_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
536 struct wined3d_context *context;
537 GLenum error;
539 context = context_acquire(surface->resource.device, NULL);
540 ENTER_GL();
542 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
543 error = glGetError();
544 if (!surface->pbo || error != GL_NO_ERROR)
545 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
547 TRACE("Binding PBO %u.\n", surface->pbo);
549 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
550 checkGLcall("glBindBufferARB");
552 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
553 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
554 checkGLcall("glBufferDataARB");
556 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
557 checkGLcall("glBindBufferARB");
559 /* We don't need the system memory anymore and we can't even use it for PBOs. */
560 if (!(surface->flags & SFLAG_CLIENT))
562 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
563 surface->resource.heapMemory = NULL;
565 surface->resource.allocatedMemory = NULL;
566 surface->flags |= SFLAG_PBO;
567 LEAVE_GL();
568 context_release(context);
571 static void surface_prepare_system_memory(struct wined3d_surface *surface)
573 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
575 TRACE("surface %p.\n", surface);
577 if (!(surface->flags & SFLAG_PBO) && surface_need_pbo(surface, gl_info))
578 surface_load_pbo(surface, gl_info);
579 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
581 /* Whatever surface we have, make sure that there is memory allocated
582 * for the downloaded copy, or a PBO to map. */
583 if (!surface->resource.heapMemory)
584 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
586 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
587 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
589 if (surface->flags & SFLAG_INSYSMEM)
590 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
594 static void surface_evict_sysmem(struct wined3d_surface *surface)
596 if (surface->flags & SFLAG_DONOTFREE)
597 return;
599 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
600 surface->resource.allocatedMemory = NULL;
601 surface->resource.heapMemory = NULL;
602 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
605 /* Context activation is done by the caller. */
606 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
607 struct wined3d_context *context, BOOL srgb)
609 struct wined3d_device *device = surface->resource.device;
610 DWORD active_sampler;
612 /* We don't need a specific texture unit, but after binding the texture
613 * the current unit is dirty. Read the unit back instead of switching to
614 * 0, this avoids messing around with the state manager's GL states. The
615 * current texture unit should always be a valid one.
617 * To be more specific, this is tricky because we can implicitly be
618 * called from sampler() in state.c. This means we can't touch anything
619 * other than whatever happens to be the currently active texture, or we
620 * would risk marking already applied sampler states dirty again. */
621 active_sampler = device->rev_tex_unit_map[context->active_texture];
623 if (active_sampler != WINED3D_UNMAPPED_STAGE)
624 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
625 surface_bind(surface, context, srgb);
628 static void surface_force_reload(struct wined3d_surface *surface)
630 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
633 static void surface_release_client_storage(struct wined3d_surface *surface)
635 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
637 ENTER_GL();
638 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
639 if (surface->texture_name)
641 surface_bind_and_dirtify(surface, context, FALSE);
642 glTexImage2D(surface->texture_target, surface->texture_level,
643 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
645 if (surface->texture_name_srgb)
647 surface_bind_and_dirtify(surface, context, TRUE);
648 glTexImage2D(surface->texture_target, surface->texture_level,
649 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
651 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
652 LEAVE_GL();
654 context_release(context);
656 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
657 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
658 surface_force_reload(surface);
661 static HRESULT surface_private_setup(struct wined3d_surface *surface)
663 /* TODO: Check against the maximum texture sizes supported by the video card. */
664 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
665 unsigned int pow2Width, pow2Height;
667 TRACE("surface %p.\n", surface);
669 surface->texture_name = 0;
670 surface->texture_target = GL_TEXTURE_2D;
672 /* Non-power2 support */
673 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
675 pow2Width = surface->resource.width;
676 pow2Height = surface->resource.height;
678 else
680 /* Find the nearest pow2 match */
681 pow2Width = pow2Height = 1;
682 while (pow2Width < surface->resource.width)
683 pow2Width <<= 1;
684 while (pow2Height < surface->resource.height)
685 pow2Height <<= 1;
687 surface->pow2Width = pow2Width;
688 surface->pow2Height = pow2Height;
690 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
692 /* TODO: Add support for non power two compressed textures. */
693 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
695 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
696 surface, surface->resource.width, surface->resource.height);
697 return WINED3DERR_NOTAVAILABLE;
701 if (pow2Width != surface->resource.width
702 || pow2Height != surface->resource.height)
704 surface->flags |= SFLAG_NONPOW2;
707 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
708 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
710 /* One of three options:
711 * 1: Do the same as we do with NPOT and scale the texture, (any
712 * texture ops would require the texture to be scaled which is
713 * potentially slow)
714 * 2: Set the texture to the maximum size (bad idea).
715 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
716 * 4: Create the surface, but allow it to be used only for DirectDraw
717 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
718 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
719 * the render target. */
720 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
722 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
723 return WINED3DERR_NOTAVAILABLE;
726 /* We should never use this surface in combination with OpenGL! */
727 TRACE("Creating an oversized surface: %ux%u.\n",
728 surface->pow2Width, surface->pow2Height);
730 else
732 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
733 * and EXT_PALETTED_TEXTURE is used in combination with texture
734 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
735 * EXT_PALETTED_TEXTURE doesn't work in combination with
736 * ARB_TEXTURE_RECTANGLE. */
737 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
738 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
739 && gl_info->supported[EXT_PALETTED_TEXTURE]
740 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
742 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
743 surface->pow2Width = surface->resource.width;
744 surface->pow2Height = surface->resource.height;
745 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
749 switch (wined3d_settings.offscreen_rendering_mode)
751 case ORM_FBO:
752 surface->get_drawable_size = get_drawable_size_fbo;
753 break;
755 case ORM_BACKBUFFER:
756 surface->get_drawable_size = get_drawable_size_backbuffer;
757 break;
759 default:
760 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
761 return WINED3DERR_INVALIDCALL;
764 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
765 surface->flags |= SFLAG_DISCARDED;
767 return WINED3D_OK;
770 static void surface_realize_palette(struct wined3d_surface *surface)
772 struct wined3d_palette *palette = surface->palette;
774 TRACE("surface %p.\n", surface);
776 if (!palette) return;
778 if (surface->resource.format->id == WINED3DFMT_P8_UINT
779 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
781 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
783 /* Make sure the texture is up to date. This call doesn't do
784 * anything if the texture is already up to date. */
785 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
787 /* We want to force a palette refresh, so mark the drawable as not being up to date */
788 if (!surface_is_offscreen(surface))
789 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
791 else
793 if (!(surface->flags & SFLAG_INSYSMEM))
795 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
796 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
798 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
802 if (surface->flags & SFLAG_DIBSECTION)
804 RGBQUAD col[256];
805 unsigned int i;
807 TRACE("Updating the DC's palette.\n");
809 for (i = 0; i < 256; ++i)
811 col[i].rgbRed = palette->palents[i].peRed;
812 col[i].rgbGreen = palette->palents[i].peGreen;
813 col[i].rgbBlue = palette->palents[i].peBlue;
814 col[i].rgbReserved = 0;
816 SetDIBColorTable(surface->hDC, 0, 256, col);
819 /* Propagate the changes to the drawable when we have a palette. */
820 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
821 surface_load_location(surface, surface->draw_binding, NULL);
824 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
826 HRESULT hr;
828 /* If there's no destination surface there is nothing to do. */
829 if (!surface->overlay_dest)
830 return WINED3D_OK;
832 /* Blt calls ModifyLocation on the dest surface, which in turn calls
833 * DrawOverlay to update the overlay. Prevent an endless recursion. */
834 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
835 return WINED3D_OK;
837 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
838 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
839 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3D_TEXF_LINEAR);
840 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
842 return hr;
845 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
847 struct wined3d_device *device = surface->resource.device;
848 const RECT *pass_rect = rect;
850 TRACE("surface %p, rect %s, flags %#x.\n",
851 surface, wine_dbgstr_rect(rect), flags);
853 if (flags & WINED3DLOCK_DISCARD)
855 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
856 surface_prepare_system_memory(surface);
857 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
859 else
861 /* surface_load_location() does not check if the rectangle specifies
862 * the full surface. Most callers don't need that, so do it here. */
863 if (rect && !rect->top && !rect->left
864 && rect->right == surface->resource.width
865 && rect->bottom == surface->resource.height)
866 pass_rect = NULL;
867 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
870 if (surface->flags & SFLAG_PBO)
872 const struct wined3d_gl_info *gl_info;
873 struct wined3d_context *context;
875 context = context_acquire(device, NULL);
876 gl_info = context->gl_info;
878 ENTER_GL();
879 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
880 checkGLcall("glBindBufferARB");
882 /* This shouldn't happen but could occur if some other function
883 * didn't handle the PBO properly. */
884 if (surface->resource.allocatedMemory)
885 ERR("The surface already has PBO memory allocated.\n");
887 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
888 checkGLcall("glMapBufferARB");
890 /* Make sure the PBO isn't set anymore in order not to break non-PBO
891 * calls. */
892 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
893 checkGLcall("glBindBufferARB");
895 LEAVE_GL();
896 context_release(context);
899 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
901 if (!rect)
902 surface_add_dirty_rect(surface, NULL);
903 else
905 struct wined3d_box b;
907 b.left = rect->left;
908 b.top = rect->top;
909 b.right = rect->right;
910 b.bottom = rect->bottom;
911 b.front = 0;
912 b.back = 1;
913 surface_add_dirty_rect(surface, &b);
918 static void surface_unmap(struct wined3d_surface *surface)
920 struct wined3d_device *device = surface->resource.device;
921 BOOL fullsurface;
923 TRACE("surface %p.\n", surface);
925 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
927 if (surface->flags & SFLAG_PBO)
929 const struct wined3d_gl_info *gl_info;
930 struct wined3d_context *context;
932 TRACE("Freeing PBO memory.\n");
934 context = context_acquire(device, NULL);
935 gl_info = context->gl_info;
937 ENTER_GL();
938 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
939 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
940 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
941 checkGLcall("glUnmapBufferARB");
942 LEAVE_GL();
943 context_release(context);
945 surface->resource.allocatedMemory = NULL;
948 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
950 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
952 TRACE("Not dirtified, nothing to do.\n");
953 goto done;
956 /* FIXME: The ORM_BACKBUFFER case probably isn't needed, but who knows
957 * what obscure bugs in backbuffer ORM removing it will uncover. Also,
958 * this should only be needed for the frontbuffer, but that requires
959 * present calls to call surface_load_location() on the backbuffer.
960 * Fix both of those after 1.4. */
961 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
962 || (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER
963 && device->fb.render_targets && surface == device->fb.render_targets[0]))
965 if (!surface->dirtyRect.left && !surface->dirtyRect.top
966 && surface->dirtyRect.right == surface->resource.width
967 && surface->dirtyRect.bottom == surface->resource.height)
969 fullsurface = TRUE;
971 else
973 /* TODO: Proper partial rectangle tracking. */
974 fullsurface = FALSE;
975 surface->flags |= SFLAG_INSYSMEM;
978 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
980 /* Partial rectangle tracking is not commonly implemented, it is only
981 * done for render targets. INSYSMEM was set before to tell
982 * surface_load_location() where to read the rectangle from.
983 * Indrawable is set because all modifications from the partial
984 * sysmem copy are written back to the drawable, thus the surface is
985 * merged again in the drawable. The sysmem copy is not fully up to
986 * date because only a subrectangle was read in Map(). */
987 if (!fullsurface)
989 surface_modify_location(surface, surface->draw_binding, TRUE);
990 surface_evict_sysmem(surface);
993 surface->dirtyRect.left = surface->resource.width;
994 surface->dirtyRect.top = surface->resource.height;
995 surface->dirtyRect.right = 0;
996 surface->dirtyRect.bottom = 0;
998 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
1000 FIXME("Depth / stencil buffer locking is not implemented.\n");
1003 done:
1004 /* Overlays have to be redrawn manually after changes with the GL implementation */
1005 if (surface->overlay_dest)
1006 surface_draw_overlay(surface);
1009 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1011 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1012 return FALSE;
1013 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1014 return FALSE;
1015 return TRUE;
1018 static void wined3d_surface_depth_blt_fbo(const struct wined3d_device *device, struct wined3d_surface *src_surface,
1019 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1021 const struct wined3d_gl_info *gl_info;
1022 struct wined3d_context *context;
1023 DWORD src_mask, dst_mask;
1024 GLbitfield gl_mask;
1026 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1027 device, src_surface, wine_dbgstr_rect(src_rect),
1028 dst_surface, wine_dbgstr_rect(dst_rect));
1030 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1031 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1033 if (src_mask != dst_mask)
1035 ERR("Incompatible formats %s and %s.\n",
1036 debug_d3dformat(src_surface->resource.format->id),
1037 debug_d3dformat(dst_surface->resource.format->id));
1038 return;
1041 if (!src_mask)
1043 ERR("Not a depth / stencil format: %s.\n",
1044 debug_d3dformat(src_surface->resource.format->id));
1045 return;
1048 gl_mask = 0;
1049 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1050 gl_mask |= GL_DEPTH_BUFFER_BIT;
1051 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1052 gl_mask |= GL_STENCIL_BUFFER_BIT;
1054 /* Make sure the locations are up-to-date. Loading the destination
1055 * surface isn't required if the entire surface is overwritten. */
1056 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1057 if (!surface_is_full_rect(dst_surface, dst_rect))
1058 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1060 context = context_acquire(device, NULL);
1061 if (!context->valid)
1063 context_release(context);
1064 WARN("Invalid context, skipping blit.\n");
1065 return;
1068 gl_info = context->gl_info;
1070 ENTER_GL();
1072 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1073 glReadBuffer(GL_NONE);
1074 checkGLcall("glReadBuffer()");
1075 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1077 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1078 context_set_draw_buffer(context, GL_NONE);
1079 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1080 context_invalidate_state(context, STATE_FRAMEBUFFER);
1082 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1084 glDepthMask(GL_TRUE);
1085 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
1087 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1089 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1091 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1092 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
1094 glStencilMask(~0U);
1095 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
1098 glDisable(GL_SCISSOR_TEST);
1099 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1101 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1102 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1103 checkGLcall("glBlitFramebuffer()");
1105 LEAVE_GL();
1107 if (wined3d_settings.strict_draw_ordering)
1108 wglFlush(); /* Flush to ensure ordering across contexts. */
1110 context_release(context);
1113 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1114 * Depth / stencil is not supported. */
1115 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
1116 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1117 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1119 const struct wined3d_gl_info *gl_info;
1120 struct wined3d_context *context;
1121 RECT src_rect, dst_rect;
1122 GLenum gl_filter;
1123 GLenum buffer;
1125 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1126 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1127 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1128 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1129 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1131 src_rect = *src_rect_in;
1132 dst_rect = *dst_rect_in;
1134 switch (filter)
1136 case WINED3D_TEXF_LINEAR:
1137 gl_filter = GL_LINEAR;
1138 break;
1140 default:
1141 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1142 case WINED3D_TEXF_NONE:
1143 case WINED3D_TEXF_POINT:
1144 gl_filter = GL_NEAREST;
1145 break;
1148 /* Resolve the source surface first if needed. */
1149 if (src_location == SFLAG_INRB_MULTISAMPLE
1150 && (src_surface->resource.format->id != dst_surface->resource.format->id
1151 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1152 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1153 src_location = SFLAG_INRB_RESOLVED;
1155 /* Make sure the locations are up-to-date. Loading the destination
1156 * surface isn't required if the entire surface is overwritten. (And is
1157 * in fact harmful if we're being called by surface_load_location() with
1158 * the purpose of loading the destination surface.) */
1159 surface_load_location(src_surface, src_location, NULL);
1160 if (!surface_is_full_rect(dst_surface, &dst_rect))
1161 surface_load_location(dst_surface, dst_location, NULL);
1163 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1164 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1165 else context = context_acquire(device, NULL);
1167 if (!context->valid)
1169 context_release(context);
1170 WARN("Invalid context, skipping blit.\n");
1171 return;
1174 gl_info = context->gl_info;
1176 if (src_location == SFLAG_INDRAWABLE)
1178 TRACE("Source surface %p is onscreen.\n", src_surface);
1179 buffer = surface_get_gl_buffer(src_surface);
1180 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1182 else
1184 TRACE("Source surface %p is offscreen.\n", src_surface);
1185 buffer = GL_COLOR_ATTACHMENT0;
1188 ENTER_GL();
1189 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1190 glReadBuffer(buffer);
1191 checkGLcall("glReadBuffer()");
1192 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1193 LEAVE_GL();
1195 if (dst_location == SFLAG_INDRAWABLE)
1197 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1198 buffer = surface_get_gl_buffer(dst_surface);
1199 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1201 else
1203 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1204 buffer = GL_COLOR_ATTACHMENT0;
1207 ENTER_GL();
1208 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1209 context_set_draw_buffer(context, buffer);
1210 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1211 context_invalidate_state(context, STATE_FRAMEBUFFER);
1213 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1214 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
1215 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
1216 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
1217 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
1219 glDisable(GL_SCISSOR_TEST);
1220 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1222 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1223 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1224 checkGLcall("glBlitFramebuffer()");
1226 LEAVE_GL();
1228 if (wined3d_settings.strict_draw_ordering
1229 || (dst_location == SFLAG_INDRAWABLE
1230 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1231 wglFlush();
1233 context_release(context);
1236 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1237 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
1238 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
1240 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1241 return FALSE;
1243 /* Source and/or destination need to be on the GL side */
1244 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
1245 return FALSE;
1247 switch (blit_op)
1249 case WINED3D_BLIT_OP_COLOR_BLIT:
1250 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1251 return FALSE;
1252 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1253 return FALSE;
1254 break;
1256 case WINED3D_BLIT_OP_DEPTH_BLIT:
1257 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1258 return FALSE;
1259 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1260 return FALSE;
1261 break;
1263 default:
1264 return FALSE;
1267 if (!(src_format->id == dst_format->id
1268 || (is_identity_fixup(src_format->color_fixup)
1269 && is_identity_fixup(dst_format->color_fixup))))
1270 return FALSE;
1272 return TRUE;
1275 /* This function checks if the primary render target uses the 8bit paletted format. */
1276 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1278 if (device->fb.render_targets && device->fb.render_targets[0])
1280 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1281 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1282 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1283 return TRUE;
1285 return FALSE;
1288 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1289 DWORD color, struct wined3d_color *float_color)
1291 const struct wined3d_format *format = surface->resource.format;
1292 const struct wined3d_device *device = surface->resource.device;
1294 switch (format->id)
1296 case WINED3DFMT_P8_UINT:
1297 if (surface->palette)
1299 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1300 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1301 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1303 else
1305 float_color->r = 0.0f;
1306 float_color->g = 0.0f;
1307 float_color->b = 0.0f;
1309 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1310 break;
1312 case WINED3DFMT_B5G6R5_UNORM:
1313 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1314 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1315 float_color->b = (color & 0x1f) / 31.0f;
1316 float_color->a = 1.0f;
1317 break;
1319 case WINED3DFMT_B8G8R8_UNORM:
1320 case WINED3DFMT_B8G8R8X8_UNORM:
1321 float_color->r = D3DCOLOR_R(color);
1322 float_color->g = D3DCOLOR_G(color);
1323 float_color->b = D3DCOLOR_B(color);
1324 float_color->a = 1.0f;
1325 break;
1327 case WINED3DFMT_B8G8R8A8_UNORM:
1328 float_color->r = D3DCOLOR_R(color);
1329 float_color->g = D3DCOLOR_G(color);
1330 float_color->b = D3DCOLOR_B(color);
1331 float_color->a = D3DCOLOR_A(color);
1332 break;
1334 default:
1335 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1336 return FALSE;
1339 return TRUE;
1342 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1344 const struct wined3d_format *format = surface->resource.format;
1346 switch (format->id)
1348 case WINED3DFMT_S1_UINT_D15_UNORM:
1349 *float_depth = depth / (float)0x00007fff;
1350 break;
1352 case WINED3DFMT_D16_UNORM:
1353 *float_depth = depth / (float)0x0000ffff;
1354 break;
1356 case WINED3DFMT_D24_UNORM_S8_UINT:
1357 case WINED3DFMT_X8D24_UNORM:
1358 *float_depth = depth / (float)0x00ffffff;
1359 break;
1361 case WINED3DFMT_D32_UNORM:
1362 *float_depth = depth / (float)0xffffffff;
1363 break;
1365 default:
1366 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1367 return FALSE;
1370 return TRUE;
1373 /* Do not call while under the GL lock. */
1374 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1376 const struct wined3d_resource *resource = &surface->resource;
1377 struct wined3d_device *device = resource->device;
1378 const struct blit_shader *blitter;
1380 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1381 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1382 if (!blitter)
1384 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1385 return WINED3DERR_INVALIDCALL;
1388 return blitter->depth_fill(device, surface, rect, depth);
1391 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1392 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1394 struct wined3d_device *device = src_surface->resource.device;
1396 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1397 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1398 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1399 return WINED3DERR_INVALIDCALL;
1401 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1403 surface_modify_ds_location(dst_surface, SFLAG_INTEXTURE,
1404 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1406 return WINED3D_OK;
1409 /* Do not call while under the GL lock. */
1410 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1411 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1412 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
1414 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1415 struct wined3d_device *device = dst_surface->resource.device;
1416 DWORD src_ds_flags, dst_ds_flags;
1417 RECT src_rect, dst_rect;
1418 BOOL scale, convert;
1420 static const DWORD simple_blit = WINEDDBLT_ASYNC
1421 | WINEDDBLT_COLORFILL
1422 | WINEDDBLT_WAIT
1423 | WINEDDBLT_DEPTHFILL
1424 | WINEDDBLT_DONOTWAIT;
1426 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1427 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1428 flags, fx, debug_d3dtexturefiltertype(filter));
1429 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1431 if (fx)
1433 TRACE("dwSize %#x.\n", fx->dwSize);
1434 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
1435 TRACE("dwROP %#x.\n", fx->dwROP);
1436 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
1437 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
1438 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
1439 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
1440 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
1441 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
1442 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
1443 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
1444 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
1445 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
1446 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
1447 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
1448 TRACE("dwReserved %#x.\n", fx->dwReserved);
1449 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
1450 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
1451 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
1452 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
1453 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
1454 TRACE("ddckDestColorkey {%#x, %#x}.\n",
1455 fx->ddckDestColorkey.color_space_low_value,
1456 fx->ddckDestColorkey.color_space_high_value);
1457 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
1458 fx->ddckSrcColorkey.color_space_low_value,
1459 fx->ddckSrcColorkey.color_space_high_value);
1462 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1464 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1465 return WINEDDERR_SURFACEBUSY;
1468 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1470 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1471 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1472 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1473 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1474 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1476 WARN("The application gave us a bad destination rectangle.\n");
1477 return WINEDDERR_INVALIDRECT;
1480 if (src_surface)
1482 surface_get_rect(src_surface, src_rect_in, &src_rect);
1484 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1485 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1486 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1487 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1488 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1490 WARN("Application gave us bad source rectangle for Blt.\n");
1491 return WINEDDERR_INVALIDRECT;
1494 else
1496 memset(&src_rect, 0, sizeof(src_rect));
1499 if (!fx || !(fx->dwDDFX))
1500 flags &= ~WINEDDBLT_DDFX;
1502 if (flags & WINEDDBLT_WAIT)
1503 flags &= ~WINEDDBLT_WAIT;
1505 if (flags & WINEDDBLT_ASYNC)
1507 static unsigned int once;
1509 if (!once++)
1510 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1511 flags &= ~WINEDDBLT_ASYNC;
1514 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1515 if (flags & WINEDDBLT_DONOTWAIT)
1517 static unsigned int once;
1519 if (!once++)
1520 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1521 flags &= ~WINEDDBLT_DONOTWAIT;
1524 if (!device->d3d_initialized)
1526 WARN("D3D not initialized, using fallback.\n");
1527 goto cpu;
1530 /* We want to avoid invalidating the sysmem location for converted
1531 * surfaces, since otherwise we'd have to convert the data back when
1532 * locking them. */
1533 if (dst_surface->flags & SFLAG_CONVERTED)
1535 WARN("Converted surface, using CPU blit.\n");
1536 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1539 if (flags & ~simple_blit)
1541 WARN("Using fallback for complex blit (%#x).\n", flags);
1542 goto fallback;
1545 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1546 src_swapchain = src_surface->container.u.swapchain;
1547 else
1548 src_swapchain = NULL;
1550 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1551 dst_swapchain = dst_surface->container.u.swapchain;
1552 else
1553 dst_swapchain = NULL;
1555 /* This isn't strictly needed. FBO blits for example could deal with
1556 * cross-swapchain blits by first downloading the source to a texture
1557 * before switching to the destination context. We just have this here to
1558 * not have to deal with the issue, since cross-swapchain blits should be
1559 * rare. */
1560 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1562 FIXME("Using fallback for cross-swapchain blit.\n");
1563 goto fallback;
1566 scale = src_surface
1567 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
1568 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
1569 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
1571 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1572 if (src_surface)
1573 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1574 else
1575 src_ds_flags = 0;
1577 if (src_ds_flags || dst_ds_flags)
1579 if (flags & WINEDDBLT_DEPTHFILL)
1581 float depth;
1583 TRACE("Depth fill.\n");
1585 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1586 return WINED3DERR_INVALIDCALL;
1588 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1589 return WINED3D_OK;
1591 else
1593 if (src_ds_flags != dst_ds_flags)
1595 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1596 return WINED3DERR_INVALIDCALL;
1599 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1600 return WINED3D_OK;
1603 else
1605 /* In principle this would apply to depth blits as well, but we don't
1606 * implement those in the CPU blitter at the moment. */
1607 if ((dst_surface->flags & SFLAG_INSYSMEM)
1608 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
1610 if (scale)
1611 TRACE("Not doing sysmem blit because of scaling.\n");
1612 else if (convert)
1613 TRACE("Not doing sysmem blit because of format conversion.\n");
1614 else
1615 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1618 if (flags & WINEDDBLT_COLORFILL)
1620 struct wined3d_color color;
1622 TRACE("Color fill.\n");
1624 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1625 goto fallback;
1627 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1628 return WINED3D_OK;
1630 else
1632 TRACE("Color blit.\n");
1634 /* Upload */
1635 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
1637 if (scale)
1638 TRACE("Not doing upload because of scaling.\n");
1639 else if (convert)
1640 TRACE("Not doing upload because of format conversion.\n");
1641 else
1643 POINT dst_point = {dst_rect.left, dst_rect.top};
1645 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
1647 if (!surface_is_offscreen(dst_surface))
1648 surface_load_location(dst_surface, dst_surface->draw_binding, NULL);
1649 return WINED3D_OK;
1654 /* Use present for back -> front blits. The idea behind this is
1655 * that present is potentially faster than a blit, in particular
1656 * when FBO blits aren't available. Some ddraw applications like
1657 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
1658 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
1659 * applications can't blit directly to the frontbuffer. */
1660 if (dst_swapchain && dst_swapchain->back_buffers
1661 && dst_surface == dst_swapchain->front_buffer
1662 && src_surface == dst_swapchain->back_buffers[0])
1664 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
1666 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1668 /* Set the swap effect to COPY, we don't want the backbuffer
1669 * to become undefined. */
1670 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
1671 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
1672 dst_swapchain->desc.swap_effect = swap_effect;
1674 return WINED3D_OK;
1677 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1678 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1679 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1681 TRACE("Using FBO blit.\n");
1683 surface_blt_fbo(device, filter,
1684 src_surface, src_surface->draw_binding, &src_rect,
1685 dst_surface, dst_surface->draw_binding, &dst_rect);
1686 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1687 return WINED3D_OK;
1690 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1691 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1692 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1694 TRACE("Using arbfp blit.\n");
1696 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1697 return WINED3D_OK;
1702 fallback:
1704 /* Special cases for render targets. */
1705 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1706 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1708 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1709 src_surface, &src_rect, flags, fx, filter)))
1710 return WINED3D_OK;
1713 cpu:
1715 /* For the rest call the X11 surface implementation. For render targets
1716 * this should be implemented OpenGL accelerated in BltOverride, other
1717 * blits are rather rare. */
1718 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1721 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1722 struct wined3d_surface *render_target)
1724 TRACE("surface %p, render_target %p.\n", surface, render_target);
1726 /* TODO: Check surface sizes, pools, etc. */
1728 if (render_target->resource.multisample_type)
1729 return WINED3DERR_INVALIDCALL;
1731 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1734 /* Context activation is done by the caller. */
1735 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1737 if (surface->flags & SFLAG_DIBSECTION)
1739 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1741 else
1743 if (!surface->resource.heapMemory)
1744 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1745 else if (!(surface->flags & SFLAG_CLIENT))
1746 ERR("Surface %p has heapMemory %p and flags %#x.\n",
1747 surface, surface->resource.heapMemory, surface->flags);
1749 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1750 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1753 ENTER_GL();
1754 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1755 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1756 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1757 surface->resource.size, surface->resource.allocatedMemory));
1758 checkGLcall("glGetBufferSubDataARB");
1759 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1760 checkGLcall("glDeleteBuffersARB");
1761 LEAVE_GL();
1763 surface->pbo = 0;
1764 surface->flags &= ~SFLAG_PBO;
1767 /* Do not call while under the GL lock. */
1768 static void surface_unload(struct wined3d_resource *resource)
1770 struct wined3d_surface *surface = surface_from_resource(resource);
1771 struct wined3d_renderbuffer_entry *entry, *entry2;
1772 struct wined3d_device *device = resource->device;
1773 const struct wined3d_gl_info *gl_info;
1774 struct wined3d_context *context;
1776 TRACE("surface %p.\n", surface);
1778 if (resource->pool == WINED3D_POOL_DEFAULT)
1780 /* Default pool resources are supposed to be destroyed before Reset is called.
1781 * Implicit resources stay however. So this means we have an implicit render target
1782 * or depth stencil. The content may be destroyed, but we still have to tear down
1783 * opengl resources, so we cannot leave early.
1785 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1786 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1787 * or the depth stencil into an FBO the texture or render buffer will be removed
1788 * and all flags get lost
1790 if (!(surface->flags & SFLAG_PBO))
1791 surface_init_sysmem(surface);
1792 /* We also get here when the ddraw swapchain is destroyed, for example
1793 * for a mode switch. In this case this surface won't necessarily be
1794 * an implicit surface. We have to mark it lost so that the
1795 * application can restore it after the mode switch. */
1796 surface->flags |= SFLAG_LOST;
1798 else
1800 /* Load the surface into system memory */
1801 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1802 surface_modify_location(surface, surface->draw_binding, FALSE);
1804 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1805 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1806 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1808 context = context_acquire(device, NULL);
1809 gl_info = context->gl_info;
1811 /* Destroy PBOs, but load them into real sysmem before */
1812 if (surface->flags & SFLAG_PBO)
1813 surface_remove_pbo(surface, gl_info);
1815 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1816 * all application-created targets the application has to release the surface
1817 * before calling _Reset
1819 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1821 ENTER_GL();
1822 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1823 LEAVE_GL();
1824 list_remove(&entry->entry);
1825 HeapFree(GetProcessHeap(), 0, entry);
1827 list_init(&surface->renderbuffers);
1828 surface->current_renderbuffer = NULL;
1830 ENTER_GL();
1832 /* If we're in a texture, the texture name belongs to the texture.
1833 * Otherwise, destroy it. */
1834 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1836 glDeleteTextures(1, &surface->texture_name);
1837 surface->texture_name = 0;
1838 glDeleteTextures(1, &surface->texture_name_srgb);
1839 surface->texture_name_srgb = 0;
1841 if (surface->rb_multisample)
1843 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1844 surface->rb_multisample = 0;
1846 if (surface->rb_resolved)
1848 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1849 surface->rb_resolved = 0;
1852 LEAVE_GL();
1854 context_release(context);
1856 resource_unload(resource);
1859 static const struct wined3d_resource_ops surface_resource_ops =
1861 surface_unload,
1864 static const struct wined3d_surface_ops surface_ops =
1866 surface_private_setup,
1867 surface_realize_palette,
1868 surface_map,
1869 surface_unmap,
1872 /*****************************************************************************
1873 * Initializes the GDI surface, aka creates the DIB section we render to
1874 * The DIB section creation is done by calling GetDC, which will create the
1875 * section and releasing the dc to allow the app to use it. The dib section
1876 * will stay until the surface is released
1878 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1879 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1880 * avoid confusion in the shared surface code.
1882 * Returns:
1883 * WINED3D_OK on success
1884 * The return values of called methods on failure
1886 *****************************************************************************/
1887 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1889 HRESULT hr;
1891 TRACE("surface %p.\n", surface);
1893 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1895 ERR("Overlays not yet supported by GDI surfaces.\n");
1896 return WINED3DERR_INVALIDCALL;
1899 /* Sysmem textures have memory already allocated - release it,
1900 * this avoids an unnecessary memcpy. */
1901 hr = surface_create_dib_section(surface);
1902 if (SUCCEEDED(hr))
1904 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1905 surface->resource.heapMemory = NULL;
1906 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1909 /* We don't mind the nonpow2 stuff in GDI. */
1910 surface->pow2Width = surface->resource.width;
1911 surface->pow2Height = surface->resource.height;
1913 return WINED3D_OK;
1916 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1918 struct wined3d_palette *palette = surface->palette;
1920 TRACE("surface %p.\n", surface);
1922 if (!palette) return;
1924 if (surface->flags & SFLAG_DIBSECTION)
1926 RGBQUAD col[256];
1927 unsigned int i;
1929 TRACE("Updating the DC's palette.\n");
1931 for (i = 0; i < 256; ++i)
1933 col[i].rgbRed = palette->palents[i].peRed;
1934 col[i].rgbGreen = palette->palents[i].peGreen;
1935 col[i].rgbBlue = palette->palents[i].peBlue;
1936 col[i].rgbReserved = 0;
1938 SetDIBColorTable(surface->hDC, 0, 256, col);
1941 /* Update the image because of the palette change. Some games like e.g.
1942 * Red Alert call SetEntries a lot to implement fading. */
1943 /* Tell the swapchain to update the screen. */
1944 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1946 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1947 if (surface == swapchain->front_buffer)
1949 x11_copy_to_screen(swapchain, NULL);
1954 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
1956 TRACE("surface %p, rect %s, flags %#x.\n",
1957 surface, wine_dbgstr_rect(rect), flags);
1959 if (!(surface->flags & SFLAG_DIBSECTION))
1961 HRESULT hr;
1963 /* This happens on gdi surfaces if the application set a user pointer
1964 * and resets it. Recreate the DIB section. */
1965 if (FAILED(hr = surface_create_dib_section(surface)))
1967 ERR("Failed to create dib section, hr %#x.\n", hr);
1968 return;
1970 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1971 surface->resource.heapMemory = NULL;
1972 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1976 static void gdi_surface_unmap(struct wined3d_surface *surface)
1978 TRACE("surface %p.\n", surface);
1980 /* Tell the swapchain to update the screen. */
1981 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1983 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1984 if (surface == swapchain->front_buffer)
1986 x11_copy_to_screen(swapchain, &surface->lockedRect);
1990 memset(&surface->lockedRect, 0, sizeof(RECT));
1993 static const struct wined3d_surface_ops gdi_surface_ops =
1995 gdi_surface_private_setup,
1996 gdi_surface_realize_palette,
1997 gdi_surface_map,
1998 gdi_surface_unmap,
2001 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2003 GLuint *name;
2004 DWORD flag;
2006 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2008 if(srgb)
2010 name = &surface->texture_name_srgb;
2011 flag = SFLAG_INSRGBTEX;
2013 else
2015 name = &surface->texture_name;
2016 flag = SFLAG_INTEXTURE;
2019 if (!*name && new_name)
2021 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2022 * surface has no texture name yet. See if we can get rid of this. */
2023 if (surface->flags & flag)
2025 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2026 surface_modify_location(surface, flag, FALSE);
2030 *name = new_name;
2031 surface_force_reload(surface);
2034 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2036 TRACE("surface %p, target %#x.\n", surface, target);
2038 if (surface->texture_target != target)
2040 if (target == GL_TEXTURE_RECTANGLE_ARB)
2042 surface->flags &= ~SFLAG_NORMCOORD;
2044 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2046 surface->flags |= SFLAG_NORMCOORD;
2049 surface->texture_target = target;
2050 surface_force_reload(surface);
2053 /* Context activation is done by the caller. */
2054 void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
2056 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
2058 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2060 struct wined3d_texture *texture = surface->container.u.texture;
2062 TRACE("Passing to container (%p).\n", texture);
2063 texture->texture_ops->texture_bind(texture, context, srgb);
2065 else
2067 if (surface->texture_level)
2069 ERR("Standalone surface %p is non-zero texture level %u.\n",
2070 surface, surface->texture_level);
2073 if (srgb)
2074 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2076 ENTER_GL();
2078 if (!surface->texture_name)
2080 glGenTextures(1, &surface->texture_name);
2081 checkGLcall("glGenTextures");
2083 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2085 context_bind_texture(context, surface->texture_target, surface->texture_name);
2086 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2087 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2088 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2089 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2090 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2091 checkGLcall("glTexParameteri");
2093 else
2095 context_bind_texture(context, surface->texture_target, surface->texture_name);
2098 LEAVE_GL();
2102 /* This call just downloads data, the caller is responsible for binding the
2103 * correct texture. */
2104 /* Context activation is done by the caller. */
2105 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2107 const struct wined3d_format *format = surface->resource.format;
2109 /* Only support read back of converted P8 surfaces. */
2110 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2112 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
2113 return;
2116 ENTER_GL();
2118 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2120 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2121 surface, surface->texture_level, format->glFormat, format->glType,
2122 surface->resource.allocatedMemory);
2124 if (surface->flags & SFLAG_PBO)
2126 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2127 checkGLcall("glBindBufferARB");
2128 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2129 checkGLcall("glGetCompressedTexImageARB");
2130 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2131 checkGLcall("glBindBufferARB");
2133 else
2135 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2136 surface->texture_level, surface->resource.allocatedMemory));
2137 checkGLcall("glGetCompressedTexImageARB");
2140 LEAVE_GL();
2142 else
2144 void *mem;
2145 GLenum gl_format = format->glFormat;
2146 GLenum gl_type = format->glType;
2147 int src_pitch = 0;
2148 int dst_pitch = 0;
2150 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2151 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2153 gl_format = GL_ALPHA;
2154 gl_type = GL_UNSIGNED_BYTE;
2157 if (surface->flags & SFLAG_NONPOW2)
2159 unsigned char alignment = surface->resource.device->surface_alignment;
2160 src_pitch = format->byte_count * surface->pow2Width;
2161 dst_pitch = wined3d_surface_get_pitch(surface);
2162 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2163 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2165 else
2167 mem = surface->resource.allocatedMemory;
2170 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2171 surface, surface->texture_level, gl_format, gl_type, mem);
2173 if (surface->flags & SFLAG_PBO)
2175 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2176 checkGLcall("glBindBufferARB");
2178 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2179 checkGLcall("glGetTexImage");
2181 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2182 checkGLcall("glBindBufferARB");
2184 else
2186 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2187 checkGLcall("glGetTexImage");
2189 LEAVE_GL();
2191 if (surface->flags & SFLAG_NONPOW2)
2193 const BYTE *src_data;
2194 BYTE *dst_data;
2195 UINT y;
2197 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2198 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2199 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2201 * We're doing this...
2203 * instead of boxing the texture :
2204 * |<-texture width ->| -->pow2width| /\
2205 * |111111111111111111| | |
2206 * |222 Texture 222222| boxed empty | texture height
2207 * |3333 Data 33333333| | |
2208 * |444444444444444444| | \/
2209 * ----------------------------------- |
2210 * | boxed empty | boxed empty | pow2height
2211 * | | | \/
2212 * -----------------------------------
2215 * we're repacking the data to the expected texture width
2217 * |<-texture width ->| -->pow2width| /\
2218 * |111111111111111111222222222222222| |
2219 * |222333333333333333333444444444444| texture height
2220 * |444444 | |
2221 * | | \/
2222 * | | |
2223 * | empty | pow2height
2224 * | | \/
2225 * -----------------------------------
2227 * == is the same as
2229 * |<-texture width ->| /\
2230 * |111111111111111111|
2231 * |222222222222222222|texture height
2232 * |333333333333333333|
2233 * |444444444444444444| \/
2234 * --------------------
2236 * this also means that any references to allocatedMemory should work with the data as if were a
2237 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2239 * internally the texture is still stored in a boxed format so any references to textureName will
2240 * get a boxed texture with width pow2width and not a texture of width resource.width.
2242 * Performance should not be an issue, because applications normally do not lock the surfaces when
2243 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2244 * and doesn't have to be re-read. */
2245 src_data = mem;
2246 dst_data = surface->resource.allocatedMemory;
2247 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2248 for (y = 1; y < surface->resource.height; ++y)
2250 /* skip the first row */
2251 src_data += src_pitch;
2252 dst_data += dst_pitch;
2253 memcpy(dst_data, src_data, dst_pitch);
2256 HeapFree(GetProcessHeap(), 0, mem);
2260 /* Surface has now been downloaded */
2261 surface->flags |= SFLAG_INSYSMEM;
2264 /* This call just uploads data, the caller is responsible for binding the
2265 * correct texture. */
2266 /* Context activation is done by the caller. */
2267 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2268 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
2269 BOOL srgb, const struct wined3d_bo_address *data)
2271 UINT update_w = src_rect->right - src_rect->left;
2272 UINT update_h = src_rect->bottom - src_rect->top;
2274 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
2275 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
2276 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2278 if (surface->flags & SFLAG_LOCKED)
2280 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
2281 surface->flags |= SFLAG_PIN_SYSMEM;
2284 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2285 update_h *= format->heightscale;
2287 ENTER_GL();
2289 if (data->buffer_object)
2291 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2292 checkGLcall("glBindBufferARB");
2295 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2297 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2298 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2299 const BYTE *addr = data->addr;
2300 GLenum internal;
2302 addr += (src_rect->top / format->block_height) * src_pitch;
2303 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2305 if (srgb)
2306 internal = format->glGammaInternal;
2307 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2308 internal = format->rtInternal;
2309 else
2310 internal = format->glInternal;
2312 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2313 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2314 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2316 if (row_length == src_pitch)
2318 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2319 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2321 else
2323 UINT row, y;
2325 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2326 * can't use the unpack row length like below. */
2327 for (row = 0, y = dst_point->y; row < row_count; ++row)
2329 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2330 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2331 y += format->block_height;
2332 addr += src_pitch;
2335 checkGLcall("glCompressedTexSubImage2DARB");
2337 else
2339 const BYTE *addr = data->addr;
2341 addr += src_rect->top * src_pitch;
2342 addr += src_rect->left * format->byte_count;
2344 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2345 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2346 update_w, update_h, format->glFormat, format->glType, addr);
2348 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
2349 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2350 update_w, update_h, format->glFormat, format->glType, addr);
2351 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2352 checkGLcall("glTexSubImage2D");
2355 if (data->buffer_object)
2357 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2358 checkGLcall("glBindBufferARB");
2361 LEAVE_GL();
2363 if (wined3d_settings.strict_draw_ordering)
2364 wglFlush();
2366 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2368 struct wined3d_device *device = surface->resource.device;
2369 unsigned int i;
2371 for (i = 0; i < device->context_count; ++i)
2373 context_surface_update(device->contexts[i], surface);
2378 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
2379 struct wined3d_surface *src_surface, const RECT *src_rect)
2381 const struct wined3d_format *src_format;
2382 const struct wined3d_format *dst_format;
2383 const struct wined3d_gl_info *gl_info;
2384 enum wined3d_conversion_type convert;
2385 struct wined3d_context *context;
2386 struct wined3d_bo_address data;
2387 struct wined3d_format format;
2388 UINT update_w, update_h;
2389 UINT dst_w, dst_h;
2390 UINT src_w, src_h;
2391 UINT src_pitch;
2392 POINT p;
2393 RECT r;
2395 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
2396 dst_surface, wine_dbgstr_point(dst_point),
2397 src_surface, wine_dbgstr_rect(src_rect));
2399 src_format = src_surface->resource.format;
2400 dst_format = dst_surface->resource.format;
2402 if (src_format->id != dst_format->id)
2404 WARN("Source and destination surfaces should have the same format.\n");
2405 return WINED3DERR_INVALIDCALL;
2408 if (!dst_point)
2410 p.x = 0;
2411 p.y = 0;
2412 dst_point = &p;
2414 else if (dst_point->x < 0 || dst_point->y < 0)
2416 WARN("Invalid destination point.\n");
2417 return WINED3DERR_INVALIDCALL;
2420 if (!src_rect)
2422 r.left = 0;
2423 r.top = 0;
2424 r.right = src_surface->resource.width;
2425 r.bottom = src_surface->resource.height;
2426 src_rect = &r;
2428 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
2429 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
2431 WARN("Invalid source rectangle.\n");
2432 return WINED3DERR_INVALIDCALL;
2435 src_w = src_surface->resource.width;
2436 src_h = src_surface->resource.height;
2438 dst_w = dst_surface->resource.width;
2439 dst_h = dst_surface->resource.height;
2441 update_w = src_rect->right - src_rect->left;
2442 update_h = src_rect->bottom - src_rect->top;
2444 if (update_w > dst_w || dst_point->x > dst_w - update_w
2445 || update_h > dst_h || dst_point->y > dst_h - update_h)
2447 WARN("Destination out of bounds.\n");
2448 return WINED3DERR_INVALIDCALL;
2451 /* NPOT block sizes would be silly. */
2452 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS)
2453 && ((update_w & (src_format->block_width - 1) || update_h & (src_format->block_height - 1))
2454 && (src_w != update_w || dst_w != update_w || src_h != update_h || dst_h != update_h)))
2456 WARN("Update rect not block-aligned.\n");
2457 return WINED3DERR_INVALIDCALL;
2460 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
2461 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
2462 if (convert != WINED3D_CT_NONE || format.convert)
2464 RECT dst_rect = {dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h};
2465 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
2468 context = context_acquire(dst_surface->resource.device, NULL);
2469 gl_info = context->gl_info;
2471 /* Only load the surface for partial updates. For newly allocated texture
2472 * the texture wouldn't be the current location, and we'd upload zeroes
2473 * just to overwrite them again. */
2474 if (update_w == dst_w && update_h == dst_h)
2475 surface_prepare_texture(dst_surface, context, FALSE);
2476 else
2477 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
2478 surface_bind(dst_surface, context, FALSE);
2480 data.buffer_object = src_surface->pbo;
2481 data.addr = src_surface->resource.allocatedMemory;
2482 src_pitch = wined3d_surface_get_pitch(src_surface);
2484 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
2486 invalidate_active_texture(dst_surface->resource.device, context);
2488 context_release(context);
2490 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
2491 return WINED3D_OK;
2494 /* This call just allocates the texture, the caller is responsible for binding
2495 * the correct texture. */
2496 /* Context activation is done by the caller. */
2497 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2498 const struct wined3d_format *format, BOOL srgb)
2500 BOOL enable_client_storage = FALSE;
2501 GLsizei width = surface->pow2Width;
2502 GLsizei height = surface->pow2Height;
2503 const BYTE *mem = NULL;
2504 GLenum internal;
2506 if (srgb)
2508 internal = format->glGammaInternal;
2510 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2512 internal = format->rtInternal;
2514 else
2516 internal = format->glInternal;
2519 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2521 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",
2522 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2523 internal, width, height, format->glFormat, format->glType);
2525 ENTER_GL();
2527 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2529 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2530 || !surface->resource.allocatedMemory)
2532 /* In some cases we want to disable client storage.
2533 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2534 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2535 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2536 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2538 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2539 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2540 surface->flags &= ~SFLAG_CLIENT;
2541 enable_client_storage = TRUE;
2543 else
2545 surface->flags |= SFLAG_CLIENT;
2547 /* Point OpenGL to our allocated texture memory. Do not use
2548 * resource.allocatedMemory here because it might point into a
2549 * PBO. Instead use heapMemory, but get the alignment right. */
2550 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2551 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2555 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2557 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2558 internal, width, height, 0, surface->resource.size, mem));
2559 checkGLcall("glCompressedTexImage2DARB");
2561 else
2563 glTexImage2D(surface->texture_target, surface->texture_level,
2564 internal, width, height, 0, format->glFormat, format->glType, mem);
2565 checkGLcall("glTexImage2D");
2568 if(enable_client_storage) {
2569 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2570 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2572 LEAVE_GL();
2575 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2576 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2577 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2578 /* GL locking is done by the caller */
2579 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2581 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2582 struct wined3d_renderbuffer_entry *entry;
2583 GLuint renderbuffer = 0;
2584 unsigned int src_width, src_height;
2585 unsigned int width, height;
2587 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2589 width = rt->pow2Width;
2590 height = rt->pow2Height;
2592 else
2594 width = surface->pow2Width;
2595 height = surface->pow2Height;
2598 src_width = surface->pow2Width;
2599 src_height = surface->pow2Height;
2601 /* A depth stencil smaller than the render target is not valid */
2602 if (width > src_width || height > src_height) return;
2604 /* Remove any renderbuffer set if the sizes match */
2605 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2606 || (width == src_width && height == src_height))
2608 surface->current_renderbuffer = NULL;
2609 return;
2612 /* Look if we've already got a renderbuffer of the correct dimensions */
2613 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2615 if (entry->width == width && entry->height == height)
2617 renderbuffer = entry->id;
2618 surface->current_renderbuffer = entry;
2619 break;
2623 if (!renderbuffer)
2625 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2626 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2627 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2628 surface->resource.format->glInternal, width, height);
2630 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2631 entry->width = width;
2632 entry->height = height;
2633 entry->id = renderbuffer;
2634 list_add_head(&surface->renderbuffers, &entry->entry);
2636 surface->current_renderbuffer = entry;
2639 checkGLcall("set_compatible_renderbuffer");
2642 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2644 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2646 TRACE("surface %p.\n", surface);
2648 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2650 ERR("Surface %p is not on a swapchain.\n", surface);
2651 return GL_NONE;
2654 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2656 if (swapchain->render_to_fbo)
2658 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2659 return GL_COLOR_ATTACHMENT0;
2661 TRACE("Returning GL_BACK\n");
2662 return GL_BACK;
2664 else if (surface == swapchain->front_buffer)
2666 TRACE("Returning GL_FRONT\n");
2667 return GL_FRONT;
2670 FIXME("Higher back buffer, returning GL_BACK\n");
2671 return GL_BACK;
2674 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2675 void surface_add_dirty_rect(struct wined3d_surface *surface, const struct wined3d_box *dirty_rect)
2677 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2679 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2680 /* No partial locking for textures yet. */
2681 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2683 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2684 if (dirty_rect)
2686 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->left);
2687 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->top);
2688 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->right);
2689 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->bottom);
2691 else
2693 surface->dirtyRect.left = 0;
2694 surface->dirtyRect.top = 0;
2695 surface->dirtyRect.right = surface->resource.width;
2696 surface->dirtyRect.bottom = surface->resource.height;
2699 /* if the container is a texture then mark it dirty. */
2700 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2702 TRACE("Passing to container.\n");
2703 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2707 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2709 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2710 BOOL ck_changed;
2712 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2714 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
2716 ERR("Not supported on scratch surfaces.\n");
2717 return WINED3DERR_INVALIDCALL;
2720 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2722 /* Reload if either the texture and sysmem have different ideas about the
2723 * color key, or the actual key values changed. */
2724 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2725 && (surface->gl_color_key.color_space_low_value != surface->src_blt_color_key.color_space_low_value
2726 || surface->gl_color_key.color_space_high_value != surface->src_blt_color_key.color_space_high_value)))
2728 TRACE("Reloading because of color keying\n");
2729 /* To perform the color key conversion we need a sysmem copy of
2730 * the surface. Make sure we have it. */
2732 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2733 /* Make sure the texture is reloaded because of the color key change,
2734 * this kills performance though :( */
2735 /* TODO: This is not necessarily needed with hw palettized texture support. */
2736 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2737 /* Switching color keying on / off may change the internal format. */
2738 if (ck_changed)
2739 surface_force_reload(surface);
2741 else if (!(surface->flags & flag))
2743 TRACE("Reloading because surface is dirty.\n");
2745 else
2747 TRACE("surface is already in texture\n");
2748 return WINED3D_OK;
2751 /* No partial locking for textures yet. */
2752 surface_load_location(surface, flag, NULL);
2753 surface_evict_sysmem(surface);
2755 return WINED3D_OK;
2758 /* See also float_16_to_32() in wined3d_private.h */
2759 static inline unsigned short float_32_to_16(const float *in)
2761 int exp = 0;
2762 float tmp = fabsf(*in);
2763 unsigned int mantissa;
2764 unsigned short ret;
2766 /* Deal with special numbers */
2767 if (*in == 0.0f)
2768 return 0x0000;
2769 if (isnan(*in))
2770 return 0x7c01;
2771 if (isinf(*in))
2772 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2774 if (tmp < powf(2, 10))
2778 tmp = tmp * 2.0f;
2779 exp--;
2780 } while (tmp < powf(2, 10));
2782 else if (tmp >= powf(2, 11))
2786 tmp /= 2.0f;
2787 exp++;
2788 } while (tmp >= powf(2, 11));
2791 mantissa = (unsigned int)tmp;
2792 if (tmp - mantissa >= 0.5f)
2793 ++mantissa; /* Round to nearest, away from zero. */
2795 exp += 10; /* Normalize the mantissa. */
2796 exp += 15; /* Exponent is encoded with excess 15. */
2798 if (exp > 30) /* too big */
2800 ret = 0x7c00; /* INF */
2802 else if (exp <= 0)
2804 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2805 while (exp <= 0)
2807 mantissa = mantissa >> 1;
2808 ++exp;
2810 ret = mantissa & 0x3ff;
2812 else
2814 ret = (exp << 10) | (mantissa & 0x3ff);
2817 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2818 return ret;
2821 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2823 ULONG refcount;
2825 TRACE("Surface %p, container %p of type %#x.\n",
2826 surface, surface->container.u.base, surface->container.type);
2828 switch (surface->container.type)
2830 case WINED3D_CONTAINER_TEXTURE:
2831 return wined3d_texture_incref(surface->container.u.texture);
2833 case WINED3D_CONTAINER_SWAPCHAIN:
2834 return wined3d_swapchain_incref(surface->container.u.swapchain);
2836 default:
2837 ERR("Unhandled container type %#x.\n", surface->container.type);
2838 case WINED3D_CONTAINER_NONE:
2839 break;
2842 refcount = InterlockedIncrement(&surface->resource.ref);
2843 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2845 return refcount;
2848 /* Do not call while under the GL lock. */
2849 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2851 ULONG refcount;
2853 TRACE("Surface %p, container %p of type %#x.\n",
2854 surface, surface->container.u.base, surface->container.type);
2856 switch (surface->container.type)
2858 case WINED3D_CONTAINER_TEXTURE:
2859 return wined3d_texture_decref(surface->container.u.texture);
2861 case WINED3D_CONTAINER_SWAPCHAIN:
2862 return wined3d_swapchain_decref(surface->container.u.swapchain);
2864 default:
2865 ERR("Unhandled container type %#x.\n", surface->container.type);
2866 case WINED3D_CONTAINER_NONE:
2867 break;
2870 refcount = InterlockedDecrement(&surface->resource.ref);
2871 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2873 if (!refcount)
2875 surface_cleanup(surface);
2876 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2878 TRACE("Destroyed surface %p.\n", surface);
2879 HeapFree(GetProcessHeap(), 0, surface);
2882 return refcount;
2885 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2887 return resource_set_priority(&surface->resource, priority);
2890 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2892 return resource_get_priority(&surface->resource);
2895 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2897 TRACE("surface %p.\n", surface);
2899 if (!surface->resource.device->d3d_initialized)
2901 ERR("D3D not initialized.\n");
2902 return;
2905 surface_internal_preload(surface, SRGB_ANY);
2908 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2910 TRACE("surface %p.\n", surface);
2912 return surface->resource.parent;
2915 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2917 TRACE("surface %p.\n", surface);
2919 return &surface->resource;
2922 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2924 TRACE("surface %p, flags %#x.\n", surface, flags);
2926 switch (flags)
2928 case WINEDDGBS_CANBLT:
2929 case WINEDDGBS_ISBLTDONE:
2930 return WINED3D_OK;
2932 default:
2933 return WINED3DERR_INVALIDCALL;
2937 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2939 TRACE("surface %p, flags %#x.\n", surface, flags);
2941 /* XXX: DDERR_INVALIDSURFACETYPE */
2943 switch (flags)
2945 case WINEDDGFS_CANFLIP:
2946 case WINEDDGFS_ISFLIPDONE:
2947 return WINED3D_OK;
2949 default:
2950 return WINED3DERR_INVALIDCALL;
2954 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2956 TRACE("surface %p.\n", surface);
2958 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2959 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2962 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2964 TRACE("surface %p.\n", surface);
2966 surface->flags &= ~SFLAG_LOST;
2967 return WINED3D_OK;
2970 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2972 TRACE("surface %p, palette %p.\n", surface, palette);
2974 if (surface->palette == palette)
2976 TRACE("Nop palette change.\n");
2977 return WINED3D_OK;
2980 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2981 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2983 surface->palette = palette;
2985 if (palette)
2987 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2988 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
2990 surface->surface_ops->surface_realize_palette(surface);
2993 return WINED3D_OK;
2996 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
2997 DWORD flags, const struct wined3d_color_key *color_key)
2999 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3001 if (flags & WINEDDCKEY_COLORSPACE)
3003 FIXME(" colorkey value not supported (%08x) !\n", flags);
3004 return WINED3DERR_INVALIDCALL;
3007 /* Dirtify the surface, but only if a key was changed. */
3008 if (color_key)
3010 switch (flags & ~WINEDDCKEY_COLORSPACE)
3012 case WINEDDCKEY_DESTBLT:
3013 surface->dst_blt_color_key = *color_key;
3014 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3015 break;
3017 case WINEDDCKEY_DESTOVERLAY:
3018 surface->dst_overlay_color_key = *color_key;
3019 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3020 break;
3022 case WINEDDCKEY_SRCOVERLAY:
3023 surface->src_overlay_color_key = *color_key;
3024 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3025 break;
3027 case WINEDDCKEY_SRCBLT:
3028 surface->src_blt_color_key = *color_key;
3029 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3030 break;
3033 else
3035 switch (flags & ~WINEDDCKEY_COLORSPACE)
3037 case WINEDDCKEY_DESTBLT:
3038 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3039 break;
3041 case WINEDDCKEY_DESTOVERLAY:
3042 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3043 break;
3045 case WINEDDCKEY_SRCOVERLAY:
3046 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3047 break;
3049 case WINEDDCKEY_SRCBLT:
3050 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3051 break;
3055 return WINED3D_OK;
3058 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3060 TRACE("surface %p.\n", surface);
3062 return surface->palette;
3065 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3067 const struct wined3d_format *format = surface->resource.format;
3068 DWORD pitch;
3070 TRACE("surface %p.\n", surface);
3072 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
3074 /* Since compressed formats are block based, pitch means the amount of
3075 * bytes to the next row of block rather than the next row of pixels. */
3076 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3077 pitch = row_block_count * format->block_byte_count;
3079 else
3081 unsigned char alignment = surface->resource.device->surface_alignment;
3082 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3083 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3086 TRACE("Returning %u.\n", pitch);
3088 return pitch;
3091 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3093 TRACE("surface %p, mem %p.\n", surface, mem);
3095 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3097 WARN("Surface is locked or the DC is in use.\n");
3098 return WINED3DERR_INVALIDCALL;
3101 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
3102 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3104 ERR("Not supported on render targets.\n");
3105 return WINED3DERR_INVALIDCALL;
3108 if (mem && mem != surface->resource.allocatedMemory)
3110 void *release = NULL;
3112 /* Do I have to copy the old surface content? */
3113 if (surface->flags & SFLAG_DIBSECTION)
3115 DeleteDC(surface->hDC);
3116 DeleteObject(surface->dib.DIBsection);
3117 surface->dib.bitmap_data = NULL;
3118 surface->resource.allocatedMemory = NULL;
3119 surface->hDC = NULL;
3120 surface->flags &= ~SFLAG_DIBSECTION;
3122 else if (!(surface->flags & SFLAG_USERPTR))
3124 release = surface->resource.heapMemory;
3125 surface->resource.heapMemory = NULL;
3127 surface->resource.allocatedMemory = mem;
3128 surface->flags |= SFLAG_USERPTR;
3130 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
3131 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3133 /* For client textures OpenGL has to be notified. */
3134 if (surface->flags & SFLAG_CLIENT)
3135 surface_release_client_storage(surface);
3137 /* Now free the old memory if any. */
3138 HeapFree(GetProcessHeap(), 0, release);
3140 else if (surface->flags & SFLAG_USERPTR)
3142 /* HeapMemory should be NULL already. */
3143 if (surface->resource.heapMemory)
3144 ERR("User pointer surface has heap memory allocated.\n");
3146 if (!mem)
3148 surface->resource.allocatedMemory = NULL;
3149 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
3151 if (surface->flags & SFLAG_CLIENT)
3152 surface_release_client_storage(surface);
3154 surface_prepare_system_memory(surface);
3157 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3160 return WINED3D_OK;
3163 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3165 LONG w, h;
3167 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3169 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3171 WARN("Not an overlay surface.\n");
3172 return WINEDDERR_NOTAOVERLAYSURFACE;
3175 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3176 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3177 surface->overlay_destrect.left = x;
3178 surface->overlay_destrect.top = y;
3179 surface->overlay_destrect.right = x + w;
3180 surface->overlay_destrect.bottom = y + h;
3182 surface_draw_overlay(surface);
3184 return WINED3D_OK;
3187 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3189 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3191 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3193 TRACE("Not an overlay surface.\n");
3194 return WINEDDERR_NOTAOVERLAYSURFACE;
3197 if (!surface->overlay_dest)
3199 TRACE("Overlay not visible.\n");
3200 *x = 0;
3201 *y = 0;
3202 return WINEDDERR_OVERLAYNOTVISIBLE;
3205 *x = surface->overlay_destrect.left;
3206 *y = surface->overlay_destrect.top;
3208 TRACE("Returning position %d, %d.\n", *x, *y);
3210 return WINED3D_OK;
3213 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3214 DWORD flags, struct wined3d_surface *ref)
3216 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3218 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3220 TRACE("Not an overlay surface.\n");
3221 return WINEDDERR_NOTAOVERLAYSURFACE;
3224 return WINED3D_OK;
3227 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3228 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3230 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3231 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3233 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3235 WARN("Not an overlay surface.\n");
3236 return WINEDDERR_NOTAOVERLAYSURFACE;
3238 else if (!dst_surface)
3240 WARN("Dest surface is NULL.\n");
3241 return WINED3DERR_INVALIDCALL;
3244 if (src_rect)
3246 surface->overlay_srcrect = *src_rect;
3248 else
3250 surface->overlay_srcrect.left = 0;
3251 surface->overlay_srcrect.top = 0;
3252 surface->overlay_srcrect.right = surface->resource.width;
3253 surface->overlay_srcrect.bottom = surface->resource.height;
3256 if (dst_rect)
3258 surface->overlay_destrect = *dst_rect;
3260 else
3262 surface->overlay_destrect.left = 0;
3263 surface->overlay_destrect.top = 0;
3264 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3265 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3268 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3270 surface->overlay_dest = NULL;
3271 list_remove(&surface->overlay_entry);
3274 if (flags & WINEDDOVER_SHOW)
3276 if (surface->overlay_dest != dst_surface)
3278 surface->overlay_dest = dst_surface;
3279 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3282 else if (flags & WINEDDOVER_HIDE)
3284 /* tests show that the rectangles are erased on hide */
3285 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3286 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3287 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3288 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3289 surface->overlay_dest = NULL;
3292 surface_draw_overlay(surface);
3294 return WINED3D_OK;
3297 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
3298 UINT width, UINT height, enum wined3d_format_id format_id,
3299 enum wined3d_multisample_type multisample_type, UINT multisample_quality)
3301 struct wined3d_device *device = surface->resource.device;
3302 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3303 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
3304 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height);
3306 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u.\n",
3307 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type);
3309 if (!resource_size)
3310 return WINED3DERR_INVALIDCALL;
3312 if (device->d3d_initialized)
3313 surface->resource.resource_ops->resource_unload(&surface->resource);
3315 if (surface->flags & SFLAG_DIBSECTION)
3317 DeleteDC(surface->hDC);
3318 DeleteObject(surface->dib.DIBsection);
3319 surface->dib.bitmap_data = NULL;
3320 surface->flags &= ~SFLAG_DIBSECTION;
3323 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_USERPTR);
3324 surface->resource.allocatedMemory = NULL;
3325 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
3326 surface->resource.heapMemory = NULL;
3328 surface->resource.width = width;
3329 surface->resource.height = height;
3330 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
3331 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
3333 surface->pow2Width = width;
3334 surface->pow2Height = height;
3336 else
3338 surface->pow2Width = surface->pow2Height = 1;
3339 while (surface->pow2Width < width)
3340 surface->pow2Width <<= 1;
3341 while (surface->pow2Height < height)
3342 surface->pow2Height <<= 1;
3345 if (surface->pow2Width != width || surface->pow2Height != height)
3346 surface->flags |= SFLAG_NONPOW2;
3347 else
3348 surface->flags &= ~SFLAG_NONPOW2;
3350 surface->resource.format = format;
3351 surface->resource.multisample_type = multisample_type;
3352 surface->resource.multisample_quality = multisample_quality;
3353 surface->resource.size = resource_size;
3355 if (!surface_init_sysmem(surface))
3356 return E_OUTOFMEMORY;
3358 return WINED3D_OK;
3361 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3362 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3364 unsigned short *dst_s;
3365 const float *src_f;
3366 unsigned int x, y;
3368 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3370 for (y = 0; y < h; ++y)
3372 src_f = (const float *)(src + y * pitch_in);
3373 dst_s = (unsigned short *) (dst + y * pitch_out);
3374 for (x = 0; x < w; ++x)
3376 dst_s[x] = float_32_to_16(src_f + x);
3381 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3382 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3384 static const unsigned char convert_5to8[] =
3386 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3387 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3388 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3389 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3391 static const unsigned char convert_6to8[] =
3393 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3394 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3395 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3396 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3397 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3398 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3399 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3400 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3402 unsigned int x, y;
3404 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3406 for (y = 0; y < h; ++y)
3408 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3409 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3410 for (x = 0; x < w; ++x)
3412 WORD pixel = src_line[x];
3413 dst_line[x] = 0xff000000
3414 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3415 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3416 | convert_5to8[(pixel & 0x001f)];
3421 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3422 * in both cases we're just setting the X / Alpha channel to 0xff. */
3423 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3424 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3426 unsigned int x, y;
3428 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3430 for (y = 0; y < h; ++y)
3432 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3433 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3435 for (x = 0; x < w; ++x)
3437 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3442 static inline BYTE cliptobyte(int x)
3444 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3447 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3448 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3450 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3451 unsigned int x, y;
3453 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3455 for (y = 0; y < h; ++y)
3457 const BYTE *src_line = src + y * pitch_in;
3458 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3459 for (x = 0; x < w; ++x)
3461 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3462 * C = Y - 16; D = U - 128; E = V - 128;
3463 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3464 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3465 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3466 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3467 * U and V are shared between the pixels. */
3468 if (!(x & 1)) /* For every even pixel, read new U and V. */
3470 d = (int) src_line[1] - 128;
3471 e = (int) src_line[3] - 128;
3472 r2 = 409 * e + 128;
3473 g2 = - 100 * d - 208 * e + 128;
3474 b2 = 516 * d + 128;
3476 c2 = 298 * ((int) src_line[0] - 16);
3477 dst_line[x] = 0xff000000
3478 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3479 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3480 | cliptobyte((c2 + b2) >> 8); /* blue */
3481 /* Scale RGB values to 0..255 range,
3482 * then clip them if still not in range (may be negative),
3483 * then shift them within DWORD if necessary. */
3484 src_line += 2;
3489 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3490 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3492 unsigned int x, y;
3493 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3495 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3497 for (y = 0; y < h; ++y)
3499 const BYTE *src_line = src + y * pitch_in;
3500 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3501 for (x = 0; x < w; ++x)
3503 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3504 * C = Y - 16; D = U - 128; E = V - 128;
3505 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3506 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3507 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3508 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3509 * U and V are shared between the pixels. */
3510 if (!(x & 1)) /* For every even pixel, read new U and V. */
3512 d = (int) src_line[1] - 128;
3513 e = (int) src_line[3] - 128;
3514 r2 = 409 * e + 128;
3515 g2 = - 100 * d - 208 * e + 128;
3516 b2 = 516 * d + 128;
3518 c2 = 298 * ((int) src_line[0] - 16);
3519 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3520 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3521 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3522 /* Scale RGB values to 0..255 range,
3523 * then clip them if still not in range (may be negative),
3524 * then shift them within DWORD if necessary. */
3525 src_line += 2;
3530 struct d3dfmt_convertor_desc
3532 enum wined3d_format_id from, to;
3533 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3536 static const struct d3dfmt_convertor_desc convertors[] =
3538 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3539 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3540 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3541 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3542 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3543 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3546 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3547 enum wined3d_format_id to)
3549 unsigned int i;
3551 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3553 if (convertors[i].from == from && convertors[i].to == to)
3554 return &convertors[i];
3557 return NULL;
3560 /*****************************************************************************
3561 * surface_convert_format
3563 * Creates a duplicate of a surface in a different format. Is used by Blt to
3564 * blit between surfaces with different formats.
3566 * Parameters
3567 * source: Source surface
3568 * fmt: Requested destination format
3570 *****************************************************************************/
3571 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3573 struct wined3d_mapped_rect src_map, dst_map;
3574 const struct d3dfmt_convertor_desc *conv;
3575 struct wined3d_surface *ret = NULL;
3576 HRESULT hr;
3578 conv = find_convertor(source->resource.format->id, to_fmt);
3579 if (!conv)
3581 FIXME("Cannot find a conversion function from format %s to %s.\n",
3582 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3583 return NULL;
3586 wined3d_surface_create(source->resource.device, source->resource.width,
3587 source->resource.height, to_fmt, 0 /* level */, 0 /* usage */, WINED3D_POOL_SCRATCH,
3588 WINED3D_MULTISAMPLE_NONE /* TODO: Multisampled conversion */, 0 /* MultiSampleQuality */,
3589 source->surface_type, WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD,
3590 NULL /* parent */, &wined3d_null_parent_ops, &ret);
3591 if (!ret)
3593 ERR("Failed to create a destination surface for conversion.\n");
3594 return NULL;
3597 memset(&src_map, 0, sizeof(src_map));
3598 memset(&dst_map, 0, sizeof(dst_map));
3600 hr = wined3d_surface_map(source, &src_map, NULL, WINED3DLOCK_READONLY);
3601 if (FAILED(hr))
3603 ERR("Failed to lock the source surface.\n");
3604 wined3d_surface_decref(ret);
3605 return NULL;
3607 hr = wined3d_surface_map(ret, &dst_map, NULL, WINED3DLOCK_READONLY);
3608 if (FAILED(hr))
3610 ERR("Failed to lock the destination surface.\n");
3611 wined3d_surface_unmap(source);
3612 wined3d_surface_decref(ret);
3613 return NULL;
3616 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
3617 source->resource.width, source->resource.height);
3619 wined3d_surface_unmap(ret);
3620 wined3d_surface_unmap(source);
3622 return ret;
3625 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3626 unsigned int bpp, UINT pitch, DWORD color)
3628 BYTE *first;
3629 int x, y;
3631 /* Do first row */
3633 #define COLORFILL_ROW(type) \
3634 do { \
3635 type *d = (type *)buf; \
3636 for (x = 0; x < width; ++x) \
3637 d[x] = (type)color; \
3638 } while(0)
3640 switch (bpp)
3642 case 1:
3643 COLORFILL_ROW(BYTE);
3644 break;
3646 case 2:
3647 COLORFILL_ROW(WORD);
3648 break;
3650 case 3:
3652 BYTE *d = buf;
3653 for (x = 0; x < width; ++x, d += 3)
3655 d[0] = (color ) & 0xFF;
3656 d[1] = (color >> 8) & 0xFF;
3657 d[2] = (color >> 16) & 0xFF;
3659 break;
3661 case 4:
3662 COLORFILL_ROW(DWORD);
3663 break;
3665 default:
3666 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3667 return WINED3DERR_NOTAVAILABLE;
3670 #undef COLORFILL_ROW
3672 /* Now copy first row. */
3673 first = buf;
3674 for (y = 1; y < height; ++y)
3676 buf += pitch;
3677 memcpy(buf, first, width * bpp);
3680 return WINED3D_OK;
3683 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3685 TRACE("surface %p.\n", surface);
3687 if (!(surface->flags & SFLAG_LOCKED))
3689 WARN("Trying to unmap unmapped surface.\n");
3690 return WINEDDERR_NOTLOCKED;
3692 surface->flags &= ~SFLAG_LOCKED;
3694 surface->surface_ops->surface_unmap(surface);
3696 return WINED3D_OK;
3699 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3700 struct wined3d_mapped_rect *mapped_rect, const RECT *rect, DWORD flags)
3702 const struct wined3d_format *format = surface->resource.format;
3704 TRACE("surface %p, mapped_rect %p, rect %s, flags %#x.\n",
3705 surface, mapped_rect, wine_dbgstr_rect(rect), flags);
3707 if (surface->flags & SFLAG_LOCKED)
3709 WARN("Surface is already mapped.\n");
3710 return WINED3DERR_INVALIDCALL;
3712 if ((format->flags & WINED3DFMT_FLAG_BLOCKS)
3713 && rect && (rect->left || rect->top
3714 || rect->right != surface->resource.width
3715 || rect->bottom != surface->resource.height))
3717 UINT width_mask = format->block_width - 1;
3718 UINT height_mask = format->block_height - 1;
3720 if ((rect->left & width_mask) || (rect->right & width_mask)
3721 || (rect->top & height_mask) || (rect->bottom & height_mask))
3723 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3724 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3726 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
3727 return WINED3DERR_INVALIDCALL;
3731 surface->flags |= SFLAG_LOCKED;
3733 if (!(surface->flags & SFLAG_LOCKABLE))
3734 WARN("Trying to lock unlockable surface.\n");
3736 /* Performance optimization: Count how often a surface is mapped, if it is
3737 * mapped regularly do not throw away the system memory copy. This avoids
3738 * the need to download the surface from OpenGL all the time. The surface
3739 * is still downloaded if the OpenGL texture is changed. */
3740 if (!(surface->flags & SFLAG_DYNLOCK))
3742 if (++surface->lockCount > MAXLOCKCOUNT)
3744 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3745 surface->flags |= SFLAG_DYNLOCK;
3749 surface->surface_ops->surface_map(surface, rect, flags);
3751 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3752 mapped_rect->row_pitch = surface->resource.width * format->byte_count;
3753 else
3754 mapped_rect->row_pitch = wined3d_surface_get_pitch(surface);
3756 if (!rect)
3758 mapped_rect->data = surface->resource.allocatedMemory;
3759 surface->lockedRect.left = 0;
3760 surface->lockedRect.top = 0;
3761 surface->lockedRect.right = surface->resource.width;
3762 surface->lockedRect.bottom = surface->resource.height;
3764 else
3766 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3768 /* Compressed textures are block based, so calculate the offset of
3769 * the block that contains the top-left pixel of the locked rectangle. */
3770 mapped_rect->data = surface->resource.allocatedMemory
3771 + ((rect->top / format->block_height) * mapped_rect->row_pitch)
3772 + ((rect->left / format->block_width) * format->block_byte_count);
3774 else
3776 mapped_rect->data = surface->resource.allocatedMemory
3777 + (mapped_rect->row_pitch * rect->top)
3778 + (rect->left * format->byte_count);
3780 surface->lockedRect.left = rect->left;
3781 surface->lockedRect.top = rect->top;
3782 surface->lockedRect.right = rect->right;
3783 surface->lockedRect.bottom = rect->bottom;
3786 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3787 TRACE("Returning memory %p, pitch %u.\n", mapped_rect->data, mapped_rect->row_pitch);
3789 return WINED3D_OK;
3792 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3794 struct wined3d_mapped_rect map;
3795 HRESULT hr;
3797 TRACE("surface %p, dc %p.\n", surface, dc);
3799 if (surface->flags & SFLAG_USERPTR)
3801 ERR("Not supported on surfaces with application-provided memory.\n");
3802 return WINEDDERR_NODC;
3805 /* Give more detailed info for ddraw. */
3806 if (surface->flags & SFLAG_DCINUSE)
3807 return WINEDDERR_DCALREADYCREATED;
3809 /* Can't GetDC if the surface is locked. */
3810 if (surface->flags & SFLAG_LOCKED)
3811 return WINED3DERR_INVALIDCALL;
3813 /* Create a DIB section if there isn't a dc yet. */
3814 if (!surface->hDC)
3816 if (surface->flags & SFLAG_CLIENT)
3818 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3819 surface_release_client_storage(surface);
3821 hr = surface_create_dib_section(surface);
3822 if (FAILED(hr))
3823 return WINED3DERR_INVALIDCALL;
3825 /* Use the DIB section from now on if we are not using a PBO. */
3826 if (!(surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)))
3828 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
3829 surface->resource.heapMemory = NULL;
3830 surface->resource.allocatedMemory = surface->dib.bitmap_data;
3834 /* Map the surface. */
3835 hr = wined3d_surface_map(surface, &map, NULL, 0);
3836 if (FAILED(hr))
3838 ERR("Map failed, hr %#x.\n", hr);
3839 return hr;
3842 /* Sync the DIB with the PBO. This can't be done earlier because Map()
3843 * activates the allocatedMemory. */
3844 if (surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM))
3845 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
3847 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3848 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3850 /* GetDC on palettized formats is unsupported in D3D9, and the method
3851 * is missing in D3D8, so this should only be used for DX <=7
3852 * surfaces (with non-device palettes). */
3853 const PALETTEENTRY *pal = NULL;
3855 if (surface->palette)
3857 pal = surface->palette->palents;
3859 else
3861 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3862 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3864 if (dds_primary && dds_primary->palette)
3865 pal = dds_primary->palette->palents;
3868 if (pal)
3870 RGBQUAD col[256];
3871 unsigned int i;
3873 for (i = 0; i < 256; ++i)
3875 col[i].rgbRed = pal[i].peRed;
3876 col[i].rgbGreen = pal[i].peGreen;
3877 col[i].rgbBlue = pal[i].peBlue;
3878 col[i].rgbReserved = 0;
3880 SetDIBColorTable(surface->hDC, 0, 256, col);
3884 surface->flags |= SFLAG_DCINUSE;
3886 *dc = surface->hDC;
3887 TRACE("Returning dc %p.\n", *dc);
3889 return WINED3D_OK;
3892 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3894 TRACE("surface %p, dc %p.\n", surface, dc);
3896 if (!(surface->flags & SFLAG_DCINUSE))
3897 return WINEDDERR_NODC;
3899 if (surface->hDC != dc)
3901 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3902 dc, surface->hDC);
3903 return WINEDDERR_NODC;
3906 /* Copy the contents of the DIB over to the PBO. */
3907 if ((surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)) && surface->resource.allocatedMemory)
3908 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3910 /* We locked first, so unlock now. */
3911 wined3d_surface_unmap(surface);
3913 surface->flags &= ~SFLAG_DCINUSE;
3915 return WINED3D_OK;
3918 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3920 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3922 if (flags)
3924 static UINT once;
3925 if (!once++)
3926 FIXME("Ignoring flags %#x.\n", flags);
3927 else
3928 WARN("Ignoring flags %#x.\n", flags);
3931 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
3933 ERR("Not supported on swapchain surfaces.\n");
3934 return WINEDDERR_NOTFLIPPABLE;
3937 /* Flipping is only supported on render targets and overlays. */
3938 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
3940 WARN("Tried to flip a non-render target, non-overlay surface.\n");
3941 return WINEDDERR_NOTFLIPPABLE;
3944 flip_surface(surface, override);
3946 /* Update overlays if they're visible. */
3947 if ((surface->resource.usage & WINED3DUSAGE_OVERLAY) && surface->overlay_dest)
3948 return surface_draw_overlay(surface);
3950 return WINED3D_OK;
3953 /* Do not call while under the GL lock. */
3954 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3956 struct wined3d_device *device = surface->resource.device;
3958 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3960 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3962 struct wined3d_texture *texture = surface->container.u.texture;
3964 TRACE("Passing to container (%p).\n", texture);
3965 texture->texture_ops->texture_preload(texture, srgb);
3967 else
3969 struct wined3d_context *context;
3971 TRACE("(%p) : About to load surface\n", surface);
3973 /* TODO: Use already acquired context when possible. */
3974 context = context_acquire(device, NULL);
3976 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3978 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
3980 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3981 GLclampf tmp;
3982 tmp = 0.9f;
3983 ENTER_GL();
3984 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3985 LEAVE_GL();
3988 context_release(context);
3992 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3994 if (!surface->resource.allocatedMemory)
3996 if (!surface->resource.heapMemory)
3998 if (!(surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3999 surface->resource.size + RESOURCE_ALIGNMENT)))
4001 ERR("Failed to allocate memory.\n");
4002 return FALSE;
4005 else if (!(surface->flags & SFLAG_CLIENT))
4007 ERR("Surface %p has heapMemory %p and flags %#x.\n",
4008 surface, surface->resource.heapMemory, surface->flags);
4011 surface->resource.allocatedMemory =
4012 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
4014 else
4016 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
4019 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
4021 return TRUE;
4024 /* Read the framebuffer back into the surface */
4025 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
4027 struct wined3d_device *device = surface->resource.device;
4028 const struct wined3d_gl_info *gl_info;
4029 struct wined3d_context *context;
4030 BYTE *mem;
4031 GLint fmt;
4032 GLint type;
4033 BYTE *row, *top, *bottom;
4034 int i;
4035 BOOL bpp;
4036 RECT local_rect;
4037 BOOL srcIsUpsideDown;
4038 GLint rowLen = 0;
4039 GLint skipPix = 0;
4040 GLint skipRow = 0;
4042 context = context_acquire(device, surface);
4043 context_apply_blit_state(context, device);
4044 gl_info = context->gl_info;
4046 ENTER_GL();
4048 /* Select the correct read buffer, and give some debug output.
4049 * There is no need to keep track of the current read buffer or reset it, every part of the code
4050 * that reads sets the read buffer as desired.
4052 if (surface_is_offscreen(surface))
4054 /* Mapping the primary render target which is not on a swapchain.
4055 * Read from the back buffer. */
4056 TRACE("Mapping offscreen render target.\n");
4057 glReadBuffer(device->offscreenBuffer);
4058 srcIsUpsideDown = TRUE;
4060 else
4062 /* Onscreen surfaces are always part of a swapchain */
4063 GLenum buffer = surface_get_gl_buffer(surface);
4064 TRACE("Mapping %#x buffer.\n", buffer);
4065 glReadBuffer(buffer);
4066 checkGLcall("glReadBuffer");
4067 srcIsUpsideDown = FALSE;
4070 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
4071 if (!rect)
4073 local_rect.left = 0;
4074 local_rect.top = 0;
4075 local_rect.right = surface->resource.width;
4076 local_rect.bottom = surface->resource.height;
4078 else
4080 local_rect = *rect;
4082 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
4084 switch (surface->resource.format->id)
4086 case WINED3DFMT_P8_UINT:
4088 if (primary_render_target_is_p8(device))
4090 /* In case of P8 render targets the index is stored in the alpha component */
4091 fmt = GL_ALPHA;
4092 type = GL_UNSIGNED_BYTE;
4093 mem = dest;
4094 bpp = surface->resource.format->byte_count;
4096 else
4098 /* GL can't return palettized data, so read ARGB pixels into a
4099 * separate block of memory and convert them into palettized format
4100 * in software. Slow, but if the app means to use palettized render
4101 * targets and locks it...
4103 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4104 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4105 * for the color channels when palettizing the colors.
4107 fmt = GL_RGB;
4108 type = GL_UNSIGNED_BYTE;
4109 pitch *= 3;
4110 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4111 if (!mem)
4113 ERR("Out of memory\n");
4114 LEAVE_GL();
4115 return;
4117 bpp = surface->resource.format->byte_count * 3;
4120 break;
4122 default:
4123 mem = dest;
4124 fmt = surface->resource.format->glFormat;
4125 type = surface->resource.format->glType;
4126 bpp = surface->resource.format->byte_count;
4129 if (surface->flags & SFLAG_PBO)
4131 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4132 checkGLcall("glBindBufferARB");
4133 if (mem)
4135 ERR("mem not null for pbo -- unexpected\n");
4136 mem = NULL;
4140 /* Save old pixel store pack state */
4141 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4142 checkGLcall("glGetIntegerv");
4143 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4144 checkGLcall("glGetIntegerv");
4145 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4146 checkGLcall("glGetIntegerv");
4148 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4149 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4150 checkGLcall("glPixelStorei");
4151 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4152 checkGLcall("glPixelStorei");
4153 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4154 checkGLcall("glPixelStorei");
4156 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4157 local_rect.right - local_rect.left,
4158 local_rect.bottom - local_rect.top,
4159 fmt, type, mem);
4160 checkGLcall("glReadPixels");
4162 /* Reset previous pixel store pack state */
4163 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4164 checkGLcall("glPixelStorei");
4165 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4166 checkGLcall("glPixelStorei");
4167 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4168 checkGLcall("glPixelStorei");
4170 if (surface->flags & SFLAG_PBO)
4172 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4173 checkGLcall("glBindBufferARB");
4175 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4176 * to get a pointer to it and perform the flipping in software. This is a lot
4177 * faster than calling glReadPixels for each line. In case we want more speed
4178 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4179 if (!srcIsUpsideDown)
4181 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4182 checkGLcall("glBindBufferARB");
4184 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4185 checkGLcall("glMapBufferARB");
4189 /* TODO: Merge this with the palettization loop below for P8 targets */
4190 if(!srcIsUpsideDown) {
4191 UINT len, off;
4192 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4193 Flip the lines in software */
4194 len = (local_rect.right - local_rect.left) * bpp;
4195 off = local_rect.left * bpp;
4197 row = HeapAlloc(GetProcessHeap(), 0, len);
4198 if(!row) {
4199 ERR("Out of memory\n");
4200 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4201 HeapFree(GetProcessHeap(), 0, mem);
4202 LEAVE_GL();
4203 return;
4206 top = mem + pitch * local_rect.top;
4207 bottom = mem + pitch * (local_rect.bottom - 1);
4208 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4209 memcpy(row, top + off, len);
4210 memcpy(top + off, bottom + off, len);
4211 memcpy(bottom + off, row, len);
4212 top += pitch;
4213 bottom -= pitch;
4215 HeapFree(GetProcessHeap(), 0, row);
4217 /* Unmap the temp PBO buffer */
4218 if (surface->flags & SFLAG_PBO)
4220 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4221 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4225 LEAVE_GL();
4226 context_release(context);
4228 /* For P8 textures we need to perform an inverse palette lookup. This is
4229 * done by searching for a palette index which matches the RGB value.
4230 * Note this isn't guaranteed to work when there are multiple entries for
4231 * the same color but we have no choice. In case of P8 render targets,
4232 * the index is stored in the alpha component so no conversion is needed. */
4233 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4235 const PALETTEENTRY *pal = NULL;
4236 DWORD width = pitch / 3;
4237 int x, y, c;
4239 if (surface->palette)
4241 pal = surface->palette->palents;
4243 else
4245 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4246 HeapFree(GetProcessHeap(), 0, mem);
4247 return;
4250 for(y = local_rect.top; y < local_rect.bottom; y++) {
4251 for(x = local_rect.left; x < local_rect.right; x++) {
4252 /* start lines pixels */
4253 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4254 const BYTE *green = blue + 1;
4255 const BYTE *red = green + 1;
4257 for(c = 0; c < 256; c++) {
4258 if(*red == pal[c].peRed &&
4259 *green == pal[c].peGreen &&
4260 *blue == pal[c].peBlue)
4262 *((BYTE *) dest + y * width + x) = c;
4263 break;
4268 HeapFree(GetProcessHeap(), 0, mem);
4272 /* Read the framebuffer contents into a texture. Note that this function
4273 * doesn't do any kind of flipping. Using this on an onscreen surface will
4274 * result in a flipped D3D texture. */
4275 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
4277 struct wined3d_device *device = surface->resource.device;
4278 struct wined3d_context *context;
4280 context = context_acquire(device, surface);
4281 device_invalidate_state(device, STATE_FRAMEBUFFER);
4283 surface_prepare_texture(surface, context, srgb);
4284 surface_bind_and_dirtify(surface, context, srgb);
4286 TRACE("Reading back offscreen render target %p.\n", surface);
4288 ENTER_GL();
4290 if (surface_is_offscreen(surface))
4291 glReadBuffer(device->offscreenBuffer);
4292 else
4293 glReadBuffer(surface_get_gl_buffer(surface));
4294 checkGLcall("glReadBuffer");
4296 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4297 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4298 checkGLcall("glCopyTexSubImage2D");
4300 LEAVE_GL();
4302 context_release(context);
4305 /* Context activation is done by the caller. */
4306 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4307 struct wined3d_context *context, BOOL srgb)
4309 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4310 enum wined3d_conversion_type convert;
4311 struct wined3d_format format;
4313 if (surface->flags & alloc_flag) return;
4315 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4316 if (convert != WINED3D_CT_NONE || format.convert)
4317 surface->flags |= SFLAG_CONVERTED;
4318 else surface->flags &= ~SFLAG_CONVERTED;
4320 surface_bind_and_dirtify(surface, context, srgb);
4321 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4322 surface->flags |= alloc_flag;
4325 /* Context activation is done by the caller. */
4326 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4328 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4330 struct wined3d_texture *texture = surface->container.u.texture;
4331 UINT sub_count = texture->level_count * texture->layer_count;
4332 UINT i;
4334 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4336 for (i = 0; i < sub_count; ++i)
4338 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4339 surface_prepare_texture_internal(s, context, srgb);
4342 return;
4345 surface_prepare_texture_internal(surface, context, srgb);
4348 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4350 if (multisample)
4352 if (surface->rb_multisample)
4353 return;
4355 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4356 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4357 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4358 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4359 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4361 else
4363 if (surface->rb_resolved)
4364 return;
4366 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4367 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4368 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4369 surface->pow2Width, surface->pow2Height);
4370 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4374 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4375 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4377 struct wined3d_device *device = surface->resource.device;
4378 UINT pitch = wined3d_surface_get_pitch(surface);
4379 const struct wined3d_gl_info *gl_info;
4380 struct wined3d_context *context;
4381 RECT local_rect;
4382 UINT w, h;
4384 surface_get_rect(surface, rect, &local_rect);
4386 mem += local_rect.top * pitch + local_rect.left * bpp;
4387 w = local_rect.right - local_rect.left;
4388 h = local_rect.bottom - local_rect.top;
4390 /* Activate the correct context for the render target */
4391 context = context_acquire(device, surface);
4392 context_apply_blit_state(context, device);
4393 gl_info = context->gl_info;
4395 ENTER_GL();
4397 if (!surface_is_offscreen(surface))
4399 GLenum buffer = surface_get_gl_buffer(surface);
4400 TRACE("Unlocking %#x buffer.\n", buffer);
4401 context_set_draw_buffer(context, buffer);
4403 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4404 glPixelZoom(1.0f, -1.0f);
4406 else
4408 /* Primary offscreen render target */
4409 TRACE("Offscreen render target.\n");
4410 context_set_draw_buffer(context, device->offscreenBuffer);
4412 glPixelZoom(1.0f, 1.0f);
4415 glRasterPos3i(local_rect.left, local_rect.top, 1);
4416 checkGLcall("glRasterPos3i");
4418 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4419 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4421 if (surface->flags & SFLAG_PBO)
4423 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4424 checkGLcall("glBindBufferARB");
4427 glDrawPixels(w, h, fmt, type, mem);
4428 checkGLcall("glDrawPixels");
4430 if (surface->flags & SFLAG_PBO)
4432 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4433 checkGLcall("glBindBufferARB");
4436 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4437 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4439 LEAVE_GL();
4441 if (wined3d_settings.strict_draw_ordering
4442 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4443 && surface->container.u.swapchain->front_buffer == surface))
4444 wglFlush();
4446 context_release(context);
4449 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck, BOOL use_texturing,
4450 struct wined3d_format *format, enum wined3d_conversion_type *conversion_type)
4452 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4453 const struct wined3d_device *device = surface->resource.device;
4454 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4455 BOOL blit_supported = FALSE;
4457 /* Copy the default values from the surface. Below we might perform fixups */
4458 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4459 *format = *surface->resource.format;
4460 *conversion_type = WINED3D_CT_NONE;
4462 /* Ok, now look if we have to do any conversion */
4463 switch (surface->resource.format->id)
4465 case WINED3DFMT_P8_UINT:
4466 /* Below the call to blit_supported is disabled for Wine 1.2
4467 * because the function isn't operating correctly yet. At the
4468 * moment 8-bit blits are handled in software and if certain GL
4469 * extensions are around, surface conversion is performed at
4470 * upload time. The blit_supported call recognizes it as a
4471 * destination fixup. This type of upload 'fixup' and 8-bit to
4472 * 8-bit blits need to be handled by the blit_shader.
4473 * TODO: get rid of this #if 0. */
4474 #if 0
4475 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4476 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4477 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4478 #endif
4479 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4481 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4482 * texturing. Further also use conversion in case of color keying.
4483 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4484 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4485 * conflicts with this.
4487 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4488 || colorkey_active || !use_texturing)
4490 format->glFormat = GL_RGBA;
4491 format->glInternal = GL_RGBA;
4492 format->glType = GL_UNSIGNED_BYTE;
4493 format->conv_byte_count = 4;
4494 if (colorkey_active)
4495 *conversion_type = WINED3D_CT_PALETTED_CK;
4496 else
4497 *conversion_type = WINED3D_CT_PALETTED;
4499 break;
4501 case WINED3DFMT_B2G3R3_UNORM:
4502 /* **********************
4503 GL_UNSIGNED_BYTE_3_3_2
4504 ********************** */
4505 if (colorkey_active) {
4506 /* This texture format will never be used.. So do not care about color keying
4507 up until the point in time it will be needed :-) */
4508 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4510 break;
4512 case WINED3DFMT_B5G6R5_UNORM:
4513 if (colorkey_active)
4515 *conversion_type = WINED3D_CT_CK_565;
4516 format->glFormat = GL_RGBA;
4517 format->glInternal = GL_RGB5_A1;
4518 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4519 format->conv_byte_count = 2;
4521 break;
4523 case WINED3DFMT_B5G5R5X1_UNORM:
4524 if (colorkey_active)
4526 *conversion_type = WINED3D_CT_CK_5551;
4527 format->glFormat = GL_BGRA;
4528 format->glInternal = GL_RGB5_A1;
4529 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4530 format->conv_byte_count = 2;
4532 break;
4534 case WINED3DFMT_B8G8R8_UNORM:
4535 if (colorkey_active)
4537 *conversion_type = WINED3D_CT_CK_RGB24;
4538 format->glFormat = GL_RGBA;
4539 format->glInternal = GL_RGBA8;
4540 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4541 format->conv_byte_count = 4;
4543 break;
4545 case WINED3DFMT_B8G8R8X8_UNORM:
4546 if (colorkey_active)
4548 *conversion_type = WINED3D_CT_RGB32_888;
4549 format->glFormat = GL_RGBA;
4550 format->glInternal = GL_RGBA8;
4551 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4552 format->conv_byte_count = 4;
4554 break;
4556 default:
4557 break;
4560 if (*conversion_type != WINED3D_CT_NONE)
4562 format->rtInternal = format->glInternal;
4563 format->glGammaInternal = format->glInternal;
4566 return WINED3D_OK;
4569 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
4571 /* FIXME: Is this really how color keys are supposed to work? I think it
4572 * makes more sense to compare the individual channels. */
4573 return color >= color_key->color_space_low_value
4574 && color <= color_key->color_space_high_value;
4577 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4579 const struct wined3d_device *device = surface->resource.device;
4580 const struct wined3d_palette *pal = surface->palette;
4581 BOOL index_in_alpha = FALSE;
4582 unsigned int i;
4584 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4585 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4586 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4587 * duplicate entries. Store the color key in the unused alpha component to speed the
4588 * download up and to make conversion unneeded. */
4589 index_in_alpha = primary_render_target_is_p8(device);
4591 if (!pal)
4593 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4594 if (index_in_alpha)
4596 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4597 * there's no palette at this time. */
4598 for (i = 0; i < 256; i++) table[i][3] = i;
4601 else
4603 TRACE("Using surface palette %p\n", pal);
4604 /* Get the surface's palette */
4605 for (i = 0; i < 256; ++i)
4607 table[i][0] = pal->palents[i].peRed;
4608 table[i][1] = pal->palents[i].peGreen;
4609 table[i][2] = pal->palents[i].peBlue;
4611 /* When index_in_alpha is set the palette index is stored in the
4612 * alpha component. In case of a readback we can then read
4613 * GL_ALPHA. Color keying is handled in BltOverride using a
4614 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4615 * color key itself is passed to glAlphaFunc in other cases the
4616 * alpha component of pixels that should be masked away is set to 0. */
4617 if (index_in_alpha)
4618 table[i][3] = i;
4619 else if (colorkey && color_in_range(&surface->src_blt_color_key, i))
4620 table[i][3] = 0x00;
4621 else if (pal->flags & WINEDDPCAPS_ALPHA)
4622 table[i][3] = pal->palents[i].peFlags;
4623 else
4624 table[i][3] = 0xFF;
4629 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
4630 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
4632 const BYTE *source;
4633 BYTE *dest;
4635 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
4636 src, dst, pitch, width, height, outpitch, conversion_type, surface);
4638 switch (conversion_type)
4640 case WINED3D_CT_NONE:
4642 memcpy(dst, src, pitch * height);
4643 break;
4646 case WINED3D_CT_PALETTED:
4647 case WINED3D_CT_PALETTED_CK:
4649 BYTE table[256][4];
4650 unsigned int x, y;
4652 d3dfmt_p8_init_palette(surface, table, (conversion_type == WINED3D_CT_PALETTED_CK));
4654 for (y = 0; y < height; y++)
4656 source = src + pitch * y;
4657 dest = dst + outpitch * y;
4658 /* This is an 1 bpp format, using the width here is fine */
4659 for (x = 0; x < width; x++) {
4660 BYTE color = *source++;
4661 *dest++ = table[color][0];
4662 *dest++ = table[color][1];
4663 *dest++ = table[color][2];
4664 *dest++ = table[color][3];
4668 break;
4670 case WINED3D_CT_CK_565:
4672 /* Converting the 565 format in 5551 packed to emulate color-keying.
4674 Note : in all these conversion, it would be best to average the averaging
4675 pixels to get the color of the pixel that will be color-keyed to
4676 prevent 'color bleeding'. This will be done later on if ever it is
4677 too visible.
4679 Note2: Nvidia documents say that their driver does not support alpha + color keying
4680 on the same surface and disables color keying in such a case
4682 unsigned int x, y;
4683 const WORD *Source;
4684 WORD *Dest;
4686 TRACE("Color keyed 565\n");
4688 for (y = 0; y < height; y++) {
4689 Source = (const WORD *)(src + y * pitch);
4690 Dest = (WORD *) (dst + y * outpitch);
4691 for (x = 0; x < width; x++ ) {
4692 WORD color = *Source++;
4693 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4694 if (!color_in_range(&surface->src_blt_color_key, color))
4695 *Dest |= 0x0001;
4696 Dest++;
4700 break;
4702 case WINED3D_CT_CK_5551:
4704 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4705 unsigned int x, y;
4706 const WORD *Source;
4707 WORD *Dest;
4708 TRACE("Color keyed 5551\n");
4709 for (y = 0; y < height; y++) {
4710 Source = (const WORD *)(src + y * pitch);
4711 Dest = (WORD *) (dst + y * outpitch);
4712 for (x = 0; x < width; x++ ) {
4713 WORD color = *Source++;
4714 *Dest = color;
4715 if (!color_in_range(&surface->src_blt_color_key, color))
4716 *Dest |= (1 << 15);
4717 else
4718 *Dest &= ~(1 << 15);
4719 Dest++;
4723 break;
4725 case WINED3D_CT_CK_RGB24:
4727 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4728 unsigned int x, y;
4729 for (y = 0; y < height; y++)
4731 source = src + pitch * y;
4732 dest = dst + outpitch * y;
4733 for (x = 0; x < width; x++) {
4734 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4735 DWORD dstcolor = color << 8;
4736 if (!color_in_range(&surface->src_blt_color_key, color))
4737 dstcolor |= 0xff;
4738 *(DWORD*)dest = dstcolor;
4739 source += 3;
4740 dest += 4;
4744 break;
4746 case WINED3D_CT_RGB32_888:
4748 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4749 unsigned int x, y;
4750 for (y = 0; y < height; y++)
4752 source = src + pitch * y;
4753 dest = dst + outpitch * y;
4754 for (x = 0; x < width; x++) {
4755 DWORD color = 0xffffff & *(const DWORD*)source;
4756 DWORD dstcolor = color << 8;
4757 if (!color_in_range(&surface->src_blt_color_key, color))
4758 dstcolor |= 0xff;
4759 *(DWORD*)dest = dstcolor;
4760 source += 4;
4761 dest += 4;
4765 break;
4767 default:
4768 ERR("Unsupported conversion type %#x.\n", conversion_type);
4770 return WINED3D_OK;
4773 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4775 /* Flip the surface contents */
4776 /* Flip the DC */
4778 HDC tmp;
4779 tmp = front->hDC;
4780 front->hDC = back->hDC;
4781 back->hDC = tmp;
4784 /* Flip the DIBsection */
4786 HBITMAP tmp = front->dib.DIBsection;
4787 front->dib.DIBsection = back->dib.DIBsection;
4788 back->dib.DIBsection = tmp;
4791 /* Flip the surface data */
4793 void* tmp;
4795 tmp = front->dib.bitmap_data;
4796 front->dib.bitmap_data = back->dib.bitmap_data;
4797 back->dib.bitmap_data = tmp;
4799 tmp = front->resource.allocatedMemory;
4800 front->resource.allocatedMemory = back->resource.allocatedMemory;
4801 back->resource.allocatedMemory = tmp;
4803 tmp = front->resource.heapMemory;
4804 front->resource.heapMemory = back->resource.heapMemory;
4805 back->resource.heapMemory = tmp;
4808 /* Flip the PBO */
4810 GLuint tmp_pbo = front->pbo;
4811 front->pbo = back->pbo;
4812 back->pbo = tmp_pbo;
4815 /* Flip the opengl texture */
4817 GLuint tmp;
4819 tmp = back->texture_name;
4820 back->texture_name = front->texture_name;
4821 front->texture_name = tmp;
4823 tmp = back->texture_name_srgb;
4824 back->texture_name_srgb = front->texture_name_srgb;
4825 front->texture_name_srgb = tmp;
4827 tmp = back->rb_multisample;
4828 back->rb_multisample = front->rb_multisample;
4829 front->rb_multisample = tmp;
4831 tmp = back->rb_resolved;
4832 back->rb_resolved = front->rb_resolved;
4833 front->rb_resolved = tmp;
4835 resource_unload(&back->resource);
4836 resource_unload(&front->resource);
4840 DWORD tmp_flags = back->flags;
4841 back->flags = front->flags;
4842 front->flags = tmp_flags;
4846 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4847 * pixel copy calls. */
4848 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4849 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4851 struct wined3d_device *device = dst_surface->resource.device;
4852 float xrel, yrel;
4853 UINT row;
4854 struct wined3d_context *context;
4855 BOOL upsidedown = FALSE;
4856 RECT dst_rect = *dst_rect_in;
4858 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4859 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4861 if(dst_rect.top > dst_rect.bottom) {
4862 UINT tmp = dst_rect.bottom;
4863 dst_rect.bottom = dst_rect.top;
4864 dst_rect.top = tmp;
4865 upsidedown = TRUE;
4868 context = context_acquire(device, src_surface);
4869 context_apply_blit_state(context, device);
4870 surface_internal_preload(dst_surface, SRGB_RGB);
4871 ENTER_GL();
4873 /* Bind the target texture */
4874 context_bind_texture(context, dst_surface->texture_target, dst_surface->texture_name);
4875 if (surface_is_offscreen(src_surface))
4877 TRACE("Reading from an offscreen target\n");
4878 upsidedown = !upsidedown;
4879 glReadBuffer(device->offscreenBuffer);
4881 else
4883 glReadBuffer(surface_get_gl_buffer(src_surface));
4885 checkGLcall("glReadBuffer");
4887 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4888 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4890 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4892 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4894 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4895 ERR("Texture filtering not supported in direct blit.\n");
4897 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4898 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4900 ERR("Texture filtering not supported in direct blit\n");
4903 if (upsidedown
4904 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4905 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4907 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4909 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4910 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4911 src_rect->left, src_surface->resource.height - src_rect->bottom,
4912 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4914 else
4916 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4917 /* I have to process this row by row to swap the image,
4918 * otherwise it would be upside down, so stretching in y direction
4919 * doesn't cost extra time
4921 * However, stretching in x direction can be avoided if not necessary
4923 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4924 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4926 /* Well, that stuff works, but it's very slow.
4927 * find a better way instead
4929 UINT col;
4931 for (col = dst_rect.left; col < dst_rect.right; ++col)
4933 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4934 dst_rect.left + col /* x offset */, row /* y offset */,
4935 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4938 else
4940 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4941 dst_rect.left /* x offset */, row /* y offset */,
4942 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4946 checkGLcall("glCopyTexSubImage2D");
4948 LEAVE_GL();
4949 context_release(context);
4951 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4952 * path is never entered
4954 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4957 /* Uses the hardware to stretch and flip the image */
4958 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4959 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4961 struct wined3d_device *device = dst_surface->resource.device;
4962 struct wined3d_swapchain *src_swapchain = NULL;
4963 GLuint src, backup = 0;
4964 float left, right, top, bottom; /* Texture coordinates */
4965 UINT fbwidth = src_surface->resource.width;
4966 UINT fbheight = src_surface->resource.height;
4967 struct wined3d_context *context;
4968 GLenum drawBuffer = GL_BACK;
4969 GLenum texture_target;
4970 BOOL noBackBufferBackup;
4971 BOOL src_offscreen;
4972 BOOL upsidedown = FALSE;
4973 RECT dst_rect = *dst_rect_in;
4975 TRACE("Using hwstretch blit\n");
4976 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4977 context = context_acquire(device, src_surface);
4978 context_apply_blit_state(context, device);
4979 surface_internal_preload(dst_surface, SRGB_RGB);
4981 src_offscreen = surface_is_offscreen(src_surface);
4982 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4983 if (!noBackBufferBackup && !src_surface->texture_name)
4985 /* Get it a description */
4986 surface_internal_preload(src_surface, SRGB_RGB);
4988 ENTER_GL();
4990 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4991 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4993 if (context->aux_buffers >= 2)
4995 /* Got more than one aux buffer? Use the 2nd aux buffer */
4996 drawBuffer = GL_AUX1;
4998 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
5000 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
5001 drawBuffer = GL_AUX0;
5004 if(noBackBufferBackup) {
5005 glGenTextures(1, &backup);
5006 checkGLcall("glGenTextures");
5007 context_bind_texture(context, GL_TEXTURE_2D, backup);
5008 texture_target = GL_TEXTURE_2D;
5009 } else {
5010 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
5011 * we are reading from the back buffer, the backup can be used as source texture
5013 texture_target = src_surface->texture_target;
5014 context_bind_texture(context, texture_target, src_surface->texture_name);
5015 glEnable(texture_target);
5016 checkGLcall("glEnable(texture_target)");
5018 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
5019 src_surface->flags &= ~SFLAG_INTEXTURE;
5022 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
5023 * glCopyTexSubImage is a bit picky about the parameters we pass to it
5025 if(dst_rect.top > dst_rect.bottom) {
5026 UINT tmp = dst_rect.bottom;
5027 dst_rect.bottom = dst_rect.top;
5028 dst_rect.top = tmp;
5029 upsidedown = TRUE;
5032 if (src_offscreen)
5034 TRACE("Reading from an offscreen target\n");
5035 upsidedown = !upsidedown;
5036 glReadBuffer(device->offscreenBuffer);
5038 else
5040 glReadBuffer(surface_get_gl_buffer(src_surface));
5043 /* TODO: Only back up the part that will be overwritten */
5044 glCopyTexSubImage2D(texture_target, 0,
5045 0, 0 /* read offsets */,
5046 0, 0,
5047 fbwidth,
5048 fbheight);
5050 checkGLcall("glCopyTexSubImage2D");
5052 /* No issue with overriding these - the sampler is dirty due to blit usage */
5053 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
5054 wined3d_gl_mag_filter(magLookup, filter));
5055 checkGLcall("glTexParameteri");
5056 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5057 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
5058 checkGLcall("glTexParameteri");
5060 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5061 src_swapchain = src_surface->container.u.swapchain;
5062 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5064 src = backup ? backup : src_surface->texture_name;
5066 else
5068 glReadBuffer(GL_FRONT);
5069 checkGLcall("glReadBuffer(GL_FRONT)");
5071 glGenTextures(1, &src);
5072 checkGLcall("glGenTextures(1, &src)");
5073 context_bind_texture(context, GL_TEXTURE_2D, src);
5075 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5076 * out for power of 2 sizes
5078 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5079 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5080 checkGLcall("glTexImage2D");
5081 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5082 0, 0 /* read offsets */,
5083 0, 0,
5084 fbwidth,
5085 fbheight);
5087 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5088 checkGLcall("glTexParameteri");
5089 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5090 checkGLcall("glTexParameteri");
5092 glReadBuffer(GL_BACK);
5093 checkGLcall("glReadBuffer(GL_BACK)");
5095 if(texture_target != GL_TEXTURE_2D) {
5096 glDisable(texture_target);
5097 glEnable(GL_TEXTURE_2D);
5098 texture_target = GL_TEXTURE_2D;
5101 checkGLcall("glEnd and previous");
5103 left = src_rect->left;
5104 right = src_rect->right;
5106 if (!upsidedown)
5108 top = src_surface->resource.height - src_rect->top;
5109 bottom = src_surface->resource.height - src_rect->bottom;
5111 else
5113 top = src_surface->resource.height - src_rect->bottom;
5114 bottom = src_surface->resource.height - src_rect->top;
5117 if (src_surface->flags & SFLAG_NORMCOORD)
5119 left /= src_surface->pow2Width;
5120 right /= src_surface->pow2Width;
5121 top /= src_surface->pow2Height;
5122 bottom /= src_surface->pow2Height;
5125 /* draw the source texture stretched and upside down. The correct surface is bound already */
5126 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5127 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5129 context_set_draw_buffer(context, drawBuffer);
5130 glReadBuffer(drawBuffer);
5132 glBegin(GL_QUADS);
5133 /* bottom left */
5134 glTexCoord2f(left, bottom);
5135 glVertex2i(0, 0);
5137 /* top left */
5138 glTexCoord2f(left, top);
5139 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5141 /* top right */
5142 glTexCoord2f(right, top);
5143 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5145 /* bottom right */
5146 glTexCoord2f(right, bottom);
5147 glVertex2i(dst_rect.right - dst_rect.left, 0);
5148 glEnd();
5149 checkGLcall("glEnd and previous");
5151 if (texture_target != dst_surface->texture_target)
5153 glDisable(texture_target);
5154 glEnable(dst_surface->texture_target);
5155 texture_target = dst_surface->texture_target;
5158 /* Now read the stretched and upside down image into the destination texture */
5159 context_bind_texture(context, texture_target, dst_surface->texture_name);
5160 glCopyTexSubImage2D(texture_target,
5162 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5163 0, 0, /* We blitted the image to the origin */
5164 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5165 checkGLcall("glCopyTexSubImage2D");
5167 if(drawBuffer == GL_BACK) {
5168 /* Write the back buffer backup back */
5169 if(backup) {
5170 if(texture_target != GL_TEXTURE_2D) {
5171 glDisable(texture_target);
5172 glEnable(GL_TEXTURE_2D);
5173 texture_target = GL_TEXTURE_2D;
5175 context_bind_texture(context, GL_TEXTURE_2D, backup);
5177 else
5179 if (texture_target != src_surface->texture_target)
5181 glDisable(texture_target);
5182 glEnable(src_surface->texture_target);
5183 texture_target = src_surface->texture_target;
5185 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5188 glBegin(GL_QUADS);
5189 /* top left */
5190 glTexCoord2f(0.0f, 0.0f);
5191 glVertex2i(0, fbheight);
5193 /* bottom left */
5194 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5195 glVertex2i(0, 0);
5197 /* bottom right */
5198 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5199 (float)fbheight / (float)src_surface->pow2Height);
5200 glVertex2i(fbwidth, 0);
5202 /* top right */
5203 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5204 glVertex2i(fbwidth, fbheight);
5205 glEnd();
5207 glDisable(texture_target);
5208 checkGLcall("glDisable(texture_target)");
5210 /* Cleanup */
5211 if (src != src_surface->texture_name && src != backup)
5213 glDeleteTextures(1, &src);
5214 checkGLcall("glDeleteTextures(1, &src)");
5216 if(backup) {
5217 glDeleteTextures(1, &backup);
5218 checkGLcall("glDeleteTextures(1, &backup)");
5221 LEAVE_GL();
5223 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5225 context_release(context);
5227 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5228 * path is never entered
5230 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5233 /* Front buffer coordinates are always full screen coordinates, but our GL
5234 * drawable is limited to the window's client area. The sysmem and texture
5235 * copies do have the full screen size. Note that GL has a bottom-left
5236 * origin, while D3D has a top-left origin. */
5237 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5239 UINT drawable_height;
5241 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5242 && surface == surface->container.u.swapchain->front_buffer)
5244 POINT offset = {0, 0};
5245 RECT windowsize;
5247 ScreenToClient(window, &offset);
5248 OffsetRect(rect, offset.x, offset.y);
5250 GetClientRect(window, &windowsize);
5251 drawable_height = windowsize.bottom - windowsize.top;
5253 else
5255 drawable_height = surface->resource.height;
5258 rect->top = drawable_height - rect->top;
5259 rect->bottom = drawable_height - rect->bottom;
5262 static void surface_blt_to_drawable(const struct wined3d_device *device,
5263 enum wined3d_texture_filter_type filter, BOOL color_key,
5264 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5265 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5267 struct wined3d_context *context;
5268 RECT src_rect, dst_rect;
5270 src_rect = *src_rect_in;
5271 dst_rect = *dst_rect_in;
5273 /* Make sure the surface is up-to-date. This should probably use
5274 * surface_load_location() and worry about the destination surface too,
5275 * unless we're overwriting it completely. */
5276 surface_internal_preload(src_surface, SRGB_RGB);
5278 /* Activate the destination context, set it up for blitting */
5279 context = context_acquire(device, dst_surface);
5280 context_apply_blit_state(context, device);
5282 if (!surface_is_offscreen(dst_surface))
5283 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5285 device->blitter->set_shader(device->blit_priv, context, src_surface);
5287 ENTER_GL();
5289 if (color_key)
5291 glEnable(GL_ALPHA_TEST);
5292 checkGLcall("glEnable(GL_ALPHA_TEST)");
5294 /* When the primary render target uses P8, the alpha component
5295 * contains the palette index. Which means that the colorkey is one of
5296 * the palette entries. In other cases pixels that should be masked
5297 * away have alpha set to 0. */
5298 if (primary_render_target_is_p8(device))
5299 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->src_blt_color_key.color_space_low_value / 256.0f);
5300 else
5301 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5302 checkGLcall("glAlphaFunc");
5304 else
5306 glDisable(GL_ALPHA_TEST);
5307 checkGLcall("glDisable(GL_ALPHA_TEST)");
5310 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5312 if (color_key)
5314 glDisable(GL_ALPHA_TEST);
5315 checkGLcall("glDisable(GL_ALPHA_TEST)");
5318 LEAVE_GL();
5320 /* Leave the opengl state valid for blitting */
5321 device->blitter->unset_shader(context->gl_info);
5323 if (wined3d_settings.strict_draw_ordering
5324 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5325 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5326 wglFlush(); /* Flush to ensure ordering across contexts. */
5328 context_release(context);
5331 /* Do not call while under the GL lock. */
5332 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
5334 struct wined3d_device *device = s->resource.device;
5335 const struct blit_shader *blitter;
5337 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5338 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5339 if (!blitter)
5341 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5342 return WINED3DERR_INVALIDCALL;
5345 return blitter->color_fill(device, s, rect, color);
5348 /* Do not call while under the GL lock. */
5349 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5350 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5351 enum wined3d_texture_filter_type filter)
5353 struct wined3d_device *device = dst_surface->resource.device;
5354 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5355 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5357 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5358 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5359 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
5361 /* Get the swapchain. One of the surfaces has to be a primary surface */
5362 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
5364 WARN("Destination is in sysmem, rejecting gl blt\n");
5365 return WINED3DERR_INVALIDCALL;
5368 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5369 dstSwapchain = dst_surface->container.u.swapchain;
5371 if (src_surface)
5373 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
5375 WARN("Src is in sysmem, rejecting gl blt\n");
5376 return WINED3DERR_INVALIDCALL;
5379 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5380 srcSwapchain = src_surface->container.u.swapchain;
5383 /* Early sort out of cases where no render target is used */
5384 if (!dstSwapchain && !srcSwapchain
5385 && src_surface != device->fb.render_targets[0]
5386 && dst_surface != device->fb.render_targets[0])
5388 TRACE("No surface is render target, not using hardware blit.\n");
5389 return WINED3DERR_INVALIDCALL;
5392 /* No destination color keying supported */
5393 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5395 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5396 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5397 return WINED3DERR_INVALIDCALL;
5400 if (dstSwapchain && dstSwapchain == srcSwapchain)
5402 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5403 return WINED3DERR_INVALIDCALL;
5406 if (dstSwapchain && srcSwapchain)
5408 FIXME("Implement hardware blit between two different swapchains\n");
5409 return WINED3DERR_INVALIDCALL;
5412 if (dstSwapchain)
5414 /* Handled with regular texture -> swapchain blit */
5415 if (src_surface == device->fb.render_targets[0])
5416 TRACE("Blit from active render target to a swapchain\n");
5418 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5420 FIXME("Implement blit from a swapchain to the active render target\n");
5421 return WINED3DERR_INVALIDCALL;
5424 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5426 /* Blit from render target to texture */
5427 BOOL stretchx;
5429 /* P8 read back is not implemented */
5430 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5431 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5433 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5434 return WINED3DERR_INVALIDCALL;
5437 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5439 TRACE("Color keying not supported by frame buffer to texture blit\n");
5440 return WINED3DERR_INVALIDCALL;
5441 /* Destination color key is checked above */
5444 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5445 stretchx = TRUE;
5446 else
5447 stretchx = FALSE;
5449 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5450 * flip the image nor scale it.
5452 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5453 * -> If the app wants a image width an unscaled width, copy it line per line
5454 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5455 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5456 * back buffer. This is slower than reading line per line, thus not used for flipping
5457 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5458 * pixel by pixel. */
5459 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5460 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5462 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
5463 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
5465 else
5467 TRACE("Using hardware stretching to flip / stretch the texture.\n");
5468 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
5471 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5473 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5474 dst_surface->resource.allocatedMemory = NULL;
5475 dst_surface->resource.heapMemory = NULL;
5477 else
5479 dst_surface->flags &= ~SFLAG_INSYSMEM;
5482 return WINED3D_OK;
5484 else if (src_surface)
5486 /* Blit from offscreen surface to render target */
5487 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
5488 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5490 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5492 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5493 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5494 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5496 FIXME("Unsupported blit operation falling back to software\n");
5497 return WINED3DERR_INVALIDCALL;
5500 /* Color keying: Check if we have to do a color keyed blt,
5501 * and if not check if a color key is activated.
5503 * Just modify the color keying parameters in the surface and restore them afterwards
5504 * The surface keeps track of the color key last used to load the opengl surface.
5505 * PreLoad will catch the change to the flags and color key and reload if necessary.
5507 if (flags & WINEDDBLT_KEYSRC)
5509 /* Use color key from surface */
5511 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5513 /* Use color key from DDBltFx */
5514 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5515 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
5517 else
5519 /* Do not use color key */
5520 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5523 surface_blt_to_drawable(device, filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5524 src_surface, src_rect, dst_surface, dst_rect);
5526 /* Restore the color key parameters */
5527 src_surface->CKeyFlags = oldCKeyFlags;
5528 src_surface->src_blt_color_key = old_blt_key;
5530 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5532 return WINED3D_OK;
5535 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5536 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5537 return WINED3DERR_INVALIDCALL;
5540 /* GL locking is done by the caller */
5541 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5542 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5544 struct wined3d_device *device = surface->resource.device;
5545 const struct wined3d_gl_info *gl_info = context->gl_info;
5546 GLint compare_mode = GL_NONE;
5547 struct blt_info info;
5548 GLint old_binding = 0;
5549 RECT rect;
5551 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5553 glDisable(GL_CULL_FACE);
5554 glDisable(GL_BLEND);
5555 glDisable(GL_ALPHA_TEST);
5556 glDisable(GL_SCISSOR_TEST);
5557 glDisable(GL_STENCIL_TEST);
5558 glEnable(GL_DEPTH_TEST);
5559 glDepthFunc(GL_ALWAYS);
5560 glDepthMask(GL_TRUE);
5561 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5562 glViewport(x, y, w, h);
5564 SetRect(&rect, 0, h, w, 0);
5565 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5566 context_active_texture(context, context->gl_info, 0);
5567 glGetIntegerv(info.binding, &old_binding);
5568 glBindTexture(info.bind_target, texture);
5569 if (gl_info->supported[ARB_SHADOW])
5571 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5572 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5575 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5576 gl_info, info.tex_type, &surface->ds_current_size);
5578 glBegin(GL_TRIANGLE_STRIP);
5579 glTexCoord3fv(info.coords[0]);
5580 glVertex2f(-1.0f, -1.0f);
5581 glTexCoord3fv(info.coords[1]);
5582 glVertex2f(1.0f, -1.0f);
5583 glTexCoord3fv(info.coords[2]);
5584 glVertex2f(-1.0f, 1.0f);
5585 glTexCoord3fv(info.coords[3]);
5586 glVertex2f(1.0f, 1.0f);
5587 glEnd();
5589 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5590 glBindTexture(info.bind_target, old_binding);
5592 glPopAttrib();
5594 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5597 void surface_modify_ds_location(struct wined3d_surface *surface,
5598 DWORD location, UINT w, UINT h)
5600 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5602 if (location & ~(SFLAG_LOCATIONS | SFLAG_DISCARDED))
5603 FIXME("Invalid location (%#x) specified.\n", location);
5605 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5606 || (!(surface->flags & SFLAG_INTEXTURE) && (location & SFLAG_INTEXTURE)))
5608 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5610 TRACE("Passing to container.\n");
5611 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5615 surface->ds_current_size.cx = w;
5616 surface->ds_current_size.cy = h;
5617 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_DISCARDED);
5618 surface->flags |= location;
5621 /* Context activation is done by the caller. */
5622 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5624 struct wined3d_device *device = surface->resource.device;
5625 GLsizei w, h;
5627 TRACE("surface %p, new location %#x.\n", surface, location);
5629 /* TODO: Make this work for modes other than FBO */
5630 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5632 if (!(surface->flags & location))
5634 w = surface->ds_current_size.cx;
5635 h = surface->ds_current_size.cy;
5636 surface->ds_current_size.cx = 0;
5637 surface->ds_current_size.cy = 0;
5639 else
5641 w = surface->resource.width;
5642 h = surface->resource.height;
5645 if (surface->ds_current_size.cx == surface->resource.width
5646 && surface->ds_current_size.cy == surface->resource.height)
5648 TRACE("Location (%#x) is already up to date.\n", location);
5649 return;
5652 if (surface->current_renderbuffer)
5654 FIXME("Not supported with fixed up depth stencil.\n");
5655 return;
5658 if (surface->flags & SFLAG_DISCARDED)
5660 TRACE("Surface was discarded, no need copy data.\n");
5661 switch (location)
5663 case SFLAG_INTEXTURE:
5664 surface_prepare_texture(surface, context, FALSE);
5665 break;
5666 case SFLAG_INRB_MULTISAMPLE:
5667 surface_prepare_rb(surface, context->gl_info, TRUE);
5668 break;
5669 case SFLAG_INDRAWABLE:
5670 /* Nothing to do */
5671 break;
5672 default:
5673 FIXME("Unhandled location %#x\n", location);
5675 surface->flags &= ~SFLAG_DISCARDED;
5676 surface->flags |= location;
5677 surface->ds_current_size.cx = surface->resource.width;
5678 surface->ds_current_size.cy = surface->resource.height;
5679 return;
5682 if (!(surface->flags & SFLAG_LOCATIONS))
5684 FIXME("No up to date depth stencil location.\n");
5685 surface->flags |= location;
5686 surface->ds_current_size.cx = surface->resource.width;
5687 surface->ds_current_size.cy = surface->resource.height;
5688 return;
5691 if (location == SFLAG_INTEXTURE)
5693 GLint old_binding = 0;
5694 GLenum bind_target;
5696 /* The render target is allowed to be smaller than the depth/stencil
5697 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5698 * than the offscreen surface. Don't overwrite the offscreen surface
5699 * with undefined data. */
5700 w = min(w, context->swapchain->desc.backbuffer_width);
5701 h = min(h, context->swapchain->desc.backbuffer_height);
5703 TRACE("Copying onscreen depth buffer to depth texture.\n");
5705 ENTER_GL();
5707 if (!device->depth_blt_texture)
5709 glGenTextures(1, &device->depth_blt_texture);
5712 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5713 * directly on the FBO texture. That's because we need to flip. */
5714 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5715 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5716 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5718 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5719 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5721 else
5723 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5724 bind_target = GL_TEXTURE_2D;
5726 glBindTexture(bind_target, device->depth_blt_texture);
5727 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5728 * internal format, because the internal format might include stencil
5729 * data. In principle we should copy stencil data as well, but unless
5730 * the driver supports stencil export it's hard to do, and doesn't
5731 * seem to be needed in practice. If the hardware doesn't support
5732 * writing stencil data, the glCopyTexImage2D() call might trigger
5733 * software fallbacks. */
5734 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5735 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5736 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5737 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5738 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5739 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5740 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5741 glBindTexture(bind_target, old_binding);
5743 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5744 NULL, surface, SFLAG_INTEXTURE);
5745 context_set_draw_buffer(context, GL_NONE);
5746 glReadBuffer(GL_NONE);
5748 /* Do the actual blit */
5749 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5750 checkGLcall("depth_blt");
5752 context_invalidate_state(context, STATE_FRAMEBUFFER);
5754 LEAVE_GL();
5756 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5758 else if (location == SFLAG_INDRAWABLE)
5760 TRACE("Copying depth texture to onscreen depth buffer.\n");
5762 ENTER_GL();
5764 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5765 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5766 surface_depth_blt(surface, context, surface->texture_name,
5767 0, surface->pow2Height - h, w, h, surface->texture_target);
5768 checkGLcall("depth_blt");
5770 context_invalidate_state(context, STATE_FRAMEBUFFER);
5772 LEAVE_GL();
5774 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5776 else
5778 ERR("Invalid location (%#x) specified.\n", location);
5781 surface->flags |= location;
5782 surface->ds_current_size.cx = surface->resource.width;
5783 surface->ds_current_size.cy = surface->resource.height;
5786 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5788 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5789 struct wined3d_surface *overlay;
5791 TRACE("surface %p, location %s, persistent %#x.\n",
5792 surface, debug_surflocation(location), persistent);
5794 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5795 && !(surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5796 && (location & SFLAG_INDRAWABLE))
5797 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5799 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5800 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5801 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5803 if (persistent)
5805 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5806 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5808 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5810 TRACE("Passing to container.\n");
5811 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5814 surface->flags &= ~SFLAG_LOCATIONS;
5815 surface->flags |= location;
5817 /* Redraw emulated overlays, if any */
5818 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5820 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5822 surface_draw_overlay(overlay);
5826 else
5828 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5830 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5832 TRACE("Passing to container\n");
5833 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5836 surface->flags &= ~location;
5839 if (!(surface->flags & SFLAG_LOCATIONS))
5841 ERR("Surface %p does not have any up to date location.\n", surface);
5845 static DWORD resource_access_from_location(DWORD location)
5847 switch (location)
5849 case SFLAG_INSYSMEM:
5850 return WINED3D_RESOURCE_ACCESS_CPU;
5852 case SFLAG_INDRAWABLE:
5853 case SFLAG_INSRGBTEX:
5854 case SFLAG_INTEXTURE:
5855 case SFLAG_INRB_MULTISAMPLE:
5856 case SFLAG_INRB_RESOLVED:
5857 return WINED3D_RESOURCE_ACCESS_GPU;
5859 default:
5860 FIXME("Unhandled location %#x.\n", location);
5861 return 0;
5865 static void surface_load_sysmem(struct wined3d_surface *surface,
5866 const struct wined3d_gl_info *gl_info, const RECT *rect)
5868 surface_prepare_system_memory(surface);
5870 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5871 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5873 /* Download the surface to system memory. */
5874 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5876 struct wined3d_device *device = surface->resource.device;
5877 struct wined3d_context *context;
5879 /* TODO: Use already acquired context when possible. */
5880 context = context_acquire(device, NULL);
5882 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5883 surface_download_data(surface, gl_info);
5885 context_release(context);
5887 return;
5890 if (surface->flags & SFLAG_INDRAWABLE)
5892 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5893 wined3d_surface_get_pitch(surface));
5894 return;
5897 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5898 surface, surface->flags & SFLAG_LOCATIONS);
5901 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5902 const struct wined3d_gl_info *gl_info, const RECT *rect)
5904 struct wined3d_device *device = surface->resource.device;
5905 enum wined3d_conversion_type convert;
5906 struct wined3d_format format;
5907 UINT byte_count;
5908 BYTE *mem;
5910 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5912 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5913 return WINED3DERR_INVALIDCALL;
5916 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5917 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5919 if (surface->flags & SFLAG_INTEXTURE)
5921 RECT r;
5923 surface_get_rect(surface, rect, &r);
5924 surface_blt_to_drawable(device, WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
5926 return WINED3D_OK;
5929 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5931 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5932 * path through sysmem. */
5933 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5936 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5938 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5939 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5940 * called. */
5941 if ((convert != WINED3D_CT_NONE) && (surface->flags & SFLAG_PBO))
5943 struct wined3d_context *context;
5945 TRACE("Removing the pbo attached to surface %p.\n", surface);
5947 /* TODO: Use already acquired context when possible. */
5948 context = context_acquire(device, NULL);
5950 surface_remove_pbo(surface, gl_info);
5952 context_release(context);
5955 if ((convert != WINED3D_CT_NONE) && surface->resource.allocatedMemory)
5957 UINT height = surface->resource.height;
5958 UINT width = surface->resource.width;
5959 UINT src_pitch, dst_pitch;
5961 byte_count = format.conv_byte_count;
5962 src_pitch = wined3d_surface_get_pitch(surface);
5964 /* Stick to the alignment for the converted surface too, makes it
5965 * easier to load the surface. */
5966 dst_pitch = width * byte_count;
5967 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5969 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5971 ERR("Out of memory (%u).\n", dst_pitch * height);
5972 return E_OUTOFMEMORY;
5975 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5976 src_pitch, width, height, dst_pitch, convert, surface);
5978 surface->flags |= SFLAG_CONVERTED;
5980 else
5982 surface->flags &= ~SFLAG_CONVERTED;
5983 mem = surface->resource.allocatedMemory;
5984 byte_count = format.byte_count;
5987 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5989 /* Don't delete PBO memory. */
5990 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5991 HeapFree(GetProcessHeap(), 0, mem);
5993 return WINED3D_OK;
5996 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5997 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5999 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
6000 struct wined3d_device *device = surface->resource.device;
6001 enum wined3d_conversion_type convert;
6002 struct wined3d_context *context;
6003 UINT width, src_pitch, dst_pitch;
6004 struct wined3d_bo_address data;
6005 struct wined3d_format format;
6006 POINT dst_point = {0, 0};
6007 BYTE *mem;
6009 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
6010 && surface_is_offscreen(surface)
6011 && (surface->flags & SFLAG_INDRAWABLE))
6013 surface_load_fb_texture(surface, srgb);
6015 return WINED3D_OK;
6018 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6019 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
6020 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6021 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6022 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6024 if (srgb)
6025 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INTEXTURE,
6026 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6027 else
6028 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INSRGBTEX,
6029 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6031 return WINED3D_OK;
6034 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
6035 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
6036 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6037 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6038 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6040 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
6041 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
6042 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6044 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
6045 &rect, surface, dst_location, &rect);
6047 return WINED3D_OK;
6050 /* Upload from system memory */
6052 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6053 TRUE /* We will use textures */, &format, &convert);
6055 if (srgb)
6057 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6059 /* Performance warning... */
6060 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6061 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6064 else
6066 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6068 /* Performance warning... */
6069 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6070 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6074 if (!(surface->flags & SFLAG_INSYSMEM))
6076 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6077 /* Lets hope we get it from somewhere... */
6078 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6081 /* TODO: Use already acquired context when possible. */
6082 context = context_acquire(device, NULL);
6084 surface_prepare_texture(surface, context, srgb);
6085 surface_bind_and_dirtify(surface, context, srgb);
6087 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6089 surface->flags |= SFLAG_GLCKEY;
6090 surface->gl_color_key = surface->src_blt_color_key;
6092 else surface->flags &= ~SFLAG_GLCKEY;
6094 width = surface->resource.width;
6095 src_pitch = wined3d_surface_get_pitch(surface);
6097 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6098 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6099 * called. */
6100 if ((convert != WINED3D_CT_NONE || format.convert) && (surface->flags & SFLAG_PBO))
6102 TRACE("Removing the pbo attached to surface %p.\n", surface);
6103 surface_remove_pbo(surface, gl_info);
6106 if (format.convert)
6108 /* This code is entered for texture formats which need a fixup. */
6109 UINT height = surface->resource.height;
6111 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6112 dst_pitch = width * format.conv_byte_count;
6113 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6115 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6117 ERR("Out of memory (%u).\n", dst_pitch * height);
6118 context_release(context);
6119 return E_OUTOFMEMORY;
6121 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6122 format.byte_count = format.conv_byte_count;
6123 src_pitch = dst_pitch;
6125 else if (convert != WINED3D_CT_NONE && surface->resource.allocatedMemory)
6127 /* This code is only entered for color keying fixups */
6128 UINT height = surface->resource.height;
6130 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6131 dst_pitch = width * format.conv_byte_count;
6132 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6134 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6136 ERR("Out of memory (%u).\n", dst_pitch * height);
6137 context_release(context);
6138 return E_OUTOFMEMORY;
6140 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6141 width, height, dst_pitch, convert, surface);
6142 format.byte_count = format.conv_byte_count;
6143 src_pitch = dst_pitch;
6145 else
6147 mem = surface->resource.allocatedMemory;
6150 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6151 data.addr = mem;
6152 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
6154 context_release(context);
6156 /* Don't delete PBO memory. */
6157 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6158 HeapFree(GetProcessHeap(), 0, mem);
6160 return WINED3D_OK;
6163 static void surface_multisample_resolve(struct wined3d_surface *surface)
6165 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6167 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6168 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6170 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
6171 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6174 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6176 struct wined3d_device *device = surface->resource.device;
6177 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6178 HRESULT hr;
6180 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6182 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6184 if (location == SFLAG_INTEXTURE)
6186 struct wined3d_context *context = context_acquire(device, NULL);
6187 surface_load_ds_location(surface, context, location);
6188 context_release(context);
6189 return WINED3D_OK;
6191 else
6193 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6194 return WINED3DERR_INVALIDCALL;
6198 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6199 location = SFLAG_INTEXTURE;
6201 if (surface->flags & location)
6203 TRACE("Location already up to date.\n");
6205 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
6206 && surface_need_pbo(surface, gl_info))
6207 surface_load_pbo(surface, gl_info);
6209 return WINED3D_OK;
6212 if (WARN_ON(d3d_surface))
6214 DWORD required_access = resource_access_from_location(location);
6215 if ((surface->resource.access_flags & required_access) != required_access)
6216 WARN("Operation requires %#x access, but surface only has %#x.\n",
6217 required_access, surface->resource.access_flags);
6220 if (!(surface->flags & SFLAG_LOCATIONS))
6222 ERR("Surface %p does not have any up to date location.\n", surface);
6223 surface->flags |= SFLAG_LOST;
6224 return WINED3DERR_DEVICELOST;
6227 switch (location)
6229 case SFLAG_INSYSMEM:
6230 surface_load_sysmem(surface, gl_info, rect);
6231 break;
6233 case SFLAG_INDRAWABLE:
6234 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6235 return hr;
6236 break;
6238 case SFLAG_INRB_RESOLVED:
6239 surface_multisample_resolve(surface);
6240 break;
6242 case SFLAG_INTEXTURE:
6243 case SFLAG_INSRGBTEX:
6244 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6245 return hr;
6246 break;
6248 default:
6249 ERR("Don't know how to handle location %#x.\n", location);
6250 break;
6253 if (!rect)
6255 surface->flags |= location;
6257 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6258 surface_evict_sysmem(surface);
6261 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6262 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6264 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6267 return WINED3D_OK;
6270 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6272 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6274 /* Not on a swapchain - must be offscreen */
6275 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6277 /* The front buffer is always onscreen */
6278 if (surface == swapchain->front_buffer) return FALSE;
6280 /* If the swapchain is rendered to an FBO, the backbuffer is
6281 * offscreen, otherwise onscreen */
6282 return swapchain->render_to_fbo;
6285 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6286 /* Context activation is done by the caller. */
6287 static void ffp_blit_free(struct wined3d_device *device) { }
6289 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6290 /* Context activation is done by the caller. */
6291 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6293 BYTE table[256][4];
6294 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6296 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6298 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6299 ENTER_GL();
6300 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6301 LEAVE_GL();
6304 /* Context activation is done by the caller. */
6305 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6307 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6309 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6310 * else the surface is converted in software at upload time in LoadLocation.
6312 if (!(surface->flags & SFLAG_CONVERTED) && fixup == COMPLEX_FIXUP_P8
6313 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6314 ffp_blit_p8_upload_palette(surface, context->gl_info);
6316 ENTER_GL();
6317 glEnable(surface->texture_target);
6318 checkGLcall("glEnable(surface->texture_target)");
6319 LEAVE_GL();
6320 return WINED3D_OK;
6323 /* Context activation is done by the caller. */
6324 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6326 ENTER_GL();
6327 glDisable(GL_TEXTURE_2D);
6328 checkGLcall("glDisable(GL_TEXTURE_2D)");
6329 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6331 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6332 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6334 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6336 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6337 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6339 LEAVE_GL();
6342 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6343 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
6344 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
6346 enum complex_fixup src_fixup;
6348 switch (blit_op)
6350 case WINED3D_BLIT_OP_COLOR_BLIT:
6351 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
6352 return FALSE;
6354 src_fixup = get_complex_fixup(src_format->color_fixup);
6355 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6357 TRACE("Checking support for fixup:\n");
6358 dump_color_fixup_desc(src_format->color_fixup);
6361 if (!is_identity_fixup(dst_format->color_fixup))
6363 TRACE("Destination fixups are not supported\n");
6364 return FALSE;
6367 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6369 TRACE("P8 fixup supported\n");
6370 return TRUE;
6373 /* We only support identity conversions. */
6374 if (is_identity_fixup(src_format->color_fixup))
6376 TRACE("[OK]\n");
6377 return TRUE;
6380 TRACE("[FAILED]\n");
6381 return FALSE;
6383 case WINED3D_BLIT_OP_COLOR_FILL:
6384 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
6385 return FALSE;
6387 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6389 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6390 return FALSE;
6392 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6394 TRACE("Color fill not supported\n");
6395 return FALSE;
6398 /* FIXME: We should reject color fills on formats with fixups,
6399 * but this would break P8 color fills for example. */
6401 return TRUE;
6403 case WINED3D_BLIT_OP_DEPTH_FILL:
6404 return TRUE;
6406 default:
6407 TRACE("Unsupported blit_op=%d\n", blit_op);
6408 return FALSE;
6412 /* Do not call while under the GL lock. */
6413 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6414 const RECT *dst_rect, const struct wined3d_color *color)
6416 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6417 struct wined3d_fb_state fb = {&dst_surface, NULL};
6419 return device_clear_render_targets(device, 1, &fb,
6420 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6423 /* Do not call while under the GL lock. */
6424 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6425 struct wined3d_surface *surface, const RECT *rect, float depth)
6427 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6428 struct wined3d_fb_state fb = {NULL, surface};
6430 return device_clear_render_targets(device, 0, &fb,
6431 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6434 const struct blit_shader ffp_blit = {
6435 ffp_blit_alloc,
6436 ffp_blit_free,
6437 ffp_blit_set,
6438 ffp_blit_unset,
6439 ffp_blit_supported,
6440 ffp_blit_color_fill,
6441 ffp_blit_depth_fill,
6444 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6446 return WINED3D_OK;
6449 /* Context activation is done by the caller. */
6450 static void cpu_blit_free(struct wined3d_device *device)
6454 /* Context activation is done by the caller. */
6455 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6457 return WINED3D_OK;
6460 /* Context activation is done by the caller. */
6461 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6465 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6466 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
6467 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
6469 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6471 return TRUE;
6474 return FALSE;
6477 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
6478 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
6479 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
6481 UINT row_block_count;
6482 const BYTE *src_row;
6483 BYTE *dst_row;
6484 UINT x, y;
6486 src_row = src_data;
6487 dst_row = dst_data;
6489 row_block_count = (update_w + format->block_width - 1) / format->block_width;
6491 if (!flags)
6493 for (y = 0; y < update_h; y += format->block_height)
6495 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
6496 src_row += src_pitch;
6497 dst_row += dst_pitch;
6500 return WINED3D_OK;
6503 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
6505 src_row += (((update_h / format->block_height) - 1) * src_pitch);
6507 switch (format->id)
6509 case WINED3DFMT_DXT1:
6510 for (y = 0; y < update_h; y += format->block_height)
6512 struct block
6514 WORD color[2];
6515 BYTE control_row[4];
6518 const struct block *s = (const struct block *)src_row;
6519 struct block *d = (struct block *)dst_row;
6521 for (x = 0; x < row_block_count; ++x)
6523 d[x].color[0] = s[x].color[0];
6524 d[x].color[1] = s[x].color[1];
6525 d[x].control_row[0] = s[x].control_row[3];
6526 d[x].control_row[1] = s[x].control_row[2];
6527 d[x].control_row[2] = s[x].control_row[1];
6528 d[x].control_row[3] = s[x].control_row[0];
6530 src_row -= src_pitch;
6531 dst_row += dst_pitch;
6533 return WINED3D_OK;
6535 case WINED3DFMT_DXT3:
6536 for (y = 0; y < update_h; y += format->block_height)
6538 struct block
6540 WORD alpha_row[4];
6541 WORD color[2];
6542 BYTE control_row[4];
6545 const struct block *s = (const struct block *)src_row;
6546 struct block *d = (struct block *)dst_row;
6548 for (x = 0; x < row_block_count; ++x)
6550 d[x].alpha_row[0] = s[x].alpha_row[3];
6551 d[x].alpha_row[1] = s[x].alpha_row[2];
6552 d[x].alpha_row[2] = s[x].alpha_row[1];
6553 d[x].alpha_row[3] = s[x].alpha_row[0];
6554 d[x].color[0] = s[x].color[0];
6555 d[x].color[1] = s[x].color[1];
6556 d[x].control_row[0] = s[x].control_row[3];
6557 d[x].control_row[1] = s[x].control_row[2];
6558 d[x].control_row[2] = s[x].control_row[1];
6559 d[x].control_row[3] = s[x].control_row[0];
6561 src_row -= src_pitch;
6562 dst_row += dst_pitch;
6564 return WINED3D_OK;
6566 default:
6567 FIXME("Compressed flip not implemented for format %s.\n",
6568 debug_d3dformat(format->id));
6569 return E_NOTIMPL;
6573 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
6574 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
6576 return E_NOTIMPL;
6579 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6580 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6581 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
6583 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6584 const struct wined3d_format *src_format, *dst_format;
6585 struct wined3d_surface *orig_src = src_surface;
6586 struct wined3d_mapped_rect dst_map, src_map;
6587 HRESULT hr = WINED3D_OK;
6588 const BYTE *sbuf;
6589 RECT xdst,xsrc;
6590 BYTE *dbuf;
6591 int x, y;
6593 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6594 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6595 flags, fx, debug_d3dtexturefiltertype(filter));
6597 xsrc = *src_rect;
6599 if (!src_surface)
6601 RECT full_rect;
6603 full_rect.left = 0;
6604 full_rect.top = 0;
6605 full_rect.right = dst_surface->resource.width;
6606 full_rect.bottom = dst_surface->resource.height;
6607 IntersectRect(&xdst, &full_rect, dst_rect);
6609 else
6611 BOOL clip_horiz, clip_vert;
6613 xdst = *dst_rect;
6614 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6615 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6617 if (clip_vert || clip_horiz)
6619 /* Now check if this is a special case or not... */
6620 if ((flags & WINEDDBLT_DDFX)
6621 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6622 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6624 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6625 return WINED3D_OK;
6628 if (clip_horiz)
6630 if (xdst.left < 0)
6632 xsrc.left -= xdst.left;
6633 xdst.left = 0;
6635 if (xdst.right > dst_surface->resource.width)
6637 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6638 xdst.right = (int)dst_surface->resource.width;
6642 if (clip_vert)
6644 if (xdst.top < 0)
6646 xsrc.top -= xdst.top;
6647 xdst.top = 0;
6649 if (xdst.bottom > dst_surface->resource.height)
6651 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6652 xdst.bottom = (int)dst_surface->resource.height;
6656 /* And check if after clipping something is still to be done... */
6657 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6658 || (xdst.left >= (int)dst_surface->resource.width)
6659 || (xdst.top >= (int)dst_surface->resource.height)
6660 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6661 || (xsrc.left >= (int)src_surface->resource.width)
6662 || (xsrc.top >= (int)src_surface->resource.height))
6664 TRACE("Nothing to be done after clipping.\n");
6665 return WINED3D_OK;
6670 if (src_surface == dst_surface)
6672 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6673 src_map = dst_map;
6674 src_format = dst_surface->resource.format;
6675 dst_format = src_format;
6677 else
6679 dst_format = dst_surface->resource.format;
6680 if (src_surface)
6682 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6684 src_surface = surface_convert_format(src_surface, dst_format->id);
6685 if (!src_surface)
6687 /* The conv function writes a FIXME */
6688 WARN("Cannot convert source surface format to dest format.\n");
6689 goto release;
6692 wined3d_surface_map(src_surface, &src_map, NULL, WINED3DLOCK_READONLY);
6693 src_format = src_surface->resource.format;
6695 else
6697 src_format = dst_format;
6699 if (dst_rect)
6700 wined3d_surface_map(dst_surface, &dst_map, &xdst, 0);
6701 else
6702 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6705 bpp = dst_surface->resource.format->byte_count;
6706 srcheight = xsrc.bottom - xsrc.top;
6707 srcwidth = xsrc.right - xsrc.left;
6708 dstheight = xdst.bottom - xdst.top;
6709 dstwidth = xdst.right - xdst.left;
6710 width = (xdst.right - xdst.left) * bpp;
6712 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
6714 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6716 if (src_surface == dst_surface)
6718 FIXME("Only plain blits supported on compressed surfaces.\n");
6719 hr = E_NOTIMPL;
6720 goto release;
6723 if (srcheight != dstheight || srcwidth != dstwidth)
6725 WARN("Stretching not supported on compressed surfaces.\n");
6726 hr = WINED3DERR_INVALIDCALL;
6727 goto release;
6730 if (srcwidth & (src_format->block_width - 1) || srcheight & (src_format->block_height - 1))
6732 WARN("Rectangle not block-aligned.\n");
6733 hr = WINED3DERR_INVALIDCALL;
6734 goto release;
6737 hr = surface_cpu_blt_compressed(src_map.data, dst_map.data,
6738 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
6739 src_format, flags, fx);
6740 goto release;
6743 if (dst_rect && src_surface != dst_surface)
6744 dbuf = dst_map.data;
6745 else
6746 dbuf = (BYTE *)dst_map.data + (xdst.top * dst_map.row_pitch) + (xdst.left * bpp);
6748 /* First, all the 'source-less' blits */
6749 if (flags & WINEDDBLT_COLORFILL)
6751 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
6752 flags &= ~WINEDDBLT_COLORFILL;
6755 if (flags & WINEDDBLT_DEPTHFILL)
6757 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6759 if (flags & WINEDDBLT_ROP)
6761 /* Catch some degenerate cases here. */
6762 switch (fx->dwROP)
6764 case BLACKNESS:
6765 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
6766 break;
6767 case 0xAA0029: /* No-op */
6768 break;
6769 case WHITENESS:
6770 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
6771 break;
6772 case SRCCOPY: /* Well, we do that below? */
6773 break;
6774 default:
6775 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6776 goto error;
6778 flags &= ~WINEDDBLT_ROP;
6780 if (flags & WINEDDBLT_DDROPS)
6782 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6784 /* Now the 'with source' blits. */
6785 if (src_surface)
6787 const BYTE *sbase;
6788 int sx, xinc, sy, yinc;
6790 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6791 goto release;
6793 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
6794 && (srcwidth != dstwidth || srcheight != dstheight))
6796 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6797 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6800 sbase = (BYTE *)src_map.data + (xsrc.top * src_map.row_pitch) + xsrc.left * bpp;
6801 xinc = (srcwidth << 16) / dstwidth;
6802 yinc = (srcheight << 16) / dstheight;
6804 if (!flags)
6806 /* No effects, we can cheat here. */
6807 if (dstwidth == srcwidth)
6809 if (dstheight == srcheight)
6811 /* No stretching in either direction. This needs to be as
6812 * fast as possible. */
6813 sbuf = sbase;
6815 /* Check for overlapping surfaces. */
6816 if (src_surface != dst_surface || xdst.top < xsrc.top
6817 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6819 /* No overlap, or dst above src, so copy from top downwards. */
6820 for (y = 0; y < dstheight; ++y)
6822 memcpy(dbuf, sbuf, width);
6823 sbuf += src_map.row_pitch;
6824 dbuf += dst_map.row_pitch;
6827 else if (xdst.top > xsrc.top)
6829 /* Copy from bottom upwards. */
6830 sbuf += src_map.row_pitch * dstheight;
6831 dbuf += dst_map.row_pitch * dstheight;
6832 for (y = 0; y < dstheight; ++y)
6834 sbuf -= src_map.row_pitch;
6835 dbuf -= dst_map.row_pitch;
6836 memcpy(dbuf, sbuf, width);
6839 else
6841 /* Src and dst overlapping on the same line, use memmove. */
6842 for (y = 0; y < dstheight; ++y)
6844 memmove(dbuf, sbuf, width);
6845 sbuf += src_map.row_pitch;
6846 dbuf += dst_map.row_pitch;
6850 else
6852 /* Stretching in y direction only. */
6853 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6855 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6856 memcpy(dbuf, sbuf, width);
6857 dbuf += dst_map.row_pitch;
6861 else
6863 /* Stretching in X direction. */
6864 int last_sy = -1;
6865 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6867 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6869 if ((sy >> 16) == (last_sy >> 16))
6871 /* This source row is the same as last source row -
6872 * Copy the already stretched row. */
6873 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
6875 else
6877 #define STRETCH_ROW(type) \
6878 do { \
6879 const type *s = (const type *)sbuf; \
6880 type *d = (type *)dbuf; \
6881 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6882 d[x] = s[sx >> 16]; \
6883 } while(0)
6885 switch(bpp)
6887 case 1:
6888 STRETCH_ROW(BYTE);
6889 break;
6890 case 2:
6891 STRETCH_ROW(WORD);
6892 break;
6893 case 4:
6894 STRETCH_ROW(DWORD);
6895 break;
6896 case 3:
6898 const BYTE *s;
6899 BYTE *d = dbuf;
6900 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6902 DWORD pixel;
6904 s = sbuf + 3 * (sx >> 16);
6905 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6906 d[0] = (pixel ) & 0xff;
6907 d[1] = (pixel >> 8) & 0xff;
6908 d[2] = (pixel >> 16) & 0xff;
6909 d += 3;
6911 break;
6913 default:
6914 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6915 hr = WINED3DERR_NOTAVAILABLE;
6916 goto error;
6918 #undef STRETCH_ROW
6920 dbuf += dst_map.row_pitch;
6921 last_sy = sy;
6925 else
6927 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
6928 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6929 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6930 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6932 /* The color keying flags are checked for correctness in ddraw */
6933 if (flags & WINEDDBLT_KEYSRC)
6935 keylow = src_surface->src_blt_color_key.color_space_low_value;
6936 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
6938 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6940 keylow = fx->ddckSrcColorkey.color_space_low_value;
6941 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
6944 if (flags & WINEDDBLT_KEYDEST)
6946 /* Destination color keys are taken from the source surface! */
6947 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
6948 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
6950 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6952 destkeylow = fx->ddckDestColorkey.color_space_low_value;
6953 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
6956 if (bpp == 1)
6958 keymask = 0xff;
6960 else
6962 keymask = src_format->red_mask
6963 | src_format->green_mask
6964 | src_format->blue_mask;
6966 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6969 if (flags & WINEDDBLT_DDFX)
6971 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6972 LONG tmpxy;
6973 dTopLeft = dbuf;
6974 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6975 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
6976 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6978 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6980 /* I don't think we need to do anything about this flag */
6981 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6983 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6985 tmp = dTopRight;
6986 dTopRight = dTopLeft;
6987 dTopLeft = tmp;
6988 tmp = dBottomRight;
6989 dBottomRight = dBottomLeft;
6990 dBottomLeft = tmp;
6991 dstxinc = dstxinc * -1;
6993 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6995 tmp = dTopLeft;
6996 dTopLeft = dBottomLeft;
6997 dBottomLeft = tmp;
6998 tmp = dTopRight;
6999 dTopRight = dBottomRight;
7000 dBottomRight = tmp;
7001 dstyinc = dstyinc * -1;
7003 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
7005 /* I don't think we need to do anything about this flag */
7006 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
7008 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
7010 tmp = dBottomRight;
7011 dBottomRight = dTopLeft;
7012 dTopLeft = tmp;
7013 tmp = dBottomLeft;
7014 dBottomLeft = dTopRight;
7015 dTopRight = tmp;
7016 dstxinc = dstxinc * -1;
7017 dstyinc = dstyinc * -1;
7019 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
7021 tmp = dTopLeft;
7022 dTopLeft = dBottomLeft;
7023 dBottomLeft = dBottomRight;
7024 dBottomRight = dTopRight;
7025 dTopRight = tmp;
7026 tmpxy = dstxinc;
7027 dstxinc = dstyinc;
7028 dstyinc = tmpxy;
7029 dstxinc = dstxinc * -1;
7031 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
7033 tmp = dTopLeft;
7034 dTopLeft = dTopRight;
7035 dTopRight = dBottomRight;
7036 dBottomRight = dBottomLeft;
7037 dBottomLeft = tmp;
7038 tmpxy = dstxinc;
7039 dstxinc = dstyinc;
7040 dstyinc = tmpxy;
7041 dstyinc = dstyinc * -1;
7043 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
7045 /* I don't think we need to do anything about this flag */
7046 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
7048 dbuf = dTopLeft;
7049 flags &= ~(WINEDDBLT_DDFX);
7052 #define COPY_COLORKEY_FX(type) \
7053 do { \
7054 const type *s; \
7055 type *d = (type *)dbuf, *dx, tmp; \
7056 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
7058 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
7059 dx = d; \
7060 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
7062 tmp = s[sx >> 16]; \
7063 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
7064 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
7066 dx[0] = tmp; \
7068 dx = (type *)(((BYTE *)dx) + dstxinc); \
7070 d = (type *)(((BYTE *)d) + dstyinc); \
7072 } while(0)
7074 switch (bpp)
7076 case 1:
7077 COPY_COLORKEY_FX(BYTE);
7078 break;
7079 case 2:
7080 COPY_COLORKEY_FX(WORD);
7081 break;
7082 case 4:
7083 COPY_COLORKEY_FX(DWORD);
7084 break;
7085 case 3:
7087 const BYTE *s;
7088 BYTE *d = dbuf, *dx;
7089 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
7091 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
7092 dx = d;
7093 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
7095 DWORD pixel, dpixel = 0;
7096 s = sbuf + 3 * (sx>>16);
7097 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
7098 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
7099 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
7100 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
7102 dx[0] = (pixel ) & 0xff;
7103 dx[1] = (pixel >> 8) & 0xff;
7104 dx[2] = (pixel >> 16) & 0xff;
7106 dx += dstxinc;
7108 d += dstyinc;
7110 break;
7112 default:
7113 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7114 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7115 hr = WINED3DERR_NOTAVAILABLE;
7116 goto error;
7117 #undef COPY_COLORKEY_FX
7122 error:
7123 if (flags && FIXME_ON(d3d_surface))
7125 FIXME("\tUnsupported flags: %#x.\n", flags);
7128 release:
7129 wined3d_surface_unmap(dst_surface);
7130 if (src_surface && src_surface != dst_surface)
7131 wined3d_surface_unmap(src_surface);
7132 /* Release the converted surface, if any. */
7133 if (src_surface && src_surface != orig_src)
7134 wined3d_surface_decref(src_surface);
7136 return hr;
7139 /* Do not call while under the GL lock. */
7140 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7141 const RECT *dst_rect, const struct wined3d_color *color)
7143 static const RECT src_rect;
7144 WINEDDBLTFX BltFx;
7146 memset(&BltFx, 0, sizeof(BltFx));
7147 BltFx.dwSize = sizeof(BltFx);
7148 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7149 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7150 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
7153 /* Do not call while under the GL lock. */
7154 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7155 struct wined3d_surface *surface, const RECT *rect, float depth)
7157 FIXME("Depth filling not implemented by cpu_blit.\n");
7158 return WINED3DERR_INVALIDCALL;
7161 const struct blit_shader cpu_blit = {
7162 cpu_blit_alloc,
7163 cpu_blit_free,
7164 cpu_blit_set,
7165 cpu_blit_unset,
7166 cpu_blit_supported,
7167 cpu_blit_color_fill,
7168 cpu_blit_depth_fill,
7171 static HRESULT surface_init(struct wined3d_surface *surface, enum wined3d_surface_type surface_type, UINT alignment,
7172 UINT width, UINT height, UINT level, enum wined3d_multisample_type multisample_type,
7173 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7174 enum wined3d_pool pool, DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops)
7176 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7177 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7178 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
7179 unsigned int resource_size;
7180 HRESULT hr;
7182 if (multisample_quality > 0)
7184 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7185 multisample_quality = 0;
7188 /* Quick lockable sanity check.
7189 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7190 * this function is too deep to need to care about things like this.
7191 * Levels need to be checked too, since they all affect what can be done. */
7192 switch (pool)
7194 case WINED3D_POOL_SCRATCH:
7195 if (!lockable)
7197 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7198 "which are mutually exclusive, setting lockable to TRUE.\n");
7199 lockable = TRUE;
7201 break;
7203 case WINED3D_POOL_SYSTEM_MEM:
7204 if (!lockable)
7205 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7206 break;
7208 case WINED3D_POOL_MANAGED:
7209 if (usage & WINED3DUSAGE_DYNAMIC)
7210 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7211 break;
7213 case WINED3D_POOL_DEFAULT:
7214 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7215 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7216 break;
7218 default:
7219 FIXME("Unknown pool %#x.\n", pool);
7220 break;
7223 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3D_POOL_DEFAULT)
7224 FIXME("Trying to create a render target that isn't in the default pool.\n");
7226 /* FIXME: Check that the format is supported by the device. */
7228 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7229 if (!resource_size)
7230 return WINED3DERR_INVALIDCALL;
7232 surface->surface_type = surface_type;
7234 switch (surface_type)
7236 case WINED3D_SURFACE_TYPE_OPENGL:
7237 surface->surface_ops = &surface_ops;
7238 break;
7240 case WINED3D_SURFACE_TYPE_GDI:
7241 surface->surface_ops = &gdi_surface_ops;
7242 break;
7244 default:
7245 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7246 return WINED3DERR_INVALIDCALL;
7249 hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
7250 multisample_type, multisample_quality, usage, pool, width, height, 1,
7251 resource_size, parent, parent_ops, &surface_resource_ops);
7252 if (FAILED(hr))
7254 WARN("Failed to initialize resource, returning %#x.\n", hr);
7255 return hr;
7258 /* "Standalone" surface. */
7259 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7261 surface->texture_level = level;
7262 list_init(&surface->overlays);
7264 /* Flags */
7265 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7266 if (flags & WINED3D_SURFACE_DISCARD)
7267 surface->flags |= SFLAG_DISCARD;
7268 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
7269 surface->flags |= SFLAG_PIN_SYSMEM;
7270 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7271 surface->flags |= SFLAG_LOCKABLE;
7272 /* I'm not sure if this qualifies as a hack or as an optimization. It
7273 * seems reasonable to assume that lockable render targets will get
7274 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7275 * creation. However, the other reason we want to do this is that several
7276 * ddraw applications access surface memory while the surface isn't
7277 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7278 * future locks prevents these from crashing. */
7279 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7280 surface->flags |= SFLAG_DYNLOCK;
7282 /* Mark the texture as dirty so that it gets loaded first time around. */
7283 surface_add_dirty_rect(surface, NULL);
7284 list_init(&surface->renderbuffers);
7286 TRACE("surface %p, memory %p, size %u\n",
7287 surface, surface->resource.allocatedMemory, surface->resource.size);
7289 /* Call the private setup routine */
7290 hr = surface->surface_ops->surface_private_setup(surface);
7291 if (FAILED(hr))
7293 ERR("Private setup failed, returning %#x\n", hr);
7294 surface_cleanup(surface);
7295 return hr;
7298 /* Similar to lockable rendertargets above, creating the DIB section
7299 * during surface initialization prevents the sysmem pointer from changing
7300 * after a wined3d_surface_getdc() call. */
7301 if ((usage & WINED3DUSAGE_OWNDC) && !surface->hDC
7302 && SUCCEEDED(surface_create_dib_section(surface)))
7304 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
7305 surface->resource.heapMemory = NULL;
7306 surface->resource.allocatedMemory = surface->dib.bitmap_data;
7309 return hr;
7312 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7313 enum wined3d_format_id format_id, UINT level, DWORD usage, enum wined3d_pool pool,
7314 enum wined3d_multisample_type multisample_type, DWORD multisample_quality,
7315 enum wined3d_surface_type surface_type, DWORD flags, void *parent,
7316 const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7318 struct wined3d_surface *object;
7319 HRESULT hr;
7321 TRACE("device %p, width %u, height %u, format %s, level %u\n",
7322 device, width, height, debug_d3dformat(format_id), level);
7323 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7324 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7325 TRACE("surface_type %#x, flags %#x, parent %p, parent_ops %p.\n", surface_type, flags, parent, parent_ops);
7327 if (surface_type == WINED3D_SURFACE_TYPE_OPENGL && !device->adapter)
7329 ERR("OpenGL surfaces are not available without OpenGL.\n");
7330 return WINED3DERR_NOTAVAILABLE;
7333 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7334 if (!object)
7336 ERR("Failed to allocate surface memory.\n");
7337 return WINED3DERR_OUTOFVIDEOMEMORY;
7340 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level,
7341 multisample_type, multisample_quality, device, usage, format_id, pool, flags, parent, parent_ops);
7342 if (FAILED(hr))
7344 WARN("Failed to initialize surface, returning %#x.\n", hr);
7345 HeapFree(GetProcessHeap(), 0, object);
7346 return hr;
7349 TRACE("Created surface %p.\n", object);
7350 *surface = object;
7352 return hr;