mapi32: Add support to MAPISendMailW for ANSI fallback.
[wine/multimedia.git] / dlls / wined3d / surface.c
bloba80cab2f4a9dc24500575f2ae57151f1150994bb
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 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
957 && surface->container.u.swapchain->front_buffer == surface)
959 if (!surface->dirtyRect.left && !surface->dirtyRect.top
960 && surface->dirtyRect.right == surface->resource.width
961 && surface->dirtyRect.bottom == surface->resource.height)
963 fullsurface = TRUE;
965 else
967 /* TODO: Proper partial rectangle tracking. */
968 fullsurface = FALSE;
969 surface->flags |= SFLAG_INSYSMEM;
972 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
974 /* Partial rectangle tracking is not commonly implemented, it is only
975 * done for render targets. INSYSMEM was set before to tell
976 * surface_load_location() where to read the rectangle from.
977 * Indrawable is set because all modifications from the partial
978 * sysmem copy are written back to the drawable, thus the surface is
979 * merged again in the drawable. The sysmem copy is not fully up to
980 * date because only a subrectangle was read in Map(). */
981 if (!fullsurface)
983 surface_modify_location(surface, surface->draw_binding, TRUE);
984 surface_evict_sysmem(surface);
987 surface->dirtyRect.left = surface->resource.width;
988 surface->dirtyRect.top = surface->resource.height;
989 surface->dirtyRect.right = 0;
990 surface->dirtyRect.bottom = 0;
992 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
994 FIXME("Depth / stencil buffer locking is not implemented.\n");
997 done:
998 /* Overlays have to be redrawn manually after changes with the GL implementation */
999 if (surface->overlay_dest)
1000 surface_draw_overlay(surface);
1003 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1005 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1006 return FALSE;
1007 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1008 return FALSE;
1009 return TRUE;
1012 static void wined3d_surface_depth_blt_fbo(const struct wined3d_device *device, struct wined3d_surface *src_surface,
1013 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1015 const struct wined3d_gl_info *gl_info;
1016 struct wined3d_context *context;
1017 DWORD src_mask, dst_mask;
1018 GLbitfield gl_mask;
1020 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1021 device, src_surface, wine_dbgstr_rect(src_rect),
1022 dst_surface, wine_dbgstr_rect(dst_rect));
1024 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1025 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1027 if (src_mask != dst_mask)
1029 ERR("Incompatible formats %s and %s.\n",
1030 debug_d3dformat(src_surface->resource.format->id),
1031 debug_d3dformat(dst_surface->resource.format->id));
1032 return;
1035 if (!src_mask)
1037 ERR("Not a depth / stencil format: %s.\n",
1038 debug_d3dformat(src_surface->resource.format->id));
1039 return;
1042 gl_mask = 0;
1043 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1044 gl_mask |= GL_DEPTH_BUFFER_BIT;
1045 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1046 gl_mask |= GL_STENCIL_BUFFER_BIT;
1048 /* Make sure the locations are up-to-date. Loading the destination
1049 * surface isn't required if the entire surface is overwritten. */
1050 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1051 if (!surface_is_full_rect(dst_surface, dst_rect))
1052 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1054 context = context_acquire(device, NULL);
1055 if (!context->valid)
1057 context_release(context);
1058 WARN("Invalid context, skipping blit.\n");
1059 return;
1062 gl_info = context->gl_info;
1064 ENTER_GL();
1066 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1067 glReadBuffer(GL_NONE);
1068 checkGLcall("glReadBuffer()");
1069 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1071 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1072 context_set_draw_buffer(context, GL_NONE);
1073 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1074 context_invalidate_state(context, STATE_FRAMEBUFFER);
1076 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1078 glDepthMask(GL_TRUE);
1079 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
1081 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1083 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1085 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1086 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
1088 glStencilMask(~0U);
1089 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
1092 glDisable(GL_SCISSOR_TEST);
1093 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1095 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1096 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1097 checkGLcall("glBlitFramebuffer()");
1099 LEAVE_GL();
1101 if (wined3d_settings.strict_draw_ordering)
1102 wglFlush(); /* Flush to ensure ordering across contexts. */
1104 context_release(context);
1107 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1108 * Depth / stencil is not supported. */
1109 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
1110 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1111 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1113 const struct wined3d_gl_info *gl_info;
1114 struct wined3d_context *context;
1115 RECT src_rect, dst_rect;
1116 GLenum gl_filter;
1117 GLenum buffer;
1119 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1120 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1121 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1122 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1123 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1125 src_rect = *src_rect_in;
1126 dst_rect = *dst_rect_in;
1128 switch (filter)
1130 case WINED3D_TEXF_LINEAR:
1131 gl_filter = GL_LINEAR;
1132 break;
1134 default:
1135 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1136 case WINED3D_TEXF_NONE:
1137 case WINED3D_TEXF_POINT:
1138 gl_filter = GL_NEAREST;
1139 break;
1142 /* Resolve the source surface first if needed. */
1143 if (src_location == SFLAG_INRB_MULTISAMPLE
1144 && (src_surface->resource.format->id != dst_surface->resource.format->id
1145 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1146 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1147 src_location = SFLAG_INRB_RESOLVED;
1149 /* Make sure the locations are up-to-date. Loading the destination
1150 * surface isn't required if the entire surface is overwritten. (And is
1151 * in fact harmful if we're being called by surface_load_location() with
1152 * the purpose of loading the destination surface.) */
1153 surface_load_location(src_surface, src_location, NULL);
1154 if (!surface_is_full_rect(dst_surface, &dst_rect))
1155 surface_load_location(dst_surface, dst_location, NULL);
1157 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1158 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1159 else context = context_acquire(device, NULL);
1161 if (!context->valid)
1163 context_release(context);
1164 WARN("Invalid context, skipping blit.\n");
1165 return;
1168 gl_info = context->gl_info;
1170 if (src_location == SFLAG_INDRAWABLE)
1172 TRACE("Source surface %p is onscreen.\n", src_surface);
1173 buffer = surface_get_gl_buffer(src_surface);
1174 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1176 else
1178 TRACE("Source surface %p is offscreen.\n", src_surface);
1179 buffer = GL_COLOR_ATTACHMENT0;
1182 ENTER_GL();
1183 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1184 glReadBuffer(buffer);
1185 checkGLcall("glReadBuffer()");
1186 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1187 LEAVE_GL();
1189 if (dst_location == SFLAG_INDRAWABLE)
1191 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1192 buffer = surface_get_gl_buffer(dst_surface);
1193 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1195 else
1197 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1198 buffer = GL_COLOR_ATTACHMENT0;
1201 ENTER_GL();
1202 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1203 context_set_draw_buffer(context, buffer);
1204 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1205 context_invalidate_state(context, STATE_FRAMEBUFFER);
1207 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1208 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
1209 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
1210 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
1211 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
1213 glDisable(GL_SCISSOR_TEST);
1214 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1216 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1217 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1218 checkGLcall("glBlitFramebuffer()");
1220 LEAVE_GL();
1222 if (wined3d_settings.strict_draw_ordering
1223 || (dst_location == SFLAG_INDRAWABLE
1224 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1225 wglFlush();
1227 context_release(context);
1230 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1231 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
1232 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
1234 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1235 return FALSE;
1237 /* Source and/or destination need to be on the GL side */
1238 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
1239 return FALSE;
1241 switch (blit_op)
1243 case WINED3D_BLIT_OP_COLOR_BLIT:
1244 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1245 return FALSE;
1246 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1247 return FALSE;
1248 break;
1250 case WINED3D_BLIT_OP_DEPTH_BLIT:
1251 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1252 return FALSE;
1253 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1254 return FALSE;
1255 break;
1257 default:
1258 return FALSE;
1261 if (!(src_format->id == dst_format->id
1262 || (is_identity_fixup(src_format->color_fixup)
1263 && is_identity_fixup(dst_format->color_fixup))))
1264 return FALSE;
1266 return TRUE;
1269 /* This function checks if the primary render target uses the 8bit paletted format. */
1270 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1272 if (device->fb.render_targets && device->fb.render_targets[0])
1274 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1275 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1276 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1277 return TRUE;
1279 return FALSE;
1282 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1283 DWORD color, struct wined3d_color *float_color)
1285 const struct wined3d_format *format = surface->resource.format;
1286 const struct wined3d_device *device = surface->resource.device;
1288 switch (format->id)
1290 case WINED3DFMT_P8_UINT:
1291 if (surface->palette)
1293 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1294 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1295 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1297 else
1299 float_color->r = 0.0f;
1300 float_color->g = 0.0f;
1301 float_color->b = 0.0f;
1303 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1304 break;
1306 case WINED3DFMT_B5G6R5_UNORM:
1307 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1308 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1309 float_color->b = (color & 0x1f) / 31.0f;
1310 float_color->a = 1.0f;
1311 break;
1313 case WINED3DFMT_B8G8R8_UNORM:
1314 case WINED3DFMT_B8G8R8X8_UNORM:
1315 float_color->r = D3DCOLOR_R(color);
1316 float_color->g = D3DCOLOR_G(color);
1317 float_color->b = D3DCOLOR_B(color);
1318 float_color->a = 1.0f;
1319 break;
1321 case WINED3DFMT_B8G8R8A8_UNORM:
1322 float_color->r = D3DCOLOR_R(color);
1323 float_color->g = D3DCOLOR_G(color);
1324 float_color->b = D3DCOLOR_B(color);
1325 float_color->a = D3DCOLOR_A(color);
1326 break;
1328 default:
1329 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1330 return FALSE;
1333 return TRUE;
1336 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1338 const struct wined3d_format *format = surface->resource.format;
1340 switch (format->id)
1342 case WINED3DFMT_S1_UINT_D15_UNORM:
1343 *float_depth = depth / (float)0x00007fff;
1344 break;
1346 case WINED3DFMT_D16_UNORM:
1347 *float_depth = depth / (float)0x0000ffff;
1348 break;
1350 case WINED3DFMT_D24_UNORM_S8_UINT:
1351 case WINED3DFMT_X8D24_UNORM:
1352 *float_depth = depth / (float)0x00ffffff;
1353 break;
1355 case WINED3DFMT_D32_UNORM:
1356 *float_depth = depth / (float)0xffffffff;
1357 break;
1359 default:
1360 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1361 return FALSE;
1364 return TRUE;
1367 /* Do not call while under the GL lock. */
1368 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1370 const struct wined3d_resource *resource = &surface->resource;
1371 struct wined3d_device *device = resource->device;
1372 const struct blit_shader *blitter;
1374 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1375 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1376 if (!blitter)
1378 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1379 return WINED3DERR_INVALIDCALL;
1382 return blitter->depth_fill(device, surface, rect, depth);
1385 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1386 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1388 struct wined3d_device *device = src_surface->resource.device;
1390 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1391 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1392 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1393 return WINED3DERR_INVALIDCALL;
1395 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1397 surface_modify_ds_location(dst_surface, SFLAG_INTEXTURE,
1398 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1400 return WINED3D_OK;
1403 /* Do not call while under the GL lock. */
1404 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1405 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1406 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
1408 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1409 struct wined3d_device *device = dst_surface->resource.device;
1410 DWORD src_ds_flags, dst_ds_flags;
1411 RECT src_rect, dst_rect;
1412 BOOL scale, convert;
1414 static const DWORD simple_blit = WINEDDBLT_ASYNC
1415 | WINEDDBLT_COLORFILL
1416 | WINEDDBLT_WAIT
1417 | WINEDDBLT_DEPTHFILL
1418 | WINEDDBLT_DONOTWAIT;
1420 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1421 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1422 flags, fx, debug_d3dtexturefiltertype(filter));
1423 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1425 if (fx)
1427 TRACE("dwSize %#x.\n", fx->dwSize);
1428 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
1429 TRACE("dwROP %#x.\n", fx->dwROP);
1430 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
1431 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
1432 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
1433 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
1434 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
1435 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
1436 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
1437 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
1438 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
1439 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
1440 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
1441 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
1442 TRACE("dwReserved %#x.\n", fx->dwReserved);
1443 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
1444 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
1445 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
1446 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
1447 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
1448 TRACE("ddckDestColorkey {%#x, %#x}.\n",
1449 fx->ddckDestColorkey.color_space_low_value,
1450 fx->ddckDestColorkey.color_space_high_value);
1451 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
1452 fx->ddckSrcColorkey.color_space_low_value,
1453 fx->ddckSrcColorkey.color_space_high_value);
1456 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1458 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1459 return WINEDDERR_SURFACEBUSY;
1462 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1464 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1465 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1466 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1467 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1468 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1470 WARN("The application gave us a bad destination rectangle.\n");
1471 return WINEDDERR_INVALIDRECT;
1474 if (src_surface)
1476 surface_get_rect(src_surface, src_rect_in, &src_rect);
1478 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1479 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1480 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1481 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1482 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1484 WARN("Application gave us bad source rectangle for Blt.\n");
1485 return WINEDDERR_INVALIDRECT;
1488 else
1490 memset(&src_rect, 0, sizeof(src_rect));
1493 if (!fx || !(fx->dwDDFX))
1494 flags &= ~WINEDDBLT_DDFX;
1496 if (flags & WINEDDBLT_WAIT)
1497 flags &= ~WINEDDBLT_WAIT;
1499 if (flags & WINEDDBLT_ASYNC)
1501 static unsigned int once;
1503 if (!once++)
1504 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1505 flags &= ~WINEDDBLT_ASYNC;
1508 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1509 if (flags & WINEDDBLT_DONOTWAIT)
1511 static unsigned int once;
1513 if (!once++)
1514 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1515 flags &= ~WINEDDBLT_DONOTWAIT;
1518 if (!device->d3d_initialized)
1520 WARN("D3D not initialized, using fallback.\n");
1521 goto cpu;
1524 /* We want to avoid invalidating the sysmem location for converted
1525 * surfaces, since otherwise we'd have to convert the data back when
1526 * locking them. */
1527 if (dst_surface->flags & SFLAG_CONVERTED)
1529 WARN("Converted surface, using CPU blit.\n");
1530 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1533 if (flags & ~simple_blit)
1535 WARN("Using fallback for complex blit (%#x).\n", flags);
1536 goto fallback;
1539 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1540 src_swapchain = src_surface->container.u.swapchain;
1541 else
1542 src_swapchain = NULL;
1544 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1545 dst_swapchain = dst_surface->container.u.swapchain;
1546 else
1547 dst_swapchain = NULL;
1549 /* This isn't strictly needed. FBO blits for example could deal with
1550 * cross-swapchain blits by first downloading the source to a texture
1551 * before switching to the destination context. We just have this here to
1552 * not have to deal with the issue, since cross-swapchain blits should be
1553 * rare. */
1554 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1556 FIXME("Using fallback for cross-swapchain blit.\n");
1557 goto fallback;
1560 scale = src_surface
1561 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
1562 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
1563 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
1565 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1566 if (src_surface)
1567 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1568 else
1569 src_ds_flags = 0;
1571 if (src_ds_flags || dst_ds_flags)
1573 if (flags & WINEDDBLT_DEPTHFILL)
1575 float depth;
1577 TRACE("Depth fill.\n");
1579 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1580 return WINED3DERR_INVALIDCALL;
1582 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1583 return WINED3D_OK;
1585 else
1587 if (src_ds_flags != dst_ds_flags)
1589 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1590 return WINED3DERR_INVALIDCALL;
1593 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1594 return WINED3D_OK;
1597 else
1599 /* In principle this would apply to depth blits as well, but we don't
1600 * implement those in the CPU blitter at the moment. */
1601 if ((dst_surface->flags & SFLAG_INSYSMEM)
1602 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
1604 if (scale)
1605 TRACE("Not doing sysmem blit because of scaling.\n");
1606 else if (convert)
1607 TRACE("Not doing sysmem blit because of format conversion.\n");
1608 else
1609 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1612 if (flags & WINEDDBLT_COLORFILL)
1614 struct wined3d_color color;
1616 TRACE("Color fill.\n");
1618 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1619 goto fallback;
1621 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1622 return WINED3D_OK;
1624 else
1626 TRACE("Color blit.\n");
1628 /* Upload */
1629 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
1631 if (scale)
1632 TRACE("Not doing upload because of scaling.\n");
1633 else if (convert)
1634 TRACE("Not doing upload because of format conversion.\n");
1635 else
1637 POINT dst_point = {dst_rect.left, dst_rect.top};
1639 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
1641 if (!surface_is_offscreen(dst_surface))
1642 surface_load_location(dst_surface, dst_surface->draw_binding, NULL);
1643 return WINED3D_OK;
1648 /* Use present for back -> front blits. The idea behind this is
1649 * that present is potentially faster than a blit, in particular
1650 * when FBO blits aren't available. Some ddraw applications like
1651 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
1652 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
1653 * applications can't blit directly to the frontbuffer. */
1654 if (dst_swapchain && dst_swapchain->back_buffers
1655 && dst_surface == dst_swapchain->front_buffer
1656 && src_surface == dst_swapchain->back_buffers[0])
1658 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
1660 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1662 /* Set the swap effect to COPY, we don't want the backbuffer
1663 * to become undefined. */
1664 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
1665 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
1666 dst_swapchain->desc.swap_effect = swap_effect;
1668 return WINED3D_OK;
1671 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1672 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1673 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1675 TRACE("Using FBO blit.\n");
1677 surface_blt_fbo(device, filter,
1678 src_surface, src_surface->draw_binding, &src_rect,
1679 dst_surface, dst_surface->draw_binding, &dst_rect);
1680 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1681 return WINED3D_OK;
1684 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1685 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1686 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1688 TRACE("Using arbfp blit.\n");
1690 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1691 return WINED3D_OK;
1696 fallback:
1698 /* Special cases for render targets. */
1699 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1700 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1702 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1703 src_surface, &src_rect, flags, fx, filter)))
1704 return WINED3D_OK;
1707 cpu:
1709 /* For the rest call the X11 surface implementation. For render targets
1710 * this should be implemented OpenGL accelerated in BltOverride, other
1711 * blits are rather rare. */
1712 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1715 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1716 struct wined3d_surface *render_target)
1718 TRACE("surface %p, render_target %p.\n", surface, render_target);
1720 /* TODO: Check surface sizes, pools, etc. */
1722 if (render_target->resource.multisample_type)
1723 return WINED3DERR_INVALIDCALL;
1725 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1728 /* Context activation is done by the caller. */
1729 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1731 if (surface->flags & SFLAG_DIBSECTION)
1733 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1735 else
1737 if (!surface->resource.heapMemory)
1738 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1739 else if (!(surface->flags & SFLAG_CLIENT))
1740 ERR("Surface %p has heapMemory %p and flags %#x.\n",
1741 surface, surface->resource.heapMemory, surface->flags);
1743 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1744 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1747 ENTER_GL();
1748 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1749 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1750 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1751 surface->resource.size, surface->resource.allocatedMemory));
1752 checkGLcall("glGetBufferSubDataARB");
1753 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1754 checkGLcall("glDeleteBuffersARB");
1755 LEAVE_GL();
1757 surface->pbo = 0;
1758 surface->flags &= ~SFLAG_PBO;
1761 /* Do not call while under the GL lock. */
1762 static void surface_unload(struct wined3d_resource *resource)
1764 struct wined3d_surface *surface = surface_from_resource(resource);
1765 struct wined3d_renderbuffer_entry *entry, *entry2;
1766 struct wined3d_device *device = resource->device;
1767 const struct wined3d_gl_info *gl_info;
1768 struct wined3d_context *context;
1770 TRACE("surface %p.\n", surface);
1772 if (resource->pool == WINED3D_POOL_DEFAULT)
1774 /* Default pool resources are supposed to be destroyed before Reset is called.
1775 * Implicit resources stay however. So this means we have an implicit render target
1776 * or depth stencil. The content may be destroyed, but we still have to tear down
1777 * opengl resources, so we cannot leave early.
1779 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1780 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1781 * or the depth stencil into an FBO the texture or render buffer will be removed
1782 * and all flags get lost
1784 if (!(surface->flags & SFLAG_PBO))
1785 surface_init_sysmem(surface);
1786 /* We also get here when the ddraw swapchain is destroyed, for example
1787 * for a mode switch. In this case this surface won't necessarily be
1788 * an implicit surface. We have to mark it lost so that the
1789 * application can restore it after the mode switch. */
1790 surface->flags |= SFLAG_LOST;
1792 else
1794 /* Load the surface into system memory */
1795 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1796 surface_modify_location(surface, surface->draw_binding, FALSE);
1798 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1799 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1800 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1802 context = context_acquire(device, NULL);
1803 gl_info = context->gl_info;
1805 /* Destroy PBOs, but load them into real sysmem before */
1806 if (surface->flags & SFLAG_PBO)
1807 surface_remove_pbo(surface, gl_info);
1809 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1810 * all application-created targets the application has to release the surface
1811 * before calling _Reset
1813 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1815 ENTER_GL();
1816 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1817 LEAVE_GL();
1818 list_remove(&entry->entry);
1819 HeapFree(GetProcessHeap(), 0, entry);
1821 list_init(&surface->renderbuffers);
1822 surface->current_renderbuffer = NULL;
1824 ENTER_GL();
1826 /* If we're in a texture, the texture name belongs to the texture.
1827 * Otherwise, destroy it. */
1828 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1830 glDeleteTextures(1, &surface->texture_name);
1831 surface->texture_name = 0;
1832 glDeleteTextures(1, &surface->texture_name_srgb);
1833 surface->texture_name_srgb = 0;
1835 if (surface->rb_multisample)
1837 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1838 surface->rb_multisample = 0;
1840 if (surface->rb_resolved)
1842 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1843 surface->rb_resolved = 0;
1846 LEAVE_GL();
1848 context_release(context);
1850 resource_unload(resource);
1853 static const struct wined3d_resource_ops surface_resource_ops =
1855 surface_unload,
1858 static const struct wined3d_surface_ops surface_ops =
1860 surface_private_setup,
1861 surface_realize_palette,
1862 surface_map,
1863 surface_unmap,
1866 /*****************************************************************************
1867 * Initializes the GDI surface, aka creates the DIB section we render to
1868 * The DIB section creation is done by calling GetDC, which will create the
1869 * section and releasing the dc to allow the app to use it. The dib section
1870 * will stay until the surface is released
1872 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1873 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1874 * avoid confusion in the shared surface code.
1876 * Returns:
1877 * WINED3D_OK on success
1878 * The return values of called methods on failure
1880 *****************************************************************************/
1881 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1883 HRESULT hr;
1885 TRACE("surface %p.\n", surface);
1887 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1889 ERR("Overlays not yet supported by GDI surfaces.\n");
1890 return WINED3DERR_INVALIDCALL;
1893 /* Sysmem textures have memory already allocated - release it,
1894 * this avoids an unnecessary memcpy. */
1895 hr = surface_create_dib_section(surface);
1896 if (SUCCEEDED(hr))
1898 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1899 surface->resource.heapMemory = NULL;
1900 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1903 /* We don't mind the nonpow2 stuff in GDI. */
1904 surface->pow2Width = surface->resource.width;
1905 surface->pow2Height = surface->resource.height;
1907 return WINED3D_OK;
1910 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1912 struct wined3d_palette *palette = surface->palette;
1914 TRACE("surface %p.\n", surface);
1916 if (!palette) return;
1918 if (surface->flags & SFLAG_DIBSECTION)
1920 RGBQUAD col[256];
1921 unsigned int i;
1923 TRACE("Updating the DC's palette.\n");
1925 for (i = 0; i < 256; ++i)
1927 col[i].rgbRed = palette->palents[i].peRed;
1928 col[i].rgbGreen = palette->palents[i].peGreen;
1929 col[i].rgbBlue = palette->palents[i].peBlue;
1930 col[i].rgbReserved = 0;
1932 SetDIBColorTable(surface->hDC, 0, 256, col);
1935 /* Update the image because of the palette change. Some games like e.g.
1936 * Red Alert call SetEntries a lot to implement fading. */
1937 /* Tell the swapchain to update the screen. */
1938 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1940 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1941 if (surface == swapchain->front_buffer)
1943 x11_copy_to_screen(swapchain, NULL);
1948 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
1950 TRACE("surface %p, rect %s, flags %#x.\n",
1951 surface, wine_dbgstr_rect(rect), flags);
1953 if (!(surface->flags & SFLAG_DIBSECTION))
1955 HRESULT hr;
1957 /* This happens on gdi surfaces if the application set a user pointer
1958 * and resets it. Recreate the DIB section. */
1959 if (FAILED(hr = surface_create_dib_section(surface)))
1961 ERR("Failed to create dib section, hr %#x.\n", hr);
1962 return;
1964 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1965 surface->resource.heapMemory = NULL;
1966 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1970 static void gdi_surface_unmap(struct wined3d_surface *surface)
1972 TRACE("surface %p.\n", surface);
1974 /* Tell the swapchain to update the screen. */
1975 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1977 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1978 if (surface == swapchain->front_buffer)
1980 x11_copy_to_screen(swapchain, &surface->lockedRect);
1984 memset(&surface->lockedRect, 0, sizeof(RECT));
1987 static const struct wined3d_surface_ops gdi_surface_ops =
1989 gdi_surface_private_setup,
1990 gdi_surface_realize_palette,
1991 gdi_surface_map,
1992 gdi_surface_unmap,
1995 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
1997 GLuint *name;
1998 DWORD flag;
2000 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2002 if(srgb)
2004 name = &surface->texture_name_srgb;
2005 flag = SFLAG_INSRGBTEX;
2007 else
2009 name = &surface->texture_name;
2010 flag = SFLAG_INTEXTURE;
2013 if (!*name && new_name)
2015 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2016 * surface has no texture name yet. See if we can get rid of this. */
2017 if (surface->flags & flag)
2019 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2020 surface_modify_location(surface, flag, FALSE);
2024 *name = new_name;
2025 surface_force_reload(surface);
2028 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2030 TRACE("surface %p, target %#x.\n", surface, target);
2032 if (surface->texture_target != target)
2034 if (target == GL_TEXTURE_RECTANGLE_ARB)
2036 surface->flags &= ~SFLAG_NORMCOORD;
2038 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2040 surface->flags |= SFLAG_NORMCOORD;
2043 surface->texture_target = target;
2044 surface_force_reload(surface);
2047 /* Context activation is done by the caller. */
2048 void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
2050 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
2052 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2054 struct wined3d_texture *texture = surface->container.u.texture;
2056 TRACE("Passing to container (%p).\n", texture);
2057 texture->texture_ops->texture_bind(texture, context, srgb);
2059 else
2061 if (surface->texture_level)
2063 ERR("Standalone surface %p is non-zero texture level %u.\n",
2064 surface, surface->texture_level);
2067 if (srgb)
2068 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2070 ENTER_GL();
2072 if (!surface->texture_name)
2074 glGenTextures(1, &surface->texture_name);
2075 checkGLcall("glGenTextures");
2077 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2079 context_bind_texture(context, surface->texture_target, surface->texture_name);
2080 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2081 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2082 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2083 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2084 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2085 checkGLcall("glTexParameteri");
2087 else
2089 context_bind_texture(context, surface->texture_target, surface->texture_name);
2092 LEAVE_GL();
2096 /* This call just downloads data, the caller is responsible for binding the
2097 * correct texture. */
2098 /* Context activation is done by the caller. */
2099 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2101 const struct wined3d_format *format = surface->resource.format;
2103 /* Only support read back of converted P8 surfaces. */
2104 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2106 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
2107 return;
2110 ENTER_GL();
2112 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2114 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2115 surface, surface->texture_level, format->glFormat, format->glType,
2116 surface->resource.allocatedMemory);
2118 if (surface->flags & SFLAG_PBO)
2120 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2121 checkGLcall("glBindBufferARB");
2122 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2123 checkGLcall("glGetCompressedTexImageARB");
2124 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2125 checkGLcall("glBindBufferARB");
2127 else
2129 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2130 surface->texture_level, surface->resource.allocatedMemory));
2131 checkGLcall("glGetCompressedTexImageARB");
2134 LEAVE_GL();
2136 else
2138 void *mem;
2139 GLenum gl_format = format->glFormat;
2140 GLenum gl_type = format->glType;
2141 int src_pitch = 0;
2142 int dst_pitch = 0;
2144 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2145 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2147 gl_format = GL_ALPHA;
2148 gl_type = GL_UNSIGNED_BYTE;
2151 if (surface->flags & SFLAG_NONPOW2)
2153 unsigned char alignment = surface->resource.device->surface_alignment;
2154 src_pitch = format->byte_count * surface->pow2Width;
2155 dst_pitch = wined3d_surface_get_pitch(surface);
2156 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2157 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2159 else
2161 mem = surface->resource.allocatedMemory;
2164 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2165 surface, surface->texture_level, gl_format, gl_type, mem);
2167 if (surface->flags & SFLAG_PBO)
2169 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2170 checkGLcall("glBindBufferARB");
2172 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2173 checkGLcall("glGetTexImage");
2175 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2176 checkGLcall("glBindBufferARB");
2178 else
2180 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2181 checkGLcall("glGetTexImage");
2183 LEAVE_GL();
2185 if (surface->flags & SFLAG_NONPOW2)
2187 const BYTE *src_data;
2188 BYTE *dst_data;
2189 UINT y;
2191 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2192 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2193 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2195 * We're doing this...
2197 * instead of boxing the texture :
2198 * |<-texture width ->| -->pow2width| /\
2199 * |111111111111111111| | |
2200 * |222 Texture 222222| boxed empty | texture height
2201 * |3333 Data 33333333| | |
2202 * |444444444444444444| | \/
2203 * ----------------------------------- |
2204 * | boxed empty | boxed empty | pow2height
2205 * | | | \/
2206 * -----------------------------------
2209 * we're repacking the data to the expected texture width
2211 * |<-texture width ->| -->pow2width| /\
2212 * |111111111111111111222222222222222| |
2213 * |222333333333333333333444444444444| texture height
2214 * |444444 | |
2215 * | | \/
2216 * | | |
2217 * | empty | pow2height
2218 * | | \/
2219 * -----------------------------------
2221 * == is the same as
2223 * |<-texture width ->| /\
2224 * |111111111111111111|
2225 * |222222222222222222|texture height
2226 * |333333333333333333|
2227 * |444444444444444444| \/
2228 * --------------------
2230 * this also means that any references to allocatedMemory should work with the data as if were a
2231 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2233 * internally the texture is still stored in a boxed format so any references to textureName will
2234 * get a boxed texture with width pow2width and not a texture of width resource.width.
2236 * Performance should not be an issue, because applications normally do not lock the surfaces when
2237 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2238 * and doesn't have to be re-read. */
2239 src_data = mem;
2240 dst_data = surface->resource.allocatedMemory;
2241 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2242 for (y = 1; y < surface->resource.height; ++y)
2244 /* skip the first row */
2245 src_data += src_pitch;
2246 dst_data += dst_pitch;
2247 memcpy(dst_data, src_data, dst_pitch);
2250 HeapFree(GetProcessHeap(), 0, mem);
2254 /* Surface has now been downloaded */
2255 surface->flags |= SFLAG_INSYSMEM;
2258 /* This call just uploads data, the caller is responsible for binding the
2259 * correct texture. */
2260 /* Context activation is done by the caller. */
2261 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2262 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
2263 BOOL srgb, const struct wined3d_bo_address *data)
2265 UINT update_w = src_rect->right - src_rect->left;
2266 UINT update_h = src_rect->bottom - src_rect->top;
2268 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
2269 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
2270 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2272 if (surface->flags & SFLAG_LOCKED)
2274 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
2275 surface->flags |= SFLAG_PIN_SYSMEM;
2278 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
2280 update_h *= format->height_scale.numerator;
2281 update_h /= format->height_scale.denominator;
2284 ENTER_GL();
2286 if (data->buffer_object)
2288 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2289 checkGLcall("glBindBufferARB");
2292 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2294 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2295 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2296 const BYTE *addr = data->addr;
2297 GLenum internal;
2299 addr += (src_rect->top / format->block_height) * src_pitch;
2300 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2302 if (srgb)
2303 internal = format->glGammaInternal;
2304 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2305 internal = format->rtInternal;
2306 else
2307 internal = format->glInternal;
2309 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2310 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2311 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2313 if (row_length == src_pitch)
2315 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2316 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2318 else
2320 UINT row, y;
2322 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2323 * can't use the unpack row length like below. */
2324 for (row = 0, y = dst_point->y; row < row_count; ++row)
2326 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2327 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2328 y += format->block_height;
2329 addr += src_pitch;
2332 checkGLcall("glCompressedTexSubImage2DARB");
2334 else
2336 const BYTE *addr = data->addr;
2338 addr += src_rect->top * src_pitch;
2339 addr += src_rect->left * format->byte_count;
2341 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2342 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2343 update_w, update_h, format->glFormat, format->glType, addr);
2345 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
2346 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2347 update_w, update_h, format->glFormat, format->glType, addr);
2348 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2349 checkGLcall("glTexSubImage2D");
2352 if (data->buffer_object)
2354 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2355 checkGLcall("glBindBufferARB");
2358 LEAVE_GL();
2360 if (wined3d_settings.strict_draw_ordering)
2361 wglFlush();
2363 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2365 struct wined3d_device *device = surface->resource.device;
2366 unsigned int i;
2368 for (i = 0; i < device->context_count; ++i)
2370 context_surface_update(device->contexts[i], surface);
2375 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
2376 struct wined3d_surface *src_surface, const RECT *src_rect)
2378 const struct wined3d_format *src_format;
2379 const struct wined3d_format *dst_format;
2380 const struct wined3d_gl_info *gl_info;
2381 enum wined3d_conversion_type convert;
2382 struct wined3d_context *context;
2383 struct wined3d_bo_address data;
2384 struct wined3d_format format;
2385 UINT update_w, update_h;
2386 UINT dst_w, dst_h;
2387 UINT src_w, src_h;
2388 UINT src_pitch;
2389 POINT p;
2390 RECT r;
2392 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
2393 dst_surface, wine_dbgstr_point(dst_point),
2394 src_surface, wine_dbgstr_rect(src_rect));
2396 src_format = src_surface->resource.format;
2397 dst_format = dst_surface->resource.format;
2399 if (src_format->id != dst_format->id)
2401 WARN("Source and destination surfaces should have the same format.\n");
2402 return WINED3DERR_INVALIDCALL;
2405 if (!dst_point)
2407 p.x = 0;
2408 p.y = 0;
2409 dst_point = &p;
2411 else if (dst_point->x < 0 || dst_point->y < 0)
2413 WARN("Invalid destination point.\n");
2414 return WINED3DERR_INVALIDCALL;
2417 if (!src_rect)
2419 r.left = 0;
2420 r.top = 0;
2421 r.right = src_surface->resource.width;
2422 r.bottom = src_surface->resource.height;
2423 src_rect = &r;
2425 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
2426 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
2428 WARN("Invalid source rectangle.\n");
2429 return WINED3DERR_INVALIDCALL;
2432 src_w = src_surface->resource.width;
2433 src_h = src_surface->resource.height;
2435 dst_w = dst_surface->resource.width;
2436 dst_h = dst_surface->resource.height;
2438 update_w = src_rect->right - src_rect->left;
2439 update_h = src_rect->bottom - src_rect->top;
2441 if (update_w > dst_w || dst_point->x > dst_w - update_w
2442 || update_h > dst_h || dst_point->y > dst_h - update_h)
2444 WARN("Destination out of bounds.\n");
2445 return WINED3DERR_INVALIDCALL;
2448 /* NPOT block sizes would be silly. */
2449 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS)
2450 && ((update_w & (src_format->block_width - 1) || update_h & (src_format->block_height - 1))
2451 && (src_w != update_w || dst_w != update_w || src_h != update_h || dst_h != update_h)))
2453 WARN("Update rect not block-aligned.\n");
2454 return WINED3DERR_INVALIDCALL;
2457 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
2458 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
2459 if (convert != WINED3D_CT_NONE || format.convert)
2461 RECT dst_rect = {dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h};
2462 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
2465 context = context_acquire(dst_surface->resource.device, NULL);
2466 gl_info = context->gl_info;
2468 /* Only load the surface for partial updates. For newly allocated texture
2469 * the texture wouldn't be the current location, and we'd upload zeroes
2470 * just to overwrite them again. */
2471 if (update_w == dst_w && update_h == dst_h)
2472 surface_prepare_texture(dst_surface, context, FALSE);
2473 else
2474 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
2475 surface_bind(dst_surface, context, FALSE);
2477 data.buffer_object = src_surface->pbo;
2478 data.addr = src_surface->resource.allocatedMemory;
2479 src_pitch = wined3d_surface_get_pitch(src_surface);
2481 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
2483 invalidate_active_texture(dst_surface->resource.device, context);
2485 context_release(context);
2487 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
2488 return WINED3D_OK;
2491 /* This call just allocates the texture, the caller is responsible for binding
2492 * the correct texture. */
2493 /* Context activation is done by the caller. */
2494 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2495 const struct wined3d_format *format, BOOL srgb)
2497 BOOL enable_client_storage = FALSE;
2498 GLsizei width = surface->pow2Width;
2499 GLsizei height = surface->pow2Height;
2500 const BYTE *mem = NULL;
2501 GLenum internal;
2503 if (srgb)
2505 internal = format->glGammaInternal;
2507 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2509 internal = format->rtInternal;
2511 else
2513 internal = format->glInternal;
2516 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
2518 height *= format->height_scale.numerator;
2519 height /= format->height_scale.denominator;
2522 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",
2523 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2524 internal, width, height, format->glFormat, format->glType);
2526 ENTER_GL();
2528 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2530 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2531 || !surface->resource.allocatedMemory)
2533 /* In some cases we want to disable client storage.
2534 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2535 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2536 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2537 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2539 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2540 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2541 surface->flags &= ~SFLAG_CLIENT;
2542 enable_client_storage = TRUE;
2544 else
2546 surface->flags |= SFLAG_CLIENT;
2548 /* Point OpenGL to our allocated texture memory. Do not use
2549 * resource.allocatedMemory here because it might point into a
2550 * PBO. Instead use heapMemory, but get the alignment right. */
2551 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2552 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2556 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2558 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2559 internal, width, height, 0, surface->resource.size, mem));
2560 checkGLcall("glCompressedTexImage2DARB");
2562 else
2564 glTexImage2D(surface->texture_target, surface->texture_level,
2565 internal, width, height, 0, format->glFormat, format->glType, mem);
2566 checkGLcall("glTexImage2D");
2569 if(enable_client_storage) {
2570 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2571 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2573 LEAVE_GL();
2576 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2577 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2578 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2579 /* GL locking is done by the caller */
2580 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2582 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2583 struct wined3d_renderbuffer_entry *entry;
2584 GLuint renderbuffer = 0;
2585 unsigned int src_width, src_height;
2586 unsigned int width, height;
2588 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2590 width = rt->pow2Width;
2591 height = rt->pow2Height;
2593 else
2595 width = surface->pow2Width;
2596 height = surface->pow2Height;
2599 src_width = surface->pow2Width;
2600 src_height = surface->pow2Height;
2602 /* A depth stencil smaller than the render target is not valid */
2603 if (width > src_width || height > src_height) return;
2605 /* Remove any renderbuffer set if the sizes match */
2606 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2607 || (width == src_width && height == src_height))
2609 surface->current_renderbuffer = NULL;
2610 return;
2613 /* Look if we've already got a renderbuffer of the correct dimensions */
2614 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2616 if (entry->width == width && entry->height == height)
2618 renderbuffer = entry->id;
2619 surface->current_renderbuffer = entry;
2620 break;
2624 if (!renderbuffer)
2626 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2627 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2628 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2629 surface->resource.format->glInternal, width, height);
2631 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2632 entry->width = width;
2633 entry->height = height;
2634 entry->id = renderbuffer;
2635 list_add_head(&surface->renderbuffers, &entry->entry);
2637 surface->current_renderbuffer = entry;
2640 checkGLcall("set_compatible_renderbuffer");
2643 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2645 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2647 TRACE("surface %p.\n", surface);
2649 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2651 ERR("Surface %p is not on a swapchain.\n", surface);
2652 return GL_NONE;
2655 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2657 if (swapchain->render_to_fbo)
2659 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2660 return GL_COLOR_ATTACHMENT0;
2662 TRACE("Returning GL_BACK\n");
2663 return GL_BACK;
2665 else if (surface == swapchain->front_buffer)
2667 TRACE("Returning GL_FRONT\n");
2668 return GL_FRONT;
2671 FIXME("Higher back buffer, returning GL_BACK\n");
2672 return GL_BACK;
2675 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2676 void surface_add_dirty_rect(struct wined3d_surface *surface, const struct wined3d_box *dirty_rect)
2678 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2680 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2681 /* No partial locking for textures yet. */
2682 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2684 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2685 if (dirty_rect)
2687 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->left);
2688 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->top);
2689 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->right);
2690 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->bottom);
2692 else
2694 surface->dirtyRect.left = 0;
2695 surface->dirtyRect.top = 0;
2696 surface->dirtyRect.right = surface->resource.width;
2697 surface->dirtyRect.bottom = surface->resource.height;
2700 /* if the container is a texture then mark it dirty. */
2701 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2703 TRACE("Passing to container.\n");
2704 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2708 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2710 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2711 BOOL ck_changed;
2713 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2715 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
2717 ERR("Not supported on scratch surfaces.\n");
2718 return WINED3DERR_INVALIDCALL;
2721 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2723 /* Reload if either the texture and sysmem have different ideas about the
2724 * color key, or the actual key values changed. */
2725 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2726 && (surface->gl_color_key.color_space_low_value != surface->src_blt_color_key.color_space_low_value
2727 || surface->gl_color_key.color_space_high_value != surface->src_blt_color_key.color_space_high_value)))
2729 TRACE("Reloading because of color keying\n");
2730 /* To perform the color key conversion we need a sysmem copy of
2731 * the surface. Make sure we have it. */
2733 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2734 /* Make sure the texture is reloaded because of the color key change,
2735 * this kills performance though :( */
2736 /* TODO: This is not necessarily needed with hw palettized texture support. */
2737 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2738 /* Switching color keying on / off may change the internal format. */
2739 if (ck_changed)
2740 surface_force_reload(surface);
2742 else if (!(surface->flags & flag))
2744 TRACE("Reloading because surface is dirty.\n");
2746 else
2748 TRACE("surface is already in texture\n");
2749 return WINED3D_OK;
2752 /* No partial locking for textures yet. */
2753 surface_load_location(surface, flag, NULL);
2754 surface_evict_sysmem(surface);
2756 return WINED3D_OK;
2759 /* See also float_16_to_32() in wined3d_private.h */
2760 static inline unsigned short float_32_to_16(const float *in)
2762 int exp = 0;
2763 float tmp = fabsf(*in);
2764 unsigned int mantissa;
2765 unsigned short ret;
2767 /* Deal with special numbers */
2768 if (*in == 0.0f)
2769 return 0x0000;
2770 if (isnan(*in))
2771 return 0x7c01;
2772 if (isinf(*in))
2773 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2775 if (tmp < powf(2, 10))
2779 tmp = tmp * 2.0f;
2780 exp--;
2781 } while (tmp < powf(2, 10));
2783 else if (tmp >= powf(2, 11))
2787 tmp /= 2.0f;
2788 exp++;
2789 } while (tmp >= powf(2, 11));
2792 mantissa = (unsigned int)tmp;
2793 if (tmp - mantissa >= 0.5f)
2794 ++mantissa; /* Round to nearest, away from zero. */
2796 exp += 10; /* Normalize the mantissa. */
2797 exp += 15; /* Exponent is encoded with excess 15. */
2799 if (exp > 30) /* too big */
2801 ret = 0x7c00; /* INF */
2803 else if (exp <= 0)
2805 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2806 while (exp <= 0)
2808 mantissa = mantissa >> 1;
2809 ++exp;
2811 ret = mantissa & 0x3ff;
2813 else
2815 ret = (exp << 10) | (mantissa & 0x3ff);
2818 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2819 return ret;
2822 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2824 ULONG refcount;
2826 TRACE("Surface %p, container %p of type %#x.\n",
2827 surface, surface->container.u.base, surface->container.type);
2829 switch (surface->container.type)
2831 case WINED3D_CONTAINER_TEXTURE:
2832 return wined3d_texture_incref(surface->container.u.texture);
2834 case WINED3D_CONTAINER_SWAPCHAIN:
2835 return wined3d_swapchain_incref(surface->container.u.swapchain);
2837 default:
2838 ERR("Unhandled container type %#x.\n", surface->container.type);
2839 case WINED3D_CONTAINER_NONE:
2840 break;
2843 refcount = InterlockedIncrement(&surface->resource.ref);
2844 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2846 return refcount;
2849 /* Do not call while under the GL lock. */
2850 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2852 ULONG refcount;
2854 TRACE("Surface %p, container %p of type %#x.\n",
2855 surface, surface->container.u.base, surface->container.type);
2857 switch (surface->container.type)
2859 case WINED3D_CONTAINER_TEXTURE:
2860 return wined3d_texture_decref(surface->container.u.texture);
2862 case WINED3D_CONTAINER_SWAPCHAIN:
2863 return wined3d_swapchain_decref(surface->container.u.swapchain);
2865 default:
2866 ERR("Unhandled container type %#x.\n", surface->container.type);
2867 case WINED3D_CONTAINER_NONE:
2868 break;
2871 refcount = InterlockedDecrement(&surface->resource.ref);
2872 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2874 if (!refcount)
2876 surface_cleanup(surface);
2877 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2879 TRACE("Destroyed surface %p.\n", surface);
2880 HeapFree(GetProcessHeap(), 0, surface);
2883 return refcount;
2886 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2888 return resource_set_priority(&surface->resource, priority);
2891 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2893 return resource_get_priority(&surface->resource);
2896 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2898 TRACE("surface %p.\n", surface);
2900 if (!surface->resource.device->d3d_initialized)
2902 ERR("D3D not initialized.\n");
2903 return;
2906 surface_internal_preload(surface, SRGB_ANY);
2909 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2911 TRACE("surface %p.\n", surface);
2913 return surface->resource.parent;
2916 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2918 TRACE("surface %p.\n", surface);
2920 return &surface->resource;
2923 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2925 TRACE("surface %p, flags %#x.\n", surface, flags);
2927 switch (flags)
2929 case WINEDDGBS_CANBLT:
2930 case WINEDDGBS_ISBLTDONE:
2931 return WINED3D_OK;
2933 default:
2934 return WINED3DERR_INVALIDCALL;
2938 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2940 TRACE("surface %p, flags %#x.\n", surface, flags);
2942 /* XXX: DDERR_INVALIDSURFACETYPE */
2944 switch (flags)
2946 case WINEDDGFS_CANFLIP:
2947 case WINEDDGFS_ISFLIPDONE:
2948 return WINED3D_OK;
2950 default:
2951 return WINED3DERR_INVALIDCALL;
2955 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2957 TRACE("surface %p.\n", surface);
2959 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2960 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2963 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2965 TRACE("surface %p.\n", surface);
2967 surface->flags &= ~SFLAG_LOST;
2968 return WINED3D_OK;
2971 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2973 TRACE("surface %p, palette %p.\n", surface, palette);
2975 if (surface->palette == palette)
2977 TRACE("Nop palette change.\n");
2978 return WINED3D_OK;
2981 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2982 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2984 surface->palette = palette;
2986 if (palette)
2988 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2989 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
2991 surface->surface_ops->surface_realize_palette(surface);
2994 return WINED3D_OK;
2997 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
2998 DWORD flags, const struct wined3d_color_key *color_key)
3000 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3002 if (flags & WINEDDCKEY_COLORSPACE)
3004 FIXME(" colorkey value not supported (%08x) !\n", flags);
3005 return WINED3DERR_INVALIDCALL;
3008 /* Dirtify the surface, but only if a key was changed. */
3009 if (color_key)
3011 switch (flags & ~WINEDDCKEY_COLORSPACE)
3013 case WINEDDCKEY_DESTBLT:
3014 surface->dst_blt_color_key = *color_key;
3015 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3016 break;
3018 case WINEDDCKEY_DESTOVERLAY:
3019 surface->dst_overlay_color_key = *color_key;
3020 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3021 break;
3023 case WINEDDCKEY_SRCOVERLAY:
3024 surface->src_overlay_color_key = *color_key;
3025 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3026 break;
3028 case WINEDDCKEY_SRCBLT:
3029 surface->src_blt_color_key = *color_key;
3030 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3031 break;
3034 else
3036 switch (flags & ~WINEDDCKEY_COLORSPACE)
3038 case WINEDDCKEY_DESTBLT:
3039 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3040 break;
3042 case WINEDDCKEY_DESTOVERLAY:
3043 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3044 break;
3046 case WINEDDCKEY_SRCOVERLAY:
3047 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3048 break;
3050 case WINEDDCKEY_SRCBLT:
3051 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3052 break;
3056 return WINED3D_OK;
3059 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3061 TRACE("surface %p.\n", surface);
3063 return surface->palette;
3066 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3068 const struct wined3d_format *format = surface->resource.format;
3069 DWORD pitch;
3071 TRACE("surface %p.\n", surface);
3073 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
3075 /* Since compressed formats are block based, pitch means the amount of
3076 * bytes to the next row of block rather than the next row of pixels. */
3077 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3078 pitch = row_block_count * format->block_byte_count;
3080 else
3082 unsigned char alignment = surface->resource.device->surface_alignment;
3083 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3084 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3087 TRACE("Returning %u.\n", pitch);
3089 return pitch;
3092 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3094 TRACE("surface %p, mem %p.\n", surface, mem);
3096 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3098 WARN("Surface is locked or the DC is in use.\n");
3099 return WINED3DERR_INVALIDCALL;
3102 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
3103 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3105 ERR("Not supported on render targets.\n");
3106 return WINED3DERR_INVALIDCALL;
3109 if (mem && mem != surface->resource.allocatedMemory)
3111 void *release = NULL;
3113 /* Do I have to copy the old surface content? */
3114 if (surface->flags & SFLAG_DIBSECTION)
3116 DeleteDC(surface->hDC);
3117 DeleteObject(surface->dib.DIBsection);
3118 surface->dib.bitmap_data = NULL;
3119 surface->resource.allocatedMemory = NULL;
3120 surface->hDC = NULL;
3121 surface->flags &= ~SFLAG_DIBSECTION;
3123 else if (!(surface->flags & SFLAG_USERPTR))
3125 release = surface->resource.heapMemory;
3126 surface->resource.heapMemory = NULL;
3128 surface->resource.allocatedMemory = mem;
3129 surface->flags |= SFLAG_USERPTR;
3131 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
3132 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3134 /* For client textures OpenGL has to be notified. */
3135 if (surface->flags & SFLAG_CLIENT)
3136 surface_release_client_storage(surface);
3138 /* Now free the old memory if any. */
3139 HeapFree(GetProcessHeap(), 0, release);
3141 else if (surface->flags & SFLAG_USERPTR)
3143 /* HeapMemory should be NULL already. */
3144 if (surface->resource.heapMemory)
3145 ERR("User pointer surface has heap memory allocated.\n");
3147 if (!mem)
3149 surface->resource.allocatedMemory = NULL;
3150 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
3152 if (surface->flags & SFLAG_CLIENT)
3153 surface_release_client_storage(surface);
3155 surface_prepare_system_memory(surface);
3158 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3161 return WINED3D_OK;
3164 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3166 LONG w, h;
3168 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3170 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3172 WARN("Not an overlay surface.\n");
3173 return WINEDDERR_NOTAOVERLAYSURFACE;
3176 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3177 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3178 surface->overlay_destrect.left = x;
3179 surface->overlay_destrect.top = y;
3180 surface->overlay_destrect.right = x + w;
3181 surface->overlay_destrect.bottom = y + h;
3183 surface_draw_overlay(surface);
3185 return WINED3D_OK;
3188 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3190 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3192 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3194 TRACE("Not an overlay surface.\n");
3195 return WINEDDERR_NOTAOVERLAYSURFACE;
3198 if (!surface->overlay_dest)
3200 TRACE("Overlay not visible.\n");
3201 *x = 0;
3202 *y = 0;
3203 return WINEDDERR_OVERLAYNOTVISIBLE;
3206 *x = surface->overlay_destrect.left;
3207 *y = surface->overlay_destrect.top;
3209 TRACE("Returning position %d, %d.\n", *x, *y);
3211 return WINED3D_OK;
3214 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3215 DWORD flags, struct wined3d_surface *ref)
3217 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3219 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3221 TRACE("Not an overlay surface.\n");
3222 return WINEDDERR_NOTAOVERLAYSURFACE;
3225 return WINED3D_OK;
3228 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3229 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3231 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3232 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3234 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3236 WARN("Not an overlay surface.\n");
3237 return WINEDDERR_NOTAOVERLAYSURFACE;
3239 else if (!dst_surface)
3241 WARN("Dest surface is NULL.\n");
3242 return WINED3DERR_INVALIDCALL;
3245 if (src_rect)
3247 surface->overlay_srcrect = *src_rect;
3249 else
3251 surface->overlay_srcrect.left = 0;
3252 surface->overlay_srcrect.top = 0;
3253 surface->overlay_srcrect.right = surface->resource.width;
3254 surface->overlay_srcrect.bottom = surface->resource.height;
3257 if (dst_rect)
3259 surface->overlay_destrect = *dst_rect;
3261 else
3263 surface->overlay_destrect.left = 0;
3264 surface->overlay_destrect.top = 0;
3265 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3266 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3269 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3271 surface->overlay_dest = NULL;
3272 list_remove(&surface->overlay_entry);
3275 if (flags & WINEDDOVER_SHOW)
3277 if (surface->overlay_dest != dst_surface)
3279 surface->overlay_dest = dst_surface;
3280 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3283 else if (flags & WINEDDOVER_HIDE)
3285 /* tests show that the rectangles are erased on hide */
3286 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3287 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3288 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3289 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3290 surface->overlay_dest = NULL;
3293 surface_draw_overlay(surface);
3295 return WINED3D_OK;
3298 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
3299 UINT width, UINT height, enum wined3d_format_id format_id,
3300 enum wined3d_multisample_type multisample_type, UINT multisample_quality)
3302 struct wined3d_device *device = surface->resource.device;
3303 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3304 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
3305 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height);
3307 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u.\n",
3308 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type);
3310 if (!resource_size)
3311 return WINED3DERR_INVALIDCALL;
3313 if (device->d3d_initialized)
3314 surface->resource.resource_ops->resource_unload(&surface->resource);
3316 if (surface->flags & SFLAG_DIBSECTION)
3318 DeleteDC(surface->hDC);
3319 DeleteObject(surface->dib.DIBsection);
3320 surface->dib.bitmap_data = NULL;
3321 surface->flags &= ~SFLAG_DIBSECTION;
3324 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_USERPTR);
3325 surface->resource.allocatedMemory = NULL;
3326 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
3327 surface->resource.heapMemory = NULL;
3329 surface->resource.width = width;
3330 surface->resource.height = height;
3331 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
3332 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
3334 surface->pow2Width = width;
3335 surface->pow2Height = height;
3337 else
3339 surface->pow2Width = surface->pow2Height = 1;
3340 while (surface->pow2Width < width)
3341 surface->pow2Width <<= 1;
3342 while (surface->pow2Height < height)
3343 surface->pow2Height <<= 1;
3346 if (surface->pow2Width != width || surface->pow2Height != height)
3347 surface->flags |= SFLAG_NONPOW2;
3348 else
3349 surface->flags &= ~SFLAG_NONPOW2;
3351 surface->resource.format = format;
3352 surface->resource.multisample_type = multisample_type;
3353 surface->resource.multisample_quality = multisample_quality;
3354 surface->resource.size = resource_size;
3356 if (!surface_init_sysmem(surface))
3357 return E_OUTOFMEMORY;
3359 return WINED3D_OK;
3362 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3363 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3365 unsigned short *dst_s;
3366 const float *src_f;
3367 unsigned int x, y;
3369 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3371 for (y = 0; y < h; ++y)
3373 src_f = (const float *)(src + y * pitch_in);
3374 dst_s = (unsigned short *) (dst + y * pitch_out);
3375 for (x = 0; x < w; ++x)
3377 dst_s[x] = float_32_to_16(src_f + x);
3382 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3383 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3385 static const unsigned char convert_5to8[] =
3387 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3388 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3389 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3390 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3392 static const unsigned char convert_6to8[] =
3394 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3395 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3396 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3397 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3398 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3399 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3400 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3401 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3403 unsigned int x, y;
3405 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3407 for (y = 0; y < h; ++y)
3409 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3410 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3411 for (x = 0; x < w; ++x)
3413 WORD pixel = src_line[x];
3414 dst_line[x] = 0xff000000
3415 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3416 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3417 | convert_5to8[(pixel & 0x001f)];
3422 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3423 * in both cases we're just setting the X / Alpha channel to 0xff. */
3424 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3425 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3427 unsigned int x, y;
3429 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3431 for (y = 0; y < h; ++y)
3433 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3434 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3436 for (x = 0; x < w; ++x)
3438 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3443 static inline BYTE cliptobyte(int x)
3445 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3448 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3449 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3451 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3452 unsigned int x, y;
3454 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3456 for (y = 0; y < h; ++y)
3458 const BYTE *src_line = src + y * pitch_in;
3459 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3460 for (x = 0; x < w; ++x)
3462 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3463 * C = Y - 16; D = U - 128; E = V - 128;
3464 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3465 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3466 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3467 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3468 * U and V are shared between the pixels. */
3469 if (!(x & 1)) /* For every even pixel, read new U and V. */
3471 d = (int) src_line[1] - 128;
3472 e = (int) src_line[3] - 128;
3473 r2 = 409 * e + 128;
3474 g2 = - 100 * d - 208 * e + 128;
3475 b2 = 516 * d + 128;
3477 c2 = 298 * ((int) src_line[0] - 16);
3478 dst_line[x] = 0xff000000
3479 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3480 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3481 | cliptobyte((c2 + b2) >> 8); /* blue */
3482 /* Scale RGB values to 0..255 range,
3483 * then clip them if still not in range (may be negative),
3484 * then shift them within DWORD if necessary. */
3485 src_line += 2;
3490 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3491 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3493 unsigned int x, y;
3494 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3496 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3498 for (y = 0; y < h; ++y)
3500 const BYTE *src_line = src + y * pitch_in;
3501 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3502 for (x = 0; x < w; ++x)
3504 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3505 * C = Y - 16; D = U - 128; E = V - 128;
3506 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3507 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3508 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3509 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3510 * U and V are shared between the pixels. */
3511 if (!(x & 1)) /* For every even pixel, read new U and V. */
3513 d = (int) src_line[1] - 128;
3514 e = (int) src_line[3] - 128;
3515 r2 = 409 * e + 128;
3516 g2 = - 100 * d - 208 * e + 128;
3517 b2 = 516 * d + 128;
3519 c2 = 298 * ((int) src_line[0] - 16);
3520 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3521 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3522 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3523 /* Scale RGB values to 0..255 range,
3524 * then clip them if still not in range (may be negative),
3525 * then shift them within DWORD if necessary. */
3526 src_line += 2;
3531 struct d3dfmt_convertor_desc
3533 enum wined3d_format_id from, to;
3534 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3537 static const struct d3dfmt_convertor_desc convertors[] =
3539 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3540 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3541 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3542 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3543 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3544 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3547 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3548 enum wined3d_format_id to)
3550 unsigned int i;
3552 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3554 if (convertors[i].from == from && convertors[i].to == to)
3555 return &convertors[i];
3558 return NULL;
3561 /*****************************************************************************
3562 * surface_convert_format
3564 * Creates a duplicate of a surface in a different format. Is used by Blt to
3565 * blit between surfaces with different formats.
3567 * Parameters
3568 * source: Source surface
3569 * fmt: Requested destination format
3571 *****************************************************************************/
3572 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3574 struct wined3d_map_desc src_map, dst_map;
3575 const struct d3dfmt_convertor_desc *conv;
3576 struct wined3d_surface *ret = NULL;
3577 HRESULT hr;
3579 conv = find_convertor(source->resource.format->id, to_fmt);
3580 if (!conv)
3582 FIXME("Cannot find a conversion function from format %s to %s.\n",
3583 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3584 return NULL;
3587 wined3d_surface_create(source->resource.device, source->resource.width,
3588 source->resource.height, to_fmt, 0 /* level */, 0 /* usage */, WINED3D_POOL_SCRATCH,
3589 WINED3D_MULTISAMPLE_NONE /* TODO: Multisampled conversion */, 0 /* MultiSampleQuality */,
3590 source->surface_type, WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD,
3591 NULL /* parent */, &wined3d_null_parent_ops, &ret);
3592 if (!ret)
3594 ERR("Failed to create a destination surface for conversion.\n");
3595 return NULL;
3598 memset(&src_map, 0, sizeof(src_map));
3599 memset(&dst_map, 0, sizeof(dst_map));
3601 hr = wined3d_surface_map(source, &src_map, NULL, WINED3DLOCK_READONLY);
3602 if (FAILED(hr))
3604 ERR("Failed to lock the source surface.\n");
3605 wined3d_surface_decref(ret);
3606 return NULL;
3608 hr = wined3d_surface_map(ret, &dst_map, NULL, WINED3DLOCK_READONLY);
3609 if (FAILED(hr))
3611 ERR("Failed to lock the destination surface.\n");
3612 wined3d_surface_unmap(source);
3613 wined3d_surface_decref(ret);
3614 return NULL;
3617 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
3618 source->resource.width, source->resource.height);
3620 wined3d_surface_unmap(ret);
3621 wined3d_surface_unmap(source);
3623 return ret;
3626 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3627 unsigned int bpp, UINT pitch, DWORD color)
3629 BYTE *first;
3630 int x, y;
3632 /* Do first row */
3634 #define COLORFILL_ROW(type) \
3635 do { \
3636 type *d = (type *)buf; \
3637 for (x = 0; x < width; ++x) \
3638 d[x] = (type)color; \
3639 } while(0)
3641 switch (bpp)
3643 case 1:
3644 COLORFILL_ROW(BYTE);
3645 break;
3647 case 2:
3648 COLORFILL_ROW(WORD);
3649 break;
3651 case 3:
3653 BYTE *d = buf;
3654 for (x = 0; x < width; ++x, d += 3)
3656 d[0] = (color ) & 0xFF;
3657 d[1] = (color >> 8) & 0xFF;
3658 d[2] = (color >> 16) & 0xFF;
3660 break;
3662 case 4:
3663 COLORFILL_ROW(DWORD);
3664 break;
3666 default:
3667 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3668 return WINED3DERR_NOTAVAILABLE;
3671 #undef COLORFILL_ROW
3673 /* Now copy first row. */
3674 first = buf;
3675 for (y = 1; y < height; ++y)
3677 buf += pitch;
3678 memcpy(buf, first, width * bpp);
3681 return WINED3D_OK;
3684 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3686 TRACE("surface %p.\n", surface);
3688 if (!(surface->flags & SFLAG_LOCKED))
3690 WARN("Trying to unmap unmapped surface.\n");
3691 return WINEDDERR_NOTLOCKED;
3693 surface->flags &= ~SFLAG_LOCKED;
3695 surface->surface_ops->surface_unmap(surface);
3697 return WINED3D_OK;
3700 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3701 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
3703 const struct wined3d_format *format = surface->resource.format;
3705 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
3706 surface, map_desc, wine_dbgstr_rect(rect), flags);
3708 if (surface->flags & SFLAG_LOCKED)
3710 WARN("Surface is already mapped.\n");
3711 return WINED3DERR_INVALIDCALL;
3713 if ((format->flags & WINED3DFMT_FLAG_BLOCKS)
3714 && rect && (rect->left || rect->top
3715 || rect->right != surface->resource.width
3716 || rect->bottom != surface->resource.height))
3718 UINT width_mask = format->block_width - 1;
3719 UINT height_mask = format->block_height - 1;
3721 if ((rect->left & width_mask) || (rect->right & width_mask)
3722 || (rect->top & height_mask) || (rect->bottom & height_mask))
3724 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3725 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3727 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
3728 return WINED3DERR_INVALIDCALL;
3732 surface->flags |= SFLAG_LOCKED;
3734 if (!(surface->flags & SFLAG_LOCKABLE))
3735 WARN("Trying to lock unlockable surface.\n");
3737 /* Performance optimization: Count how often a surface is mapped, if it is
3738 * mapped regularly do not throw away the system memory copy. This avoids
3739 * the need to download the surface from OpenGL all the time. The surface
3740 * is still downloaded if the OpenGL texture is changed. */
3741 if (!(surface->flags & SFLAG_DYNLOCK))
3743 if (++surface->lockCount > MAXLOCKCOUNT)
3745 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3746 surface->flags |= SFLAG_DYNLOCK;
3750 surface->surface_ops->surface_map(surface, rect, flags);
3752 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3753 map_desc->row_pitch = surface->resource.width * format->byte_count;
3754 else
3755 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
3756 map_desc->slice_pitch = 0;
3758 if (!rect)
3760 map_desc->data = surface->resource.allocatedMemory;
3761 surface->lockedRect.left = 0;
3762 surface->lockedRect.top = 0;
3763 surface->lockedRect.right = surface->resource.width;
3764 surface->lockedRect.bottom = surface->resource.height;
3766 else
3768 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3770 /* Compressed textures are block based, so calculate the offset of
3771 * the block that contains the top-left pixel of the locked rectangle. */
3772 map_desc->data = surface->resource.allocatedMemory
3773 + ((rect->top / format->block_height) * map_desc->row_pitch)
3774 + ((rect->left / format->block_width) * format->block_byte_count);
3776 else
3778 map_desc->data = surface->resource.allocatedMemory
3779 + (map_desc->row_pitch * rect->top)
3780 + (rect->left * format->byte_count);
3782 surface->lockedRect.left = rect->left;
3783 surface->lockedRect.top = rect->top;
3784 surface->lockedRect.right = rect->right;
3785 surface->lockedRect.bottom = rect->bottom;
3788 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3789 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
3791 return WINED3D_OK;
3794 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3796 struct wined3d_map_desc map;
3797 HRESULT hr;
3799 TRACE("surface %p, dc %p.\n", surface, dc);
3801 if (surface->flags & SFLAG_USERPTR)
3803 ERR("Not supported on surfaces with application-provided memory.\n");
3804 return WINEDDERR_NODC;
3807 /* Give more detailed info for ddraw. */
3808 if (surface->flags & SFLAG_DCINUSE)
3809 return WINEDDERR_DCALREADYCREATED;
3811 /* Can't GetDC if the surface is locked. */
3812 if (surface->flags & SFLAG_LOCKED)
3813 return WINED3DERR_INVALIDCALL;
3815 /* Create a DIB section if there isn't a dc yet. */
3816 if (!surface->hDC)
3818 if (surface->flags & SFLAG_CLIENT)
3820 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3821 surface_release_client_storage(surface);
3823 hr = surface_create_dib_section(surface);
3824 if (FAILED(hr))
3825 return WINED3DERR_INVALIDCALL;
3827 /* Use the DIB section from now on if we are not using a PBO. */
3828 if (!(surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)))
3830 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
3831 surface->resource.heapMemory = NULL;
3832 surface->resource.allocatedMemory = surface->dib.bitmap_data;
3836 /* Map the surface. */
3837 hr = wined3d_surface_map(surface, &map, NULL, 0);
3838 if (FAILED(hr))
3840 ERR("Map failed, hr %#x.\n", hr);
3841 return hr;
3844 /* Sync the DIB with the PBO. This can't be done earlier because Map()
3845 * activates the allocatedMemory. */
3846 if (surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM))
3847 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
3849 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3850 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3852 /* GetDC on palettized formats is unsupported in D3D9, and the method
3853 * is missing in D3D8, so this should only be used for DX <=7
3854 * surfaces (with non-device palettes). */
3855 const PALETTEENTRY *pal = NULL;
3857 if (surface->palette)
3859 pal = surface->palette->palents;
3861 else
3863 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3864 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3866 if (dds_primary && dds_primary->palette)
3867 pal = dds_primary->palette->palents;
3870 if (pal)
3872 RGBQUAD col[256];
3873 unsigned int i;
3875 for (i = 0; i < 256; ++i)
3877 col[i].rgbRed = pal[i].peRed;
3878 col[i].rgbGreen = pal[i].peGreen;
3879 col[i].rgbBlue = pal[i].peBlue;
3880 col[i].rgbReserved = 0;
3882 SetDIBColorTable(surface->hDC, 0, 256, col);
3886 surface->flags |= SFLAG_DCINUSE;
3888 *dc = surface->hDC;
3889 TRACE("Returning dc %p.\n", *dc);
3891 return WINED3D_OK;
3894 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3896 TRACE("surface %p, dc %p.\n", surface, dc);
3898 if (!(surface->flags & SFLAG_DCINUSE))
3899 return WINEDDERR_NODC;
3901 if (surface->hDC != dc)
3903 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3904 dc, surface->hDC);
3905 return WINEDDERR_NODC;
3908 /* Copy the contents of the DIB over to the PBO. */
3909 if ((surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)) && surface->resource.allocatedMemory)
3910 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3912 /* We locked first, so unlock now. */
3913 wined3d_surface_unmap(surface);
3915 surface->flags &= ~SFLAG_DCINUSE;
3917 return WINED3D_OK;
3920 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3922 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3924 if (flags)
3926 static UINT once;
3927 if (!once++)
3928 FIXME("Ignoring flags %#x.\n", flags);
3929 else
3930 WARN("Ignoring flags %#x.\n", flags);
3933 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
3935 ERR("Not supported on swapchain surfaces.\n");
3936 return WINEDDERR_NOTFLIPPABLE;
3939 /* Flipping is only supported on render targets and overlays. */
3940 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
3942 WARN("Tried to flip a non-render target, non-overlay surface.\n");
3943 return WINEDDERR_NOTFLIPPABLE;
3946 flip_surface(surface, override);
3948 /* Update overlays if they're visible. */
3949 if ((surface->resource.usage & WINED3DUSAGE_OVERLAY) && surface->overlay_dest)
3950 return surface_draw_overlay(surface);
3952 return WINED3D_OK;
3955 /* Do not call while under the GL lock. */
3956 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3958 struct wined3d_device *device = surface->resource.device;
3960 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3962 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3964 struct wined3d_texture *texture = surface->container.u.texture;
3966 TRACE("Passing to container (%p).\n", texture);
3967 texture->texture_ops->texture_preload(texture, srgb);
3969 else
3971 struct wined3d_context *context;
3973 TRACE("(%p) : About to load surface\n", surface);
3975 /* TODO: Use already acquired context when possible. */
3976 context = context_acquire(device, NULL);
3978 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3980 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
3982 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3983 GLclampf tmp;
3984 tmp = 0.9f;
3985 ENTER_GL();
3986 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3987 LEAVE_GL();
3990 context_release(context);
3994 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3996 if (!surface->resource.allocatedMemory)
3998 if (!surface->resource.heapMemory)
4000 if (!(surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
4001 surface->resource.size + RESOURCE_ALIGNMENT)))
4003 ERR("Failed to allocate memory.\n");
4004 return FALSE;
4007 else if (!(surface->flags & SFLAG_CLIENT))
4009 ERR("Surface %p has heapMemory %p and flags %#x.\n",
4010 surface, surface->resource.heapMemory, surface->flags);
4013 surface->resource.allocatedMemory =
4014 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
4016 else
4018 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
4021 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
4023 return TRUE;
4026 /* Read the framebuffer back into the surface */
4027 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
4029 struct wined3d_device *device = surface->resource.device;
4030 const struct wined3d_gl_info *gl_info;
4031 struct wined3d_context *context;
4032 BYTE *mem;
4033 GLint fmt;
4034 GLint type;
4035 BYTE *row, *top, *bottom;
4036 int i;
4037 BOOL bpp;
4038 RECT local_rect;
4039 BOOL srcIsUpsideDown;
4040 GLint rowLen = 0;
4041 GLint skipPix = 0;
4042 GLint skipRow = 0;
4044 context = context_acquire(device, surface);
4045 context_apply_blit_state(context, device);
4046 gl_info = context->gl_info;
4048 ENTER_GL();
4050 /* Select the correct read buffer, and give some debug output.
4051 * There is no need to keep track of the current read buffer or reset it, every part of the code
4052 * that reads sets the read buffer as desired.
4054 if (surface_is_offscreen(surface))
4056 /* Mapping the primary render target which is not on a swapchain.
4057 * Read from the back buffer. */
4058 TRACE("Mapping offscreen render target.\n");
4059 glReadBuffer(device->offscreenBuffer);
4060 srcIsUpsideDown = TRUE;
4062 else
4064 /* Onscreen surfaces are always part of a swapchain */
4065 GLenum buffer = surface_get_gl_buffer(surface);
4066 TRACE("Mapping %#x buffer.\n", buffer);
4067 glReadBuffer(buffer);
4068 checkGLcall("glReadBuffer");
4069 srcIsUpsideDown = FALSE;
4072 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
4073 if (!rect)
4075 local_rect.left = 0;
4076 local_rect.top = 0;
4077 local_rect.right = surface->resource.width;
4078 local_rect.bottom = surface->resource.height;
4080 else
4082 local_rect = *rect;
4084 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
4086 switch (surface->resource.format->id)
4088 case WINED3DFMT_P8_UINT:
4090 if (primary_render_target_is_p8(device))
4092 /* In case of P8 render targets the index is stored in the alpha component */
4093 fmt = GL_ALPHA;
4094 type = GL_UNSIGNED_BYTE;
4095 mem = dest;
4096 bpp = surface->resource.format->byte_count;
4098 else
4100 /* GL can't return palettized data, so read ARGB pixels into a
4101 * separate block of memory and convert them into palettized format
4102 * in software. Slow, but if the app means to use palettized render
4103 * targets and locks it...
4105 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4106 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4107 * for the color channels when palettizing the colors.
4109 fmt = GL_RGB;
4110 type = GL_UNSIGNED_BYTE;
4111 pitch *= 3;
4112 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4113 if (!mem)
4115 ERR("Out of memory\n");
4116 LEAVE_GL();
4117 return;
4119 bpp = surface->resource.format->byte_count * 3;
4122 break;
4124 default:
4125 mem = dest;
4126 fmt = surface->resource.format->glFormat;
4127 type = surface->resource.format->glType;
4128 bpp = surface->resource.format->byte_count;
4131 if (surface->flags & SFLAG_PBO)
4133 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4134 checkGLcall("glBindBufferARB");
4135 if (mem)
4137 ERR("mem not null for pbo -- unexpected\n");
4138 mem = NULL;
4142 /* Save old pixel store pack state */
4143 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4144 checkGLcall("glGetIntegerv");
4145 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4146 checkGLcall("glGetIntegerv");
4147 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4148 checkGLcall("glGetIntegerv");
4150 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4151 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4152 checkGLcall("glPixelStorei");
4153 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4154 checkGLcall("glPixelStorei");
4155 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4156 checkGLcall("glPixelStorei");
4158 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4159 local_rect.right - local_rect.left,
4160 local_rect.bottom - local_rect.top,
4161 fmt, type, mem);
4162 checkGLcall("glReadPixels");
4164 /* Reset previous pixel store pack state */
4165 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4166 checkGLcall("glPixelStorei");
4167 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4168 checkGLcall("glPixelStorei");
4169 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4170 checkGLcall("glPixelStorei");
4172 if (surface->flags & SFLAG_PBO)
4174 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4175 checkGLcall("glBindBufferARB");
4177 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4178 * to get a pointer to it and perform the flipping in software. This is a lot
4179 * faster than calling glReadPixels for each line. In case we want more speed
4180 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4181 if (!srcIsUpsideDown)
4183 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4184 checkGLcall("glBindBufferARB");
4186 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4187 checkGLcall("glMapBufferARB");
4191 /* TODO: Merge this with the palettization loop below for P8 targets */
4192 if(!srcIsUpsideDown) {
4193 UINT len, off;
4194 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4195 Flip the lines in software */
4196 len = (local_rect.right - local_rect.left) * bpp;
4197 off = local_rect.left * bpp;
4199 row = HeapAlloc(GetProcessHeap(), 0, len);
4200 if(!row) {
4201 ERR("Out of memory\n");
4202 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4203 HeapFree(GetProcessHeap(), 0, mem);
4204 LEAVE_GL();
4205 return;
4208 top = mem + pitch * local_rect.top;
4209 bottom = mem + pitch * (local_rect.bottom - 1);
4210 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4211 memcpy(row, top + off, len);
4212 memcpy(top + off, bottom + off, len);
4213 memcpy(bottom + off, row, len);
4214 top += pitch;
4215 bottom -= pitch;
4217 HeapFree(GetProcessHeap(), 0, row);
4219 /* Unmap the temp PBO buffer */
4220 if (surface->flags & SFLAG_PBO)
4222 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4223 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4227 LEAVE_GL();
4228 context_release(context);
4230 /* For P8 textures we need to perform an inverse palette lookup. This is
4231 * done by searching for a palette index which matches the RGB value.
4232 * Note this isn't guaranteed to work when there are multiple entries for
4233 * the same color but we have no choice. In case of P8 render targets,
4234 * the index is stored in the alpha component so no conversion is needed. */
4235 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4237 const PALETTEENTRY *pal = NULL;
4238 DWORD width = pitch / 3;
4239 int x, y, c;
4241 if (surface->palette)
4243 pal = surface->palette->palents;
4245 else
4247 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4248 HeapFree(GetProcessHeap(), 0, mem);
4249 return;
4252 for(y = local_rect.top; y < local_rect.bottom; y++) {
4253 for(x = local_rect.left; x < local_rect.right; x++) {
4254 /* start lines pixels */
4255 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4256 const BYTE *green = blue + 1;
4257 const BYTE *red = green + 1;
4259 for(c = 0; c < 256; c++) {
4260 if(*red == pal[c].peRed &&
4261 *green == pal[c].peGreen &&
4262 *blue == pal[c].peBlue)
4264 *((BYTE *) dest + y * width + x) = c;
4265 break;
4270 HeapFree(GetProcessHeap(), 0, mem);
4274 /* Read the framebuffer contents into a texture. Note that this function
4275 * doesn't do any kind of flipping. Using this on an onscreen surface will
4276 * result in a flipped D3D texture. */
4277 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
4279 struct wined3d_device *device = surface->resource.device;
4280 struct wined3d_context *context;
4282 context = context_acquire(device, surface);
4283 device_invalidate_state(device, STATE_FRAMEBUFFER);
4285 surface_prepare_texture(surface, context, srgb);
4286 surface_bind_and_dirtify(surface, context, srgb);
4288 TRACE("Reading back offscreen render target %p.\n", surface);
4290 ENTER_GL();
4292 if (surface_is_offscreen(surface))
4293 glReadBuffer(device->offscreenBuffer);
4294 else
4295 glReadBuffer(surface_get_gl_buffer(surface));
4296 checkGLcall("glReadBuffer");
4298 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4299 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4300 checkGLcall("glCopyTexSubImage2D");
4302 LEAVE_GL();
4304 context_release(context);
4307 /* Context activation is done by the caller. */
4308 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4309 struct wined3d_context *context, BOOL srgb)
4311 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4312 enum wined3d_conversion_type convert;
4313 struct wined3d_format format;
4315 if (surface->flags & alloc_flag) return;
4317 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4318 if (convert != WINED3D_CT_NONE || format.convert)
4319 surface->flags |= SFLAG_CONVERTED;
4320 else surface->flags &= ~SFLAG_CONVERTED;
4322 surface_bind_and_dirtify(surface, context, srgb);
4323 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4324 surface->flags |= alloc_flag;
4327 /* Context activation is done by the caller. */
4328 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4330 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4332 struct wined3d_texture *texture = surface->container.u.texture;
4333 UINT sub_count = texture->level_count * texture->layer_count;
4334 UINT i;
4336 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4338 for (i = 0; i < sub_count; ++i)
4340 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4341 surface_prepare_texture_internal(s, context, srgb);
4344 return;
4347 surface_prepare_texture_internal(surface, context, srgb);
4350 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4352 if (multisample)
4354 if (surface->rb_multisample)
4355 return;
4357 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4358 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4359 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4360 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4361 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4363 else
4365 if (surface->rb_resolved)
4366 return;
4368 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4369 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4370 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4371 surface->pow2Width, surface->pow2Height);
4372 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4376 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4377 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4379 struct wined3d_device *device = surface->resource.device;
4380 UINT pitch = wined3d_surface_get_pitch(surface);
4381 const struct wined3d_gl_info *gl_info;
4382 struct wined3d_context *context;
4383 RECT local_rect;
4384 UINT w, h;
4386 surface_get_rect(surface, rect, &local_rect);
4388 mem += local_rect.top * pitch + local_rect.left * bpp;
4389 w = local_rect.right - local_rect.left;
4390 h = local_rect.bottom - local_rect.top;
4392 /* Activate the correct context for the render target */
4393 context = context_acquire(device, surface);
4394 context_apply_blit_state(context, device);
4395 gl_info = context->gl_info;
4397 ENTER_GL();
4399 if (!surface_is_offscreen(surface))
4401 GLenum buffer = surface_get_gl_buffer(surface);
4402 TRACE("Unlocking %#x buffer.\n", buffer);
4403 context_set_draw_buffer(context, buffer);
4405 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4406 glPixelZoom(1.0f, -1.0f);
4408 else
4410 /* Primary offscreen render target */
4411 TRACE("Offscreen render target.\n");
4412 context_set_draw_buffer(context, device->offscreenBuffer);
4414 glPixelZoom(1.0f, 1.0f);
4417 glRasterPos3i(local_rect.left, local_rect.top, 1);
4418 checkGLcall("glRasterPos3i");
4420 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4421 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4423 if (surface->flags & SFLAG_PBO)
4425 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4426 checkGLcall("glBindBufferARB");
4429 glDrawPixels(w, h, fmt, type, mem);
4430 checkGLcall("glDrawPixels");
4432 if (surface->flags & SFLAG_PBO)
4434 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4435 checkGLcall("glBindBufferARB");
4438 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4439 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4441 LEAVE_GL();
4443 if (wined3d_settings.strict_draw_ordering
4444 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4445 && surface->container.u.swapchain->front_buffer == surface))
4446 wglFlush();
4448 context_release(context);
4451 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck, BOOL use_texturing,
4452 struct wined3d_format *format, enum wined3d_conversion_type *conversion_type)
4454 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4455 const struct wined3d_device *device = surface->resource.device;
4456 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4457 BOOL blit_supported = FALSE;
4459 /* Copy the default values from the surface. Below we might perform fixups */
4460 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4461 *format = *surface->resource.format;
4462 *conversion_type = WINED3D_CT_NONE;
4464 /* Ok, now look if we have to do any conversion */
4465 switch (surface->resource.format->id)
4467 case WINED3DFMT_P8_UINT:
4468 /* Below the call to blit_supported is disabled for Wine 1.2
4469 * because the function isn't operating correctly yet. At the
4470 * moment 8-bit blits are handled in software and if certain GL
4471 * extensions are around, surface conversion is performed at
4472 * upload time. The blit_supported call recognizes it as a
4473 * destination fixup. This type of upload 'fixup' and 8-bit to
4474 * 8-bit blits need to be handled by the blit_shader.
4475 * TODO: get rid of this #if 0. */
4476 #if 0
4477 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4478 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4479 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4480 #endif
4481 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4483 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4484 * texturing. Further also use conversion in case of color keying.
4485 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4486 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4487 * conflicts with this.
4489 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4490 || colorkey_active || !use_texturing)
4492 format->glFormat = GL_RGBA;
4493 format->glInternal = GL_RGBA;
4494 format->glType = GL_UNSIGNED_BYTE;
4495 format->conv_byte_count = 4;
4496 if (colorkey_active)
4497 *conversion_type = WINED3D_CT_PALETTED_CK;
4498 else
4499 *conversion_type = WINED3D_CT_PALETTED;
4501 break;
4503 case WINED3DFMT_B2G3R3_UNORM:
4504 /* **********************
4505 GL_UNSIGNED_BYTE_3_3_2
4506 ********************** */
4507 if (colorkey_active) {
4508 /* This texture format will never be used.. So do not care about color keying
4509 up until the point in time it will be needed :-) */
4510 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4512 break;
4514 case WINED3DFMT_B5G6R5_UNORM:
4515 if (colorkey_active)
4517 *conversion_type = WINED3D_CT_CK_565;
4518 format->glFormat = GL_RGBA;
4519 format->glInternal = GL_RGB5_A1;
4520 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4521 format->conv_byte_count = 2;
4523 break;
4525 case WINED3DFMT_B5G5R5X1_UNORM:
4526 if (colorkey_active)
4528 *conversion_type = WINED3D_CT_CK_5551;
4529 format->glFormat = GL_BGRA;
4530 format->glInternal = GL_RGB5_A1;
4531 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4532 format->conv_byte_count = 2;
4534 break;
4536 case WINED3DFMT_B8G8R8_UNORM:
4537 if (colorkey_active)
4539 *conversion_type = WINED3D_CT_CK_RGB24;
4540 format->glFormat = GL_RGBA;
4541 format->glInternal = GL_RGBA8;
4542 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4543 format->conv_byte_count = 4;
4545 break;
4547 case WINED3DFMT_B8G8R8X8_UNORM:
4548 if (colorkey_active)
4550 *conversion_type = WINED3D_CT_RGB32_888;
4551 format->glFormat = GL_RGBA;
4552 format->glInternal = GL_RGBA8;
4553 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4554 format->conv_byte_count = 4;
4556 break;
4558 case WINED3DFMT_B8G8R8A8_UNORM:
4559 if (colorkey_active)
4561 *conversion_type = WINED3D_CT_CK_ARGB32;
4562 format->conv_byte_count = 4;
4564 break;
4566 default:
4567 break;
4570 if (*conversion_type != WINED3D_CT_NONE)
4572 format->rtInternal = format->glInternal;
4573 format->glGammaInternal = format->glInternal;
4576 return WINED3D_OK;
4579 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
4581 /* FIXME: Is this really how color keys are supposed to work? I think it
4582 * makes more sense to compare the individual channels. */
4583 return color >= color_key->color_space_low_value
4584 && color <= color_key->color_space_high_value;
4587 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4589 const struct wined3d_device *device = surface->resource.device;
4590 const struct wined3d_palette *pal = surface->palette;
4591 BOOL index_in_alpha = FALSE;
4592 unsigned int i;
4594 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4595 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4596 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4597 * duplicate entries. Store the color key in the unused alpha component to speed the
4598 * download up and to make conversion unneeded. */
4599 index_in_alpha = primary_render_target_is_p8(device);
4601 if (!pal)
4603 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4604 if (index_in_alpha)
4606 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4607 * there's no palette at this time. */
4608 for (i = 0; i < 256; i++) table[i][3] = i;
4611 else
4613 TRACE("Using surface palette %p\n", pal);
4614 /* Get the surface's palette */
4615 for (i = 0; i < 256; ++i)
4617 table[i][0] = pal->palents[i].peRed;
4618 table[i][1] = pal->palents[i].peGreen;
4619 table[i][2] = pal->palents[i].peBlue;
4621 /* When index_in_alpha is set the palette index is stored in the
4622 * alpha component. In case of a readback we can then read
4623 * GL_ALPHA. Color keying is handled in BltOverride using a
4624 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4625 * color key itself is passed to glAlphaFunc in other cases the
4626 * alpha component of pixels that should be masked away is set to 0. */
4627 if (index_in_alpha)
4628 table[i][3] = i;
4629 else if (colorkey && color_in_range(&surface->src_blt_color_key, i))
4630 table[i][3] = 0x00;
4631 else if (pal->flags & WINEDDPCAPS_ALPHA)
4632 table[i][3] = pal->palents[i].peFlags;
4633 else
4634 table[i][3] = 0xFF;
4639 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
4640 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
4642 const BYTE *source;
4643 BYTE *dest;
4645 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
4646 src, dst, pitch, width, height, outpitch, conversion_type, surface);
4648 switch (conversion_type)
4650 case WINED3D_CT_NONE:
4652 memcpy(dst, src, pitch * height);
4653 break;
4656 case WINED3D_CT_PALETTED:
4657 case WINED3D_CT_PALETTED_CK:
4659 BYTE table[256][4];
4660 unsigned int x, y;
4662 d3dfmt_p8_init_palette(surface, table, (conversion_type == WINED3D_CT_PALETTED_CK));
4664 for (y = 0; y < height; y++)
4666 source = src + pitch * y;
4667 dest = dst + outpitch * y;
4668 /* This is an 1 bpp format, using the width here is fine */
4669 for (x = 0; x < width; x++) {
4670 BYTE color = *source++;
4671 *dest++ = table[color][0];
4672 *dest++ = table[color][1];
4673 *dest++ = table[color][2];
4674 *dest++ = table[color][3];
4678 break;
4680 case WINED3D_CT_CK_565:
4682 /* Converting the 565 format in 5551 packed to emulate color-keying.
4684 Note : in all these conversion, it would be best to average the averaging
4685 pixels to get the color of the pixel that will be color-keyed to
4686 prevent 'color bleeding'. This will be done later on if ever it is
4687 too visible.
4689 Note2: Nvidia documents say that their driver does not support alpha + color keying
4690 on the same surface and disables color keying in such a case
4692 unsigned int x, y;
4693 const WORD *Source;
4694 WORD *Dest;
4696 TRACE("Color keyed 565\n");
4698 for (y = 0; y < height; y++) {
4699 Source = (const WORD *)(src + y * pitch);
4700 Dest = (WORD *) (dst + y * outpitch);
4701 for (x = 0; x < width; x++ ) {
4702 WORD color = *Source++;
4703 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4704 if (!color_in_range(&surface->src_blt_color_key, color))
4705 *Dest |= 0x0001;
4706 Dest++;
4710 break;
4712 case WINED3D_CT_CK_5551:
4714 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4715 unsigned int x, y;
4716 const WORD *Source;
4717 WORD *Dest;
4718 TRACE("Color keyed 5551\n");
4719 for (y = 0; y < height; y++) {
4720 Source = (const WORD *)(src + y * pitch);
4721 Dest = (WORD *) (dst + y * outpitch);
4722 for (x = 0; x < width; x++ ) {
4723 WORD color = *Source++;
4724 *Dest = color;
4725 if (!color_in_range(&surface->src_blt_color_key, color))
4726 *Dest |= (1 << 15);
4727 else
4728 *Dest &= ~(1 << 15);
4729 Dest++;
4733 break;
4735 case WINED3D_CT_CK_RGB24:
4737 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4738 unsigned int x, y;
4739 for (y = 0; y < height; y++)
4741 source = src + pitch * y;
4742 dest = dst + outpitch * y;
4743 for (x = 0; x < width; x++) {
4744 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4745 DWORD dstcolor = color << 8;
4746 if (!color_in_range(&surface->src_blt_color_key, color))
4747 dstcolor |= 0xff;
4748 *(DWORD*)dest = dstcolor;
4749 source += 3;
4750 dest += 4;
4754 break;
4756 case WINED3D_CT_RGB32_888:
4758 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4759 unsigned int x, y;
4760 for (y = 0; y < height; y++)
4762 source = src + pitch * y;
4763 dest = dst + outpitch * y;
4764 for (x = 0; x < width; x++) {
4765 DWORD color = 0xffffff & *(const DWORD*)source;
4766 DWORD dstcolor = color << 8;
4767 if (!color_in_range(&surface->src_blt_color_key, color))
4768 dstcolor |= 0xff;
4769 *(DWORD*)dest = dstcolor;
4770 source += 4;
4771 dest += 4;
4775 break;
4777 case WINED3D_CT_CK_ARGB32:
4779 unsigned int x, y;
4780 for (y = 0; y < height; ++y)
4782 source = src + pitch * y;
4783 dest = dst + outpitch * y;
4784 for (x = 0; x < width; ++x)
4786 DWORD color = *(const DWORD *)source;
4787 if (color_in_range(&surface->src_blt_color_key, color))
4788 color &= ~0xff000000;
4789 *(DWORD*)dest = color;
4790 source += 4;
4791 dest += 4;
4795 break;
4797 default:
4798 ERR("Unsupported conversion type %#x.\n", conversion_type);
4800 return WINED3D_OK;
4803 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4805 /* Flip the surface contents */
4806 /* Flip the DC */
4808 HDC tmp;
4809 tmp = front->hDC;
4810 front->hDC = back->hDC;
4811 back->hDC = tmp;
4814 /* Flip the DIBsection */
4816 HBITMAP tmp = front->dib.DIBsection;
4817 front->dib.DIBsection = back->dib.DIBsection;
4818 back->dib.DIBsection = tmp;
4821 /* Flip the surface data */
4823 void* tmp;
4825 tmp = front->dib.bitmap_data;
4826 front->dib.bitmap_data = back->dib.bitmap_data;
4827 back->dib.bitmap_data = tmp;
4829 tmp = front->resource.allocatedMemory;
4830 front->resource.allocatedMemory = back->resource.allocatedMemory;
4831 back->resource.allocatedMemory = tmp;
4833 tmp = front->resource.heapMemory;
4834 front->resource.heapMemory = back->resource.heapMemory;
4835 back->resource.heapMemory = tmp;
4838 /* Flip the PBO */
4840 GLuint tmp_pbo = front->pbo;
4841 front->pbo = back->pbo;
4842 back->pbo = tmp_pbo;
4845 /* Flip the opengl texture */
4847 GLuint tmp;
4849 tmp = back->texture_name;
4850 back->texture_name = front->texture_name;
4851 front->texture_name = tmp;
4853 tmp = back->texture_name_srgb;
4854 back->texture_name_srgb = front->texture_name_srgb;
4855 front->texture_name_srgb = tmp;
4857 tmp = back->rb_multisample;
4858 back->rb_multisample = front->rb_multisample;
4859 front->rb_multisample = tmp;
4861 tmp = back->rb_resolved;
4862 back->rb_resolved = front->rb_resolved;
4863 front->rb_resolved = tmp;
4865 resource_unload(&back->resource);
4866 resource_unload(&front->resource);
4870 DWORD tmp_flags = back->flags;
4871 back->flags = front->flags;
4872 front->flags = tmp_flags;
4876 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4877 * pixel copy calls. */
4878 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4879 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4881 struct wined3d_device *device = dst_surface->resource.device;
4882 float xrel, yrel;
4883 UINT row;
4884 struct wined3d_context *context;
4885 BOOL upsidedown = FALSE;
4886 RECT dst_rect = *dst_rect_in;
4888 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4889 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4891 if(dst_rect.top > dst_rect.bottom) {
4892 UINT tmp = dst_rect.bottom;
4893 dst_rect.bottom = dst_rect.top;
4894 dst_rect.top = tmp;
4895 upsidedown = TRUE;
4898 context = context_acquire(device, src_surface);
4899 context_apply_blit_state(context, device);
4900 surface_internal_preload(dst_surface, SRGB_RGB);
4901 ENTER_GL();
4903 /* Bind the target texture */
4904 context_bind_texture(context, dst_surface->texture_target, dst_surface->texture_name);
4905 if (surface_is_offscreen(src_surface))
4907 TRACE("Reading from an offscreen target\n");
4908 upsidedown = !upsidedown;
4909 glReadBuffer(device->offscreenBuffer);
4911 else
4913 glReadBuffer(surface_get_gl_buffer(src_surface));
4915 checkGLcall("glReadBuffer");
4917 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4918 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4920 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4922 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4924 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4925 ERR("Texture filtering not supported in direct blit.\n");
4927 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4928 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4930 ERR("Texture filtering not supported in direct blit\n");
4933 if (upsidedown
4934 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4935 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4937 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4939 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4940 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4941 src_rect->left, src_surface->resource.height - src_rect->bottom,
4942 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4944 else
4946 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4947 /* I have to process this row by row to swap the image,
4948 * otherwise it would be upside down, so stretching in y direction
4949 * doesn't cost extra time
4951 * However, stretching in x direction can be avoided if not necessary
4953 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4954 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4956 /* Well, that stuff works, but it's very slow.
4957 * find a better way instead
4959 UINT col;
4961 for (col = dst_rect.left; col < dst_rect.right; ++col)
4963 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4964 dst_rect.left + col /* x offset */, row /* y offset */,
4965 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4968 else
4970 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4971 dst_rect.left /* x offset */, row /* y offset */,
4972 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4976 checkGLcall("glCopyTexSubImage2D");
4978 LEAVE_GL();
4979 context_release(context);
4981 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4982 * path is never entered
4984 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4987 /* Uses the hardware to stretch and flip the image */
4988 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4989 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4991 struct wined3d_device *device = dst_surface->resource.device;
4992 struct wined3d_swapchain *src_swapchain = NULL;
4993 GLuint src, backup = 0;
4994 float left, right, top, bottom; /* Texture coordinates */
4995 UINT fbwidth = src_surface->resource.width;
4996 UINT fbheight = src_surface->resource.height;
4997 struct wined3d_context *context;
4998 GLenum drawBuffer = GL_BACK;
4999 GLenum texture_target;
5000 BOOL noBackBufferBackup;
5001 BOOL src_offscreen;
5002 BOOL upsidedown = FALSE;
5003 RECT dst_rect = *dst_rect_in;
5005 TRACE("Using hwstretch blit\n");
5006 /* Activate the Proper context for reading from the source surface, set it up for blitting */
5007 context = context_acquire(device, src_surface);
5008 context_apply_blit_state(context, device);
5009 surface_internal_preload(dst_surface, SRGB_RGB);
5011 src_offscreen = surface_is_offscreen(src_surface);
5012 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
5013 if (!noBackBufferBackup && !src_surface->texture_name)
5015 /* Get it a description */
5016 surface_internal_preload(src_surface, SRGB_RGB);
5018 ENTER_GL();
5020 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
5021 * This way we don't have to wait for the 2nd readback to finish to leave this function.
5023 if (context->aux_buffers >= 2)
5025 /* Got more than one aux buffer? Use the 2nd aux buffer */
5026 drawBuffer = GL_AUX1;
5028 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
5030 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
5031 drawBuffer = GL_AUX0;
5034 if(noBackBufferBackup) {
5035 glGenTextures(1, &backup);
5036 checkGLcall("glGenTextures");
5037 context_bind_texture(context, GL_TEXTURE_2D, backup);
5038 texture_target = GL_TEXTURE_2D;
5039 } else {
5040 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
5041 * we are reading from the back buffer, the backup can be used as source texture
5043 texture_target = src_surface->texture_target;
5044 context_bind_texture(context, texture_target, src_surface->texture_name);
5045 glEnable(texture_target);
5046 checkGLcall("glEnable(texture_target)");
5048 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
5049 src_surface->flags &= ~SFLAG_INTEXTURE;
5052 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
5053 * glCopyTexSubImage is a bit picky about the parameters we pass to it
5055 if(dst_rect.top > dst_rect.bottom) {
5056 UINT tmp = dst_rect.bottom;
5057 dst_rect.bottom = dst_rect.top;
5058 dst_rect.top = tmp;
5059 upsidedown = TRUE;
5062 if (src_offscreen)
5064 TRACE("Reading from an offscreen target\n");
5065 upsidedown = !upsidedown;
5066 glReadBuffer(device->offscreenBuffer);
5068 else
5070 glReadBuffer(surface_get_gl_buffer(src_surface));
5073 /* TODO: Only back up the part that will be overwritten */
5074 glCopyTexSubImage2D(texture_target, 0,
5075 0, 0 /* read offsets */,
5076 0, 0,
5077 fbwidth,
5078 fbheight);
5080 checkGLcall("glCopyTexSubImage2D");
5082 /* No issue with overriding these - the sampler is dirty due to blit usage */
5083 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
5084 wined3d_gl_mag_filter(magLookup, filter));
5085 checkGLcall("glTexParameteri");
5086 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5087 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
5088 checkGLcall("glTexParameteri");
5090 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5091 src_swapchain = src_surface->container.u.swapchain;
5092 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5094 src = backup ? backup : src_surface->texture_name;
5096 else
5098 glReadBuffer(GL_FRONT);
5099 checkGLcall("glReadBuffer(GL_FRONT)");
5101 glGenTextures(1, &src);
5102 checkGLcall("glGenTextures(1, &src)");
5103 context_bind_texture(context, GL_TEXTURE_2D, src);
5105 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5106 * out for power of 2 sizes
5108 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5109 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5110 checkGLcall("glTexImage2D");
5111 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5112 0, 0 /* read offsets */,
5113 0, 0,
5114 fbwidth,
5115 fbheight);
5117 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5118 checkGLcall("glTexParameteri");
5119 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5120 checkGLcall("glTexParameteri");
5122 glReadBuffer(GL_BACK);
5123 checkGLcall("glReadBuffer(GL_BACK)");
5125 if(texture_target != GL_TEXTURE_2D) {
5126 glDisable(texture_target);
5127 glEnable(GL_TEXTURE_2D);
5128 texture_target = GL_TEXTURE_2D;
5131 checkGLcall("glEnd and previous");
5133 left = src_rect->left;
5134 right = src_rect->right;
5136 if (!upsidedown)
5138 top = src_surface->resource.height - src_rect->top;
5139 bottom = src_surface->resource.height - src_rect->bottom;
5141 else
5143 top = src_surface->resource.height - src_rect->bottom;
5144 bottom = src_surface->resource.height - src_rect->top;
5147 if (src_surface->flags & SFLAG_NORMCOORD)
5149 left /= src_surface->pow2Width;
5150 right /= src_surface->pow2Width;
5151 top /= src_surface->pow2Height;
5152 bottom /= src_surface->pow2Height;
5155 /* draw the source texture stretched and upside down. The correct surface is bound already */
5156 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5157 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5159 context_set_draw_buffer(context, drawBuffer);
5160 glReadBuffer(drawBuffer);
5162 glBegin(GL_QUADS);
5163 /* bottom left */
5164 glTexCoord2f(left, bottom);
5165 glVertex2i(0, 0);
5167 /* top left */
5168 glTexCoord2f(left, top);
5169 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5171 /* top right */
5172 glTexCoord2f(right, top);
5173 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5175 /* bottom right */
5176 glTexCoord2f(right, bottom);
5177 glVertex2i(dst_rect.right - dst_rect.left, 0);
5178 glEnd();
5179 checkGLcall("glEnd and previous");
5181 if (texture_target != dst_surface->texture_target)
5183 glDisable(texture_target);
5184 glEnable(dst_surface->texture_target);
5185 texture_target = dst_surface->texture_target;
5188 /* Now read the stretched and upside down image into the destination texture */
5189 context_bind_texture(context, texture_target, dst_surface->texture_name);
5190 glCopyTexSubImage2D(texture_target,
5192 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5193 0, 0, /* We blitted the image to the origin */
5194 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5195 checkGLcall("glCopyTexSubImage2D");
5197 if(drawBuffer == GL_BACK) {
5198 /* Write the back buffer backup back */
5199 if(backup) {
5200 if(texture_target != GL_TEXTURE_2D) {
5201 glDisable(texture_target);
5202 glEnable(GL_TEXTURE_2D);
5203 texture_target = GL_TEXTURE_2D;
5205 context_bind_texture(context, GL_TEXTURE_2D, backup);
5207 else
5209 if (texture_target != src_surface->texture_target)
5211 glDisable(texture_target);
5212 glEnable(src_surface->texture_target);
5213 texture_target = src_surface->texture_target;
5215 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5218 glBegin(GL_QUADS);
5219 /* top left */
5220 glTexCoord2f(0.0f, 0.0f);
5221 glVertex2i(0, fbheight);
5223 /* bottom left */
5224 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5225 glVertex2i(0, 0);
5227 /* bottom right */
5228 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5229 (float)fbheight / (float)src_surface->pow2Height);
5230 glVertex2i(fbwidth, 0);
5232 /* top right */
5233 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5234 glVertex2i(fbwidth, fbheight);
5235 glEnd();
5237 glDisable(texture_target);
5238 checkGLcall("glDisable(texture_target)");
5240 /* Cleanup */
5241 if (src != src_surface->texture_name && src != backup)
5243 glDeleteTextures(1, &src);
5244 checkGLcall("glDeleteTextures(1, &src)");
5246 if(backup) {
5247 glDeleteTextures(1, &backup);
5248 checkGLcall("glDeleteTextures(1, &backup)");
5251 LEAVE_GL();
5253 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5255 context_release(context);
5257 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5258 * path is never entered
5260 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5263 /* Front buffer coordinates are always full screen coordinates, but our GL
5264 * drawable is limited to the window's client area. The sysmem and texture
5265 * copies do have the full screen size. Note that GL has a bottom-left
5266 * origin, while D3D has a top-left origin. */
5267 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5269 UINT drawable_height;
5271 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5272 && surface == surface->container.u.swapchain->front_buffer)
5274 POINT offset = {0, 0};
5275 RECT windowsize;
5277 ScreenToClient(window, &offset);
5278 OffsetRect(rect, offset.x, offset.y);
5280 GetClientRect(window, &windowsize);
5281 drawable_height = windowsize.bottom - windowsize.top;
5283 else
5285 drawable_height = surface->resource.height;
5288 rect->top = drawable_height - rect->top;
5289 rect->bottom = drawable_height - rect->bottom;
5292 static void surface_blt_to_drawable(const struct wined3d_device *device,
5293 enum wined3d_texture_filter_type filter, BOOL color_key,
5294 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5295 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5297 struct wined3d_context *context;
5298 RECT src_rect, dst_rect;
5300 src_rect = *src_rect_in;
5301 dst_rect = *dst_rect_in;
5303 /* Make sure the surface is up-to-date. This should probably use
5304 * surface_load_location() and worry about the destination surface too,
5305 * unless we're overwriting it completely. */
5306 surface_internal_preload(src_surface, SRGB_RGB);
5308 /* Activate the destination context, set it up for blitting */
5309 context = context_acquire(device, dst_surface);
5310 context_apply_blit_state(context, device);
5312 if (!surface_is_offscreen(dst_surface))
5313 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5315 device->blitter->set_shader(device->blit_priv, context, src_surface);
5317 ENTER_GL();
5319 if (color_key)
5321 glEnable(GL_ALPHA_TEST);
5322 checkGLcall("glEnable(GL_ALPHA_TEST)");
5324 /* When the primary render target uses P8, the alpha component
5325 * contains the palette index. Which means that the colorkey is one of
5326 * the palette entries. In other cases pixels that should be masked
5327 * away have alpha set to 0. */
5328 if (primary_render_target_is_p8(device))
5329 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->src_blt_color_key.color_space_low_value / 256.0f);
5330 else
5331 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5332 checkGLcall("glAlphaFunc");
5334 else
5336 glDisable(GL_ALPHA_TEST);
5337 checkGLcall("glDisable(GL_ALPHA_TEST)");
5340 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5342 if (color_key)
5344 glDisable(GL_ALPHA_TEST);
5345 checkGLcall("glDisable(GL_ALPHA_TEST)");
5348 LEAVE_GL();
5350 /* Leave the opengl state valid for blitting */
5351 device->blitter->unset_shader(context->gl_info);
5353 if (wined3d_settings.strict_draw_ordering
5354 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5355 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5356 wglFlush(); /* Flush to ensure ordering across contexts. */
5358 context_release(context);
5361 /* Do not call while under the GL lock. */
5362 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
5364 struct wined3d_device *device = s->resource.device;
5365 const struct blit_shader *blitter;
5367 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5368 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5369 if (!blitter)
5371 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5372 return WINED3DERR_INVALIDCALL;
5375 return blitter->color_fill(device, s, rect, color);
5378 /* Do not call while under the GL lock. */
5379 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5380 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5381 enum wined3d_texture_filter_type filter)
5383 struct wined3d_device *device = dst_surface->resource.device;
5384 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5385 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5387 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5388 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5389 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
5391 /* Get the swapchain. One of the surfaces has to be a primary surface */
5392 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
5394 WARN("Destination is in sysmem, rejecting gl blt\n");
5395 return WINED3DERR_INVALIDCALL;
5398 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5399 dstSwapchain = dst_surface->container.u.swapchain;
5401 if (src_surface)
5403 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
5405 WARN("Src is in sysmem, rejecting gl blt\n");
5406 return WINED3DERR_INVALIDCALL;
5409 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5410 srcSwapchain = src_surface->container.u.swapchain;
5413 /* Early sort out of cases where no render target is used */
5414 if (!dstSwapchain && !srcSwapchain
5415 && src_surface != device->fb.render_targets[0]
5416 && dst_surface != device->fb.render_targets[0])
5418 TRACE("No surface is render target, not using hardware blit.\n");
5419 return WINED3DERR_INVALIDCALL;
5422 /* No destination color keying supported */
5423 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5425 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5426 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5427 return WINED3DERR_INVALIDCALL;
5430 if (dstSwapchain && dstSwapchain == srcSwapchain)
5432 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5433 return WINED3DERR_INVALIDCALL;
5436 if (dstSwapchain && srcSwapchain)
5438 FIXME("Implement hardware blit between two different swapchains\n");
5439 return WINED3DERR_INVALIDCALL;
5442 if (dstSwapchain)
5444 /* Handled with regular texture -> swapchain blit */
5445 if (src_surface == device->fb.render_targets[0])
5446 TRACE("Blit from active render target to a swapchain\n");
5448 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5450 FIXME("Implement blit from a swapchain to the active render target\n");
5451 return WINED3DERR_INVALIDCALL;
5454 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5456 /* Blit from render target to texture */
5457 BOOL stretchx;
5459 /* P8 read back is not implemented */
5460 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5461 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5463 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5464 return WINED3DERR_INVALIDCALL;
5467 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5469 TRACE("Color keying not supported by frame buffer to texture blit\n");
5470 return WINED3DERR_INVALIDCALL;
5471 /* Destination color key is checked above */
5474 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5475 stretchx = TRUE;
5476 else
5477 stretchx = FALSE;
5479 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5480 * flip the image nor scale it.
5482 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5483 * -> If the app wants a image width an unscaled width, copy it line per line
5484 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5485 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5486 * back buffer. This is slower than reading line per line, thus not used for flipping
5487 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5488 * pixel by pixel. */
5489 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5490 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5492 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
5493 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
5495 else
5497 TRACE("Using hardware stretching to flip / stretch the texture.\n");
5498 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
5501 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5503 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5504 dst_surface->resource.allocatedMemory = NULL;
5505 dst_surface->resource.heapMemory = NULL;
5507 else
5509 dst_surface->flags &= ~SFLAG_INSYSMEM;
5512 return WINED3D_OK;
5514 else if (src_surface)
5516 /* Blit from offscreen surface to render target */
5517 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
5518 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5520 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5522 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5523 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5524 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5526 FIXME("Unsupported blit operation falling back to software\n");
5527 return WINED3DERR_INVALIDCALL;
5530 /* Color keying: Check if we have to do a color keyed blt,
5531 * and if not check if a color key is activated.
5533 * Just modify the color keying parameters in the surface and restore them afterwards
5534 * The surface keeps track of the color key last used to load the opengl surface.
5535 * PreLoad will catch the change to the flags and color key and reload if necessary.
5537 if (flags & WINEDDBLT_KEYSRC)
5539 /* Use color key from surface */
5541 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5543 /* Use color key from DDBltFx */
5544 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5545 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
5547 else
5549 /* Do not use color key */
5550 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5553 surface_blt_to_drawable(device, filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5554 src_surface, src_rect, dst_surface, dst_rect);
5556 /* Restore the color key parameters */
5557 src_surface->CKeyFlags = oldCKeyFlags;
5558 src_surface->src_blt_color_key = old_blt_key;
5560 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5562 return WINED3D_OK;
5565 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5566 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5567 return WINED3DERR_INVALIDCALL;
5570 /* GL locking is done by the caller */
5571 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5572 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5574 struct wined3d_device *device = surface->resource.device;
5575 const struct wined3d_gl_info *gl_info = context->gl_info;
5576 GLint compare_mode = GL_NONE;
5577 struct blt_info info;
5578 GLint old_binding = 0;
5579 RECT rect;
5581 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5583 glDisable(GL_CULL_FACE);
5584 glDisable(GL_BLEND);
5585 glDisable(GL_ALPHA_TEST);
5586 glDisable(GL_SCISSOR_TEST);
5587 glDisable(GL_STENCIL_TEST);
5588 glEnable(GL_DEPTH_TEST);
5589 glDepthFunc(GL_ALWAYS);
5590 glDepthMask(GL_TRUE);
5591 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5592 glViewport(x, y, w, h);
5593 glDepthRange(0.0, 1.0);
5595 SetRect(&rect, 0, h, w, 0);
5596 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5597 context_active_texture(context, context->gl_info, 0);
5598 glGetIntegerv(info.binding, &old_binding);
5599 glBindTexture(info.bind_target, texture);
5600 if (gl_info->supported[ARB_SHADOW])
5602 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5603 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5606 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5607 gl_info, info.tex_type, &surface->ds_current_size);
5609 glBegin(GL_TRIANGLE_STRIP);
5610 glTexCoord3fv(info.coords[0]);
5611 glVertex2f(-1.0f, -1.0f);
5612 glTexCoord3fv(info.coords[1]);
5613 glVertex2f(1.0f, -1.0f);
5614 glTexCoord3fv(info.coords[2]);
5615 glVertex2f(-1.0f, 1.0f);
5616 glTexCoord3fv(info.coords[3]);
5617 glVertex2f(1.0f, 1.0f);
5618 glEnd();
5620 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5621 glBindTexture(info.bind_target, old_binding);
5623 glPopAttrib();
5625 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5628 void surface_modify_ds_location(struct wined3d_surface *surface,
5629 DWORD location, UINT w, UINT h)
5631 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5633 if (location & ~(SFLAG_LOCATIONS | SFLAG_DISCARDED))
5634 FIXME("Invalid location (%#x) specified.\n", location);
5636 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5637 || (!(surface->flags & SFLAG_INTEXTURE) && (location & SFLAG_INTEXTURE)))
5639 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5641 TRACE("Passing to container.\n");
5642 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5646 surface->ds_current_size.cx = w;
5647 surface->ds_current_size.cy = h;
5648 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_DISCARDED);
5649 surface->flags |= location;
5652 /* Context activation is done by the caller. */
5653 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5655 struct wined3d_device *device = surface->resource.device;
5656 GLsizei w, h;
5658 TRACE("surface %p, new location %#x.\n", surface, location);
5660 /* TODO: Make this work for modes other than FBO */
5661 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5663 if (!(surface->flags & location))
5665 w = surface->ds_current_size.cx;
5666 h = surface->ds_current_size.cy;
5667 surface->ds_current_size.cx = 0;
5668 surface->ds_current_size.cy = 0;
5670 else
5672 w = surface->resource.width;
5673 h = surface->resource.height;
5676 if (surface->ds_current_size.cx == surface->resource.width
5677 && surface->ds_current_size.cy == surface->resource.height)
5679 TRACE("Location (%#x) is already up to date.\n", location);
5680 return;
5683 if (surface->current_renderbuffer)
5685 FIXME("Not supported with fixed up depth stencil.\n");
5686 return;
5689 if (surface->flags & SFLAG_DISCARDED)
5691 TRACE("Surface was discarded, no need copy data.\n");
5692 switch (location)
5694 case SFLAG_INTEXTURE:
5695 surface_prepare_texture(surface, context, FALSE);
5696 break;
5697 case SFLAG_INRB_MULTISAMPLE:
5698 surface_prepare_rb(surface, context->gl_info, TRUE);
5699 break;
5700 case SFLAG_INDRAWABLE:
5701 /* Nothing to do */
5702 break;
5703 default:
5704 FIXME("Unhandled location %#x\n", location);
5706 surface->flags &= ~SFLAG_DISCARDED;
5707 surface->flags |= location;
5708 surface->ds_current_size.cx = surface->resource.width;
5709 surface->ds_current_size.cy = surface->resource.height;
5710 return;
5713 if (!(surface->flags & SFLAG_LOCATIONS))
5715 FIXME("No up to date depth stencil location.\n");
5716 surface->flags |= location;
5717 surface->ds_current_size.cx = surface->resource.width;
5718 surface->ds_current_size.cy = surface->resource.height;
5719 return;
5722 if (location == SFLAG_INTEXTURE)
5724 GLint old_binding = 0;
5725 GLenum bind_target;
5727 /* The render target is allowed to be smaller than the depth/stencil
5728 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5729 * than the offscreen surface. Don't overwrite the offscreen surface
5730 * with undefined data. */
5731 w = min(w, context->swapchain->desc.backbuffer_width);
5732 h = min(h, context->swapchain->desc.backbuffer_height);
5734 TRACE("Copying onscreen depth buffer to depth texture.\n");
5736 ENTER_GL();
5738 if (!device->depth_blt_texture)
5740 glGenTextures(1, &device->depth_blt_texture);
5743 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5744 * directly on the FBO texture. That's because we need to flip. */
5745 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5746 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5747 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5749 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5750 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5752 else
5754 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5755 bind_target = GL_TEXTURE_2D;
5757 glBindTexture(bind_target, device->depth_blt_texture);
5758 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5759 * internal format, because the internal format might include stencil
5760 * data. In principle we should copy stencil data as well, but unless
5761 * the driver supports stencil export it's hard to do, and doesn't
5762 * seem to be needed in practice. If the hardware doesn't support
5763 * writing stencil data, the glCopyTexImage2D() call might trigger
5764 * software fallbacks. */
5765 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5766 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5767 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5768 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5769 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5770 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5771 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5772 glBindTexture(bind_target, old_binding);
5774 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5775 NULL, surface, SFLAG_INTEXTURE);
5776 context_set_draw_buffer(context, GL_NONE);
5777 glReadBuffer(GL_NONE);
5779 /* Do the actual blit */
5780 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5781 checkGLcall("depth_blt");
5783 context_invalidate_state(context, STATE_FRAMEBUFFER);
5785 LEAVE_GL();
5787 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5789 else if (location == SFLAG_INDRAWABLE)
5791 TRACE("Copying depth texture to onscreen depth buffer.\n");
5793 ENTER_GL();
5795 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5796 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5797 surface_depth_blt(surface, context, surface->texture_name,
5798 0, surface->pow2Height - h, w, h, surface->texture_target);
5799 checkGLcall("depth_blt");
5801 context_invalidate_state(context, STATE_FRAMEBUFFER);
5803 LEAVE_GL();
5805 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5807 else
5809 ERR("Invalid location (%#x) specified.\n", location);
5812 surface->flags |= location;
5813 surface->ds_current_size.cx = surface->resource.width;
5814 surface->ds_current_size.cy = surface->resource.height;
5817 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5819 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5820 struct wined3d_surface *overlay;
5822 TRACE("surface %p, location %s, persistent %#x.\n",
5823 surface, debug_surflocation(location), persistent);
5825 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5826 && !(surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5827 && (location & SFLAG_INDRAWABLE))
5828 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5830 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5831 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5832 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5834 if (persistent)
5836 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5837 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5839 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5841 TRACE("Passing to container.\n");
5842 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5845 surface->flags &= ~SFLAG_LOCATIONS;
5846 surface->flags |= location;
5848 /* Redraw emulated overlays, if any */
5849 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5851 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5853 surface_draw_overlay(overlay);
5857 else
5859 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5861 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5863 TRACE("Passing to container\n");
5864 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5867 surface->flags &= ~location;
5870 if (!(surface->flags & SFLAG_LOCATIONS))
5872 ERR("Surface %p does not have any up to date location.\n", surface);
5876 static DWORD resource_access_from_location(DWORD location)
5878 switch (location)
5880 case SFLAG_INSYSMEM:
5881 return WINED3D_RESOURCE_ACCESS_CPU;
5883 case SFLAG_INDRAWABLE:
5884 case SFLAG_INSRGBTEX:
5885 case SFLAG_INTEXTURE:
5886 case SFLAG_INRB_MULTISAMPLE:
5887 case SFLAG_INRB_RESOLVED:
5888 return WINED3D_RESOURCE_ACCESS_GPU;
5890 default:
5891 FIXME("Unhandled location %#x.\n", location);
5892 return 0;
5896 static void surface_load_sysmem(struct wined3d_surface *surface,
5897 const struct wined3d_gl_info *gl_info, const RECT *rect)
5899 surface_prepare_system_memory(surface);
5901 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5902 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5904 /* Download the surface to system memory. */
5905 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5907 struct wined3d_device *device = surface->resource.device;
5908 struct wined3d_context *context;
5910 /* TODO: Use already acquired context when possible. */
5911 context = context_acquire(device, NULL);
5913 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5914 surface_download_data(surface, gl_info);
5916 context_release(context);
5918 return;
5921 if (surface->flags & SFLAG_INDRAWABLE)
5923 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5924 wined3d_surface_get_pitch(surface));
5925 return;
5928 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5929 surface, surface->flags & SFLAG_LOCATIONS);
5932 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5933 const struct wined3d_gl_info *gl_info, const RECT *rect)
5935 struct wined3d_device *device = surface->resource.device;
5936 enum wined3d_conversion_type convert;
5937 struct wined3d_format format;
5938 UINT byte_count;
5939 BYTE *mem;
5941 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5943 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5944 return WINED3DERR_INVALIDCALL;
5947 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5948 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5950 if (surface->flags & SFLAG_INTEXTURE)
5952 RECT r;
5954 surface_get_rect(surface, rect, &r);
5955 surface_blt_to_drawable(device, WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
5957 return WINED3D_OK;
5960 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5962 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5963 * path through sysmem. */
5964 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5967 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5969 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5970 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5971 * called. */
5972 if ((convert != WINED3D_CT_NONE) && (surface->flags & SFLAG_PBO))
5974 struct wined3d_context *context;
5976 TRACE("Removing the pbo attached to surface %p.\n", surface);
5978 /* TODO: Use already acquired context when possible. */
5979 context = context_acquire(device, NULL);
5981 surface_remove_pbo(surface, gl_info);
5983 context_release(context);
5986 if ((convert != WINED3D_CT_NONE) && surface->resource.allocatedMemory)
5988 UINT height = surface->resource.height;
5989 UINT width = surface->resource.width;
5990 UINT src_pitch, dst_pitch;
5992 byte_count = format.conv_byte_count;
5993 src_pitch = wined3d_surface_get_pitch(surface);
5995 /* Stick to the alignment for the converted surface too, makes it
5996 * easier to load the surface. */
5997 dst_pitch = width * byte_count;
5998 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6000 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6002 ERR("Out of memory (%u).\n", dst_pitch * height);
6003 return E_OUTOFMEMORY;
6006 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
6007 src_pitch, width, height, dst_pitch, convert, surface);
6009 surface->flags |= SFLAG_CONVERTED;
6011 else
6013 surface->flags &= ~SFLAG_CONVERTED;
6014 mem = surface->resource.allocatedMemory;
6015 byte_count = format.byte_count;
6018 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
6020 /* Don't delete PBO memory. */
6021 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6022 HeapFree(GetProcessHeap(), 0, mem);
6024 return WINED3D_OK;
6027 static HRESULT surface_load_texture(struct wined3d_surface *surface,
6028 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
6030 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
6031 struct wined3d_device *device = surface->resource.device;
6032 enum wined3d_conversion_type convert;
6033 struct wined3d_context *context;
6034 UINT width, src_pitch, dst_pitch;
6035 struct wined3d_bo_address data;
6036 struct wined3d_format format;
6037 POINT dst_point = {0, 0};
6038 BYTE *mem;
6040 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
6041 && surface_is_offscreen(surface)
6042 && (surface->flags & SFLAG_INDRAWABLE))
6044 surface_load_fb_texture(surface, srgb);
6046 return WINED3D_OK;
6049 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6050 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
6051 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6052 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6053 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6055 if (srgb)
6056 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INTEXTURE,
6057 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6058 else
6059 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INSRGBTEX,
6060 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6062 return WINED3D_OK;
6065 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
6066 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
6067 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6068 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6069 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6071 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
6072 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
6073 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6075 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
6076 &rect, surface, dst_location, &rect);
6078 return WINED3D_OK;
6081 /* Upload from system memory */
6083 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6084 TRUE /* We will use textures */, &format, &convert);
6086 if (srgb)
6088 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6090 /* Performance warning... */
6091 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6092 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6095 else
6097 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6099 /* Performance warning... */
6100 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6101 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6105 if (!(surface->flags & SFLAG_INSYSMEM))
6107 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6108 /* Lets hope we get it from somewhere... */
6109 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6112 /* TODO: Use already acquired context when possible. */
6113 context = context_acquire(device, NULL);
6115 surface_prepare_texture(surface, context, srgb);
6116 surface_bind_and_dirtify(surface, context, srgb);
6118 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6120 surface->flags |= SFLAG_GLCKEY;
6121 surface->gl_color_key = surface->src_blt_color_key;
6123 else surface->flags &= ~SFLAG_GLCKEY;
6125 width = surface->resource.width;
6126 src_pitch = wined3d_surface_get_pitch(surface);
6128 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6129 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6130 * called. */
6131 if ((convert != WINED3D_CT_NONE || format.convert) && (surface->flags & SFLAG_PBO))
6133 TRACE("Removing the pbo attached to surface %p.\n", surface);
6134 surface_remove_pbo(surface, gl_info);
6137 if (format.convert)
6139 /* This code is entered for texture formats which need a fixup. */
6140 UINT height = surface->resource.height;
6142 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6143 dst_pitch = width * format.conv_byte_count;
6144 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6146 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6148 ERR("Out of memory (%u).\n", dst_pitch * height);
6149 context_release(context);
6150 return E_OUTOFMEMORY;
6152 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6153 format.byte_count = format.conv_byte_count;
6154 src_pitch = dst_pitch;
6156 else if (convert != WINED3D_CT_NONE && surface->resource.allocatedMemory)
6158 /* This code is only entered for color keying fixups */
6159 UINT height = surface->resource.height;
6161 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6162 dst_pitch = width * format.conv_byte_count;
6163 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6165 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6167 ERR("Out of memory (%u).\n", dst_pitch * height);
6168 context_release(context);
6169 return E_OUTOFMEMORY;
6171 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6172 width, height, dst_pitch, convert, surface);
6173 format.byte_count = format.conv_byte_count;
6174 src_pitch = dst_pitch;
6176 else
6178 mem = surface->resource.allocatedMemory;
6181 data.buffer_object = surface->pbo;
6182 data.addr = mem;
6183 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
6185 context_release(context);
6187 /* Don't delete PBO memory. */
6188 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6189 HeapFree(GetProcessHeap(), 0, mem);
6191 return WINED3D_OK;
6194 static void surface_multisample_resolve(struct wined3d_surface *surface)
6196 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6198 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6199 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6201 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
6202 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6205 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6207 struct wined3d_device *device = surface->resource.device;
6208 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6209 HRESULT hr;
6211 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6213 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6215 if (location == SFLAG_INTEXTURE)
6217 struct wined3d_context *context = context_acquire(device, NULL);
6218 surface_load_ds_location(surface, context, location);
6219 context_release(context);
6220 return WINED3D_OK;
6222 else
6224 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6225 return WINED3DERR_INVALIDCALL;
6229 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6230 location = SFLAG_INTEXTURE;
6232 if (surface->flags & location)
6234 TRACE("Location already up to date.\n");
6236 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
6237 && surface_need_pbo(surface, gl_info))
6238 surface_load_pbo(surface, gl_info);
6240 return WINED3D_OK;
6243 if (WARN_ON(d3d_surface))
6245 DWORD required_access = resource_access_from_location(location);
6246 if ((surface->resource.access_flags & required_access) != required_access)
6247 WARN("Operation requires %#x access, but surface only has %#x.\n",
6248 required_access, surface->resource.access_flags);
6251 if (!(surface->flags & SFLAG_LOCATIONS))
6253 ERR("Surface %p does not have any up to date location.\n", surface);
6254 surface->flags |= SFLAG_LOST;
6255 return WINED3DERR_DEVICELOST;
6258 switch (location)
6260 case SFLAG_INSYSMEM:
6261 surface_load_sysmem(surface, gl_info, rect);
6262 break;
6264 case SFLAG_INDRAWABLE:
6265 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6266 return hr;
6267 break;
6269 case SFLAG_INRB_RESOLVED:
6270 surface_multisample_resolve(surface);
6271 break;
6273 case SFLAG_INTEXTURE:
6274 case SFLAG_INSRGBTEX:
6275 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6276 return hr;
6277 break;
6279 default:
6280 ERR("Don't know how to handle location %#x.\n", location);
6281 break;
6284 if (!rect)
6286 surface->flags |= location;
6288 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6289 surface_evict_sysmem(surface);
6292 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6293 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6295 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6298 return WINED3D_OK;
6301 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6303 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6305 /* Not on a swapchain - must be offscreen */
6306 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6308 /* The front buffer is always onscreen */
6309 if (surface == swapchain->front_buffer) return FALSE;
6311 /* If the swapchain is rendered to an FBO, the backbuffer is
6312 * offscreen, otherwise onscreen */
6313 return swapchain->render_to_fbo;
6316 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6317 /* Context activation is done by the caller. */
6318 static void ffp_blit_free(struct wined3d_device *device) { }
6320 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6321 /* Context activation is done by the caller. */
6322 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6324 BYTE table[256][4];
6325 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6327 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6329 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6330 ENTER_GL();
6331 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6332 LEAVE_GL();
6335 /* Context activation is done by the caller. */
6336 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6338 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6340 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6341 * else the surface is converted in software at upload time in LoadLocation.
6343 if (!(surface->flags & SFLAG_CONVERTED) && fixup == COMPLEX_FIXUP_P8
6344 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6345 ffp_blit_p8_upload_palette(surface, context->gl_info);
6347 ENTER_GL();
6348 glEnable(surface->texture_target);
6349 checkGLcall("glEnable(surface->texture_target)");
6350 LEAVE_GL();
6351 return WINED3D_OK;
6354 /* Context activation is done by the caller. */
6355 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6357 ENTER_GL();
6358 glDisable(GL_TEXTURE_2D);
6359 checkGLcall("glDisable(GL_TEXTURE_2D)");
6360 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6362 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6363 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6365 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6367 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6368 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6370 LEAVE_GL();
6373 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6374 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
6375 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
6377 enum complex_fixup src_fixup;
6379 switch (blit_op)
6381 case WINED3D_BLIT_OP_COLOR_BLIT:
6382 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
6383 return FALSE;
6385 src_fixup = get_complex_fixup(src_format->color_fixup);
6386 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6388 TRACE("Checking support for fixup:\n");
6389 dump_color_fixup_desc(src_format->color_fixup);
6392 if (!is_identity_fixup(dst_format->color_fixup))
6394 TRACE("Destination fixups are not supported\n");
6395 return FALSE;
6398 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6400 TRACE("P8 fixup supported\n");
6401 return TRUE;
6404 /* We only support identity conversions. */
6405 if (is_identity_fixup(src_format->color_fixup))
6407 TRACE("[OK]\n");
6408 return TRUE;
6411 TRACE("[FAILED]\n");
6412 return FALSE;
6414 case WINED3D_BLIT_OP_COLOR_FILL:
6415 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
6416 return FALSE;
6418 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6420 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6421 return FALSE;
6423 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6425 TRACE("Color fill not supported\n");
6426 return FALSE;
6429 /* FIXME: We should reject color fills on formats with fixups,
6430 * but this would break P8 color fills for example. */
6432 return TRUE;
6434 case WINED3D_BLIT_OP_DEPTH_FILL:
6435 return TRUE;
6437 default:
6438 TRACE("Unsupported blit_op=%d\n", blit_op);
6439 return FALSE;
6443 /* Do not call while under the GL lock. */
6444 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6445 const RECT *dst_rect, const struct wined3d_color *color)
6447 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6448 struct wined3d_fb_state fb = {&dst_surface, NULL};
6450 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6452 return WINED3D_OK;
6455 /* Do not call while under the GL lock. */
6456 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6457 struct wined3d_surface *surface, const RECT *rect, float depth)
6459 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6460 struct wined3d_fb_state fb = {NULL, surface};
6462 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6464 return WINED3D_OK;
6467 const struct blit_shader ffp_blit = {
6468 ffp_blit_alloc,
6469 ffp_blit_free,
6470 ffp_blit_set,
6471 ffp_blit_unset,
6472 ffp_blit_supported,
6473 ffp_blit_color_fill,
6474 ffp_blit_depth_fill,
6477 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6479 return WINED3D_OK;
6482 /* Context activation is done by the caller. */
6483 static void cpu_blit_free(struct wined3d_device *device)
6487 /* Context activation is done by the caller. */
6488 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6490 return WINED3D_OK;
6493 /* Context activation is done by the caller. */
6494 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6498 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6499 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
6500 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
6502 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6504 return TRUE;
6507 return FALSE;
6510 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
6511 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
6512 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
6514 UINT row_block_count;
6515 const BYTE *src_row;
6516 BYTE *dst_row;
6517 UINT x, y;
6519 src_row = src_data;
6520 dst_row = dst_data;
6522 row_block_count = (update_w + format->block_width - 1) / format->block_width;
6524 if (!flags)
6526 for (y = 0; y < update_h; y += format->block_height)
6528 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
6529 src_row += src_pitch;
6530 dst_row += dst_pitch;
6533 return WINED3D_OK;
6536 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
6538 src_row += (((update_h / format->block_height) - 1) * src_pitch);
6540 switch (format->id)
6542 case WINED3DFMT_DXT1:
6543 for (y = 0; y < update_h; y += format->block_height)
6545 struct block
6547 WORD color[2];
6548 BYTE control_row[4];
6551 const struct block *s = (const struct block *)src_row;
6552 struct block *d = (struct block *)dst_row;
6554 for (x = 0; x < row_block_count; ++x)
6556 d[x].color[0] = s[x].color[0];
6557 d[x].color[1] = s[x].color[1];
6558 d[x].control_row[0] = s[x].control_row[3];
6559 d[x].control_row[1] = s[x].control_row[2];
6560 d[x].control_row[2] = s[x].control_row[1];
6561 d[x].control_row[3] = s[x].control_row[0];
6563 src_row -= src_pitch;
6564 dst_row += dst_pitch;
6566 return WINED3D_OK;
6568 case WINED3DFMT_DXT3:
6569 for (y = 0; y < update_h; y += format->block_height)
6571 struct block
6573 WORD alpha_row[4];
6574 WORD color[2];
6575 BYTE control_row[4];
6578 const struct block *s = (const struct block *)src_row;
6579 struct block *d = (struct block *)dst_row;
6581 for (x = 0; x < row_block_count; ++x)
6583 d[x].alpha_row[0] = s[x].alpha_row[3];
6584 d[x].alpha_row[1] = s[x].alpha_row[2];
6585 d[x].alpha_row[2] = s[x].alpha_row[1];
6586 d[x].alpha_row[3] = s[x].alpha_row[0];
6587 d[x].color[0] = s[x].color[0];
6588 d[x].color[1] = s[x].color[1];
6589 d[x].control_row[0] = s[x].control_row[3];
6590 d[x].control_row[1] = s[x].control_row[2];
6591 d[x].control_row[2] = s[x].control_row[1];
6592 d[x].control_row[3] = s[x].control_row[0];
6594 src_row -= src_pitch;
6595 dst_row += dst_pitch;
6597 return WINED3D_OK;
6599 default:
6600 FIXME("Compressed flip not implemented for format %s.\n",
6601 debug_d3dformat(format->id));
6602 return E_NOTIMPL;
6606 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
6607 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
6609 return E_NOTIMPL;
6612 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6613 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6614 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
6616 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6617 const struct wined3d_format *src_format, *dst_format;
6618 struct wined3d_surface *orig_src = src_surface;
6619 struct wined3d_map_desc dst_map, src_map;
6620 const BYTE *sbase = NULL;
6621 HRESULT hr = WINED3D_OK;
6622 const BYTE *sbuf;
6623 BYTE *dbuf;
6624 int x, y;
6626 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6627 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6628 flags, fx, debug_d3dtexturefiltertype(filter));
6630 if (src_surface == dst_surface)
6632 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6633 src_map = dst_map;
6634 src_format = dst_surface->resource.format;
6635 dst_format = src_format;
6637 else
6639 dst_format = dst_surface->resource.format;
6640 if (src_surface)
6642 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6644 src_surface = surface_convert_format(src_surface, dst_format->id);
6645 if (!src_surface)
6647 /* The conv function writes a FIXME */
6648 WARN("Cannot convert source surface format to dest format.\n");
6649 goto release;
6652 wined3d_surface_map(src_surface, &src_map, NULL, WINED3DLOCK_READONLY);
6653 src_format = src_surface->resource.format;
6655 else
6657 src_format = dst_format;
6660 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
6663 bpp = dst_surface->resource.format->byte_count;
6664 srcheight = src_rect->bottom - src_rect->top;
6665 srcwidth = src_rect->right - src_rect->left;
6666 dstheight = dst_rect->bottom - dst_rect->top;
6667 dstwidth = dst_rect->right - dst_rect->left;
6668 width = (dst_rect->right - dst_rect->left) * bpp;
6670 if (src_surface)
6671 sbase = (BYTE *)src_map.data
6672 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
6673 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
6674 if (src_surface != dst_surface)
6675 dbuf = dst_map.data;
6676 else
6677 dbuf = (BYTE *)dst_map.data
6678 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
6679 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
6681 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
6683 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6685 if (src_surface == dst_surface)
6687 FIXME("Only plain blits supported on compressed surfaces.\n");
6688 hr = E_NOTIMPL;
6689 goto release;
6692 if (srcheight != dstheight || srcwidth != dstwidth)
6694 WARN("Stretching not supported on compressed surfaces.\n");
6695 hr = WINED3DERR_INVALIDCALL;
6696 goto release;
6699 if (srcwidth & (src_format->block_width - 1) || srcheight & (src_format->block_height - 1))
6701 WARN("Rectangle not block-aligned.\n");
6702 hr = WINED3DERR_INVALIDCALL;
6703 goto release;
6706 hr = surface_cpu_blt_compressed(sbase, dbuf,
6707 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
6708 src_format, flags, fx);
6709 goto release;
6712 /* First, all the 'source-less' blits */
6713 if (flags & WINEDDBLT_COLORFILL)
6715 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
6716 flags &= ~WINEDDBLT_COLORFILL;
6719 if (flags & WINEDDBLT_DEPTHFILL)
6721 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6723 if (flags & WINEDDBLT_ROP)
6725 /* Catch some degenerate cases here. */
6726 switch (fx->dwROP)
6728 case BLACKNESS:
6729 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
6730 break;
6731 case 0xAA0029: /* No-op */
6732 break;
6733 case WHITENESS:
6734 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
6735 break;
6736 case SRCCOPY: /* Well, we do that below? */
6737 break;
6738 default:
6739 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6740 goto error;
6742 flags &= ~WINEDDBLT_ROP;
6744 if (flags & WINEDDBLT_DDROPS)
6746 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6748 /* Now the 'with source' blits. */
6749 if (src_surface)
6751 int sx, xinc, sy, yinc;
6753 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6754 goto release;
6756 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
6757 && (srcwidth != dstwidth || srcheight != dstheight))
6759 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6760 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6763 xinc = (srcwidth << 16) / dstwidth;
6764 yinc = (srcheight << 16) / dstheight;
6766 if (!flags)
6768 /* No effects, we can cheat here. */
6769 if (dstwidth == srcwidth)
6771 if (dstheight == srcheight)
6773 /* No stretching in either direction. This needs to be as
6774 * fast as possible. */
6775 sbuf = sbase;
6777 /* Check for overlapping surfaces. */
6778 if (src_surface != dst_surface || dst_rect->top < src_rect->top
6779 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
6781 /* No overlap, or dst above src, so copy from top downwards. */
6782 for (y = 0; y < dstheight; ++y)
6784 memcpy(dbuf, sbuf, width);
6785 sbuf += src_map.row_pitch;
6786 dbuf += dst_map.row_pitch;
6789 else if (dst_rect->top > src_rect->top)
6791 /* Copy from bottom upwards. */
6792 sbuf += src_map.row_pitch * dstheight;
6793 dbuf += dst_map.row_pitch * dstheight;
6794 for (y = 0; y < dstheight; ++y)
6796 sbuf -= src_map.row_pitch;
6797 dbuf -= dst_map.row_pitch;
6798 memcpy(dbuf, sbuf, width);
6801 else
6803 /* Src and dst overlapping on the same line, use memmove. */
6804 for (y = 0; y < dstheight; ++y)
6806 memmove(dbuf, sbuf, width);
6807 sbuf += src_map.row_pitch;
6808 dbuf += dst_map.row_pitch;
6812 else
6814 /* Stretching in y direction only. */
6815 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6817 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6818 memcpy(dbuf, sbuf, width);
6819 dbuf += dst_map.row_pitch;
6823 else
6825 /* Stretching in X direction. */
6826 int last_sy = -1;
6827 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6829 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6831 if ((sy >> 16) == (last_sy >> 16))
6833 /* This source row is the same as last source row -
6834 * Copy the already stretched row. */
6835 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
6837 else
6839 #define STRETCH_ROW(type) \
6840 do { \
6841 const type *s = (const type *)sbuf; \
6842 type *d = (type *)dbuf; \
6843 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6844 d[x] = s[sx >> 16]; \
6845 } while(0)
6847 switch(bpp)
6849 case 1:
6850 STRETCH_ROW(BYTE);
6851 break;
6852 case 2:
6853 STRETCH_ROW(WORD);
6854 break;
6855 case 4:
6856 STRETCH_ROW(DWORD);
6857 break;
6858 case 3:
6860 const BYTE *s;
6861 BYTE *d = dbuf;
6862 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6864 DWORD pixel;
6866 s = sbuf + 3 * (sx >> 16);
6867 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6868 d[0] = (pixel ) & 0xff;
6869 d[1] = (pixel >> 8) & 0xff;
6870 d[2] = (pixel >> 16) & 0xff;
6871 d += 3;
6873 break;
6875 default:
6876 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6877 hr = WINED3DERR_NOTAVAILABLE;
6878 goto error;
6880 #undef STRETCH_ROW
6882 dbuf += dst_map.row_pitch;
6883 last_sy = sy;
6887 else
6889 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
6890 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6891 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6892 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6894 /* The color keying flags are checked for correctness in ddraw */
6895 if (flags & WINEDDBLT_KEYSRC)
6897 keylow = src_surface->src_blt_color_key.color_space_low_value;
6898 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
6900 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6902 keylow = fx->ddckSrcColorkey.color_space_low_value;
6903 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
6906 if (flags & WINEDDBLT_KEYDEST)
6908 /* Destination color keys are taken from the source surface! */
6909 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
6910 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
6912 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6914 destkeylow = fx->ddckDestColorkey.color_space_low_value;
6915 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
6918 if (bpp == 1)
6920 keymask = 0xff;
6922 else
6924 keymask = src_format->red_mask
6925 | src_format->green_mask
6926 | src_format->blue_mask;
6928 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6931 if (flags & WINEDDBLT_DDFX)
6933 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6934 LONG tmpxy;
6935 dTopLeft = dbuf;
6936 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6937 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
6938 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6940 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6942 /* I don't think we need to do anything about this flag */
6943 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6945 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6947 tmp = dTopRight;
6948 dTopRight = dTopLeft;
6949 dTopLeft = tmp;
6950 tmp = dBottomRight;
6951 dBottomRight = dBottomLeft;
6952 dBottomLeft = tmp;
6953 dstxinc = dstxinc * -1;
6955 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6957 tmp = dTopLeft;
6958 dTopLeft = dBottomLeft;
6959 dBottomLeft = tmp;
6960 tmp = dTopRight;
6961 dTopRight = dBottomRight;
6962 dBottomRight = tmp;
6963 dstyinc = dstyinc * -1;
6965 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6967 /* I don't think we need to do anything about this flag */
6968 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6970 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6972 tmp = dBottomRight;
6973 dBottomRight = dTopLeft;
6974 dTopLeft = tmp;
6975 tmp = dBottomLeft;
6976 dBottomLeft = dTopRight;
6977 dTopRight = tmp;
6978 dstxinc = dstxinc * -1;
6979 dstyinc = dstyinc * -1;
6981 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6983 tmp = dTopLeft;
6984 dTopLeft = dBottomLeft;
6985 dBottomLeft = dBottomRight;
6986 dBottomRight = dTopRight;
6987 dTopRight = tmp;
6988 tmpxy = dstxinc;
6989 dstxinc = dstyinc;
6990 dstyinc = tmpxy;
6991 dstxinc = dstxinc * -1;
6993 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6995 tmp = dTopLeft;
6996 dTopLeft = dTopRight;
6997 dTopRight = dBottomRight;
6998 dBottomRight = dBottomLeft;
6999 dBottomLeft = tmp;
7000 tmpxy = dstxinc;
7001 dstxinc = dstyinc;
7002 dstyinc = tmpxy;
7003 dstyinc = dstyinc * -1;
7005 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
7007 /* I don't think we need to do anything about this flag */
7008 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
7010 dbuf = dTopLeft;
7011 flags &= ~(WINEDDBLT_DDFX);
7014 #define COPY_COLORKEY_FX(type) \
7015 do { \
7016 const type *s; \
7017 type *d = (type *)dbuf, *dx, tmp; \
7018 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
7020 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
7021 dx = d; \
7022 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
7024 tmp = s[sx >> 16]; \
7025 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
7026 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
7028 dx[0] = tmp; \
7030 dx = (type *)(((BYTE *)dx) + dstxinc); \
7032 d = (type *)(((BYTE *)d) + dstyinc); \
7034 } while(0)
7036 switch (bpp)
7038 case 1:
7039 COPY_COLORKEY_FX(BYTE);
7040 break;
7041 case 2:
7042 COPY_COLORKEY_FX(WORD);
7043 break;
7044 case 4:
7045 COPY_COLORKEY_FX(DWORD);
7046 break;
7047 case 3:
7049 const BYTE *s;
7050 BYTE *d = dbuf, *dx;
7051 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
7053 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
7054 dx = d;
7055 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
7057 DWORD pixel, dpixel = 0;
7058 s = sbuf + 3 * (sx>>16);
7059 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
7060 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
7061 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
7062 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
7064 dx[0] = (pixel ) & 0xff;
7065 dx[1] = (pixel >> 8) & 0xff;
7066 dx[2] = (pixel >> 16) & 0xff;
7068 dx += dstxinc;
7070 d += dstyinc;
7072 break;
7074 default:
7075 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7076 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7077 hr = WINED3DERR_NOTAVAILABLE;
7078 goto error;
7079 #undef COPY_COLORKEY_FX
7084 error:
7085 if (flags && FIXME_ON(d3d_surface))
7087 FIXME("\tUnsupported flags: %#x.\n", flags);
7090 release:
7091 wined3d_surface_unmap(dst_surface);
7092 if (src_surface && src_surface != dst_surface)
7093 wined3d_surface_unmap(src_surface);
7094 /* Release the converted surface, if any. */
7095 if (src_surface && src_surface != orig_src)
7096 wined3d_surface_decref(src_surface);
7098 return hr;
7101 /* Do not call while under the GL lock. */
7102 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7103 const RECT *dst_rect, const struct wined3d_color *color)
7105 static const RECT src_rect;
7106 WINEDDBLTFX BltFx;
7108 memset(&BltFx, 0, sizeof(BltFx));
7109 BltFx.dwSize = sizeof(BltFx);
7110 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7111 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7112 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
7115 /* Do not call while under the GL lock. */
7116 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7117 struct wined3d_surface *surface, const RECT *rect, float depth)
7119 FIXME("Depth filling not implemented by cpu_blit.\n");
7120 return WINED3DERR_INVALIDCALL;
7123 const struct blit_shader cpu_blit = {
7124 cpu_blit_alloc,
7125 cpu_blit_free,
7126 cpu_blit_set,
7127 cpu_blit_unset,
7128 cpu_blit_supported,
7129 cpu_blit_color_fill,
7130 cpu_blit_depth_fill,
7133 static HRESULT surface_init(struct wined3d_surface *surface, enum wined3d_surface_type surface_type, UINT alignment,
7134 UINT width, UINT height, UINT level, enum wined3d_multisample_type multisample_type,
7135 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7136 enum wined3d_pool pool, DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops)
7138 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7139 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7140 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
7141 unsigned int resource_size;
7142 HRESULT hr;
7144 if (multisample_quality > 0)
7146 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7147 multisample_quality = 0;
7150 /* Quick lockable sanity check.
7151 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7152 * this function is too deep to need to care about things like this.
7153 * Levels need to be checked too, since they all affect what can be done. */
7154 switch (pool)
7156 case WINED3D_POOL_SCRATCH:
7157 if (!lockable)
7159 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7160 "which are mutually exclusive, setting lockable to TRUE.\n");
7161 lockable = TRUE;
7163 break;
7165 case WINED3D_POOL_SYSTEM_MEM:
7166 if (!lockable)
7167 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7168 break;
7170 case WINED3D_POOL_MANAGED:
7171 if (usage & WINED3DUSAGE_DYNAMIC)
7172 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7173 break;
7175 case WINED3D_POOL_DEFAULT:
7176 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7177 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7178 break;
7180 default:
7181 FIXME("Unknown pool %#x.\n", pool);
7182 break;
7185 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3D_POOL_DEFAULT)
7186 FIXME("Trying to create a render target that isn't in the default pool.\n");
7188 /* FIXME: Check that the format is supported by the device. */
7190 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7191 if (!resource_size)
7192 return WINED3DERR_INVALIDCALL;
7194 surface->surface_type = surface_type;
7196 switch (surface_type)
7198 case WINED3D_SURFACE_TYPE_OPENGL:
7199 surface->surface_ops = &surface_ops;
7200 break;
7202 case WINED3D_SURFACE_TYPE_GDI:
7203 surface->surface_ops = &gdi_surface_ops;
7204 break;
7206 default:
7207 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7208 return WINED3DERR_INVALIDCALL;
7211 hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
7212 multisample_type, multisample_quality, usage, pool, width, height, 1,
7213 resource_size, parent, parent_ops, &surface_resource_ops);
7214 if (FAILED(hr))
7216 WARN("Failed to initialize resource, returning %#x.\n", hr);
7217 return hr;
7220 /* "Standalone" surface. */
7221 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7223 surface->texture_level = level;
7224 list_init(&surface->overlays);
7226 /* Flags */
7227 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7228 if (flags & WINED3D_SURFACE_DISCARD)
7229 surface->flags |= SFLAG_DISCARD;
7230 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
7231 surface->flags |= SFLAG_PIN_SYSMEM;
7232 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7233 surface->flags |= SFLAG_LOCKABLE;
7234 /* I'm not sure if this qualifies as a hack or as an optimization. It
7235 * seems reasonable to assume that lockable render targets will get
7236 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7237 * creation. However, the other reason we want to do this is that several
7238 * ddraw applications access surface memory while the surface isn't
7239 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7240 * future locks prevents these from crashing. */
7241 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7242 surface->flags |= SFLAG_DYNLOCK;
7244 /* Mark the texture as dirty so that it gets loaded first time around. */
7245 surface_add_dirty_rect(surface, NULL);
7246 list_init(&surface->renderbuffers);
7248 TRACE("surface %p, memory %p, size %u\n",
7249 surface, surface->resource.allocatedMemory, surface->resource.size);
7251 /* Call the private setup routine */
7252 hr = surface->surface_ops->surface_private_setup(surface);
7253 if (FAILED(hr))
7255 ERR("Private setup failed, returning %#x\n", hr);
7256 surface_cleanup(surface);
7257 return hr;
7260 /* Similar to lockable rendertargets above, creating the DIB section
7261 * during surface initialization prevents the sysmem pointer from changing
7262 * after a wined3d_surface_getdc() call. */
7263 if ((usage & WINED3DUSAGE_OWNDC) && !surface->hDC
7264 && SUCCEEDED(surface_create_dib_section(surface)))
7266 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
7267 surface->resource.heapMemory = NULL;
7268 surface->resource.allocatedMemory = surface->dib.bitmap_data;
7271 return hr;
7274 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7275 enum wined3d_format_id format_id, UINT level, DWORD usage, enum wined3d_pool pool,
7276 enum wined3d_multisample_type multisample_type, DWORD multisample_quality,
7277 enum wined3d_surface_type surface_type, DWORD flags, void *parent,
7278 const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7280 struct wined3d_surface *object;
7281 HRESULT hr;
7283 TRACE("device %p, width %u, height %u, format %s, level %u\n",
7284 device, width, height, debug_d3dformat(format_id), level);
7285 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7286 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7287 TRACE("surface_type %#x, flags %#x, parent %p, parent_ops %p.\n", surface_type, flags, parent, parent_ops);
7289 if (surface_type == WINED3D_SURFACE_TYPE_OPENGL && !device->adapter)
7291 ERR("OpenGL surfaces are not available without OpenGL.\n");
7292 return WINED3DERR_NOTAVAILABLE;
7295 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7296 if (!object)
7298 ERR("Failed to allocate surface memory.\n");
7299 return WINED3DERR_OUTOFVIDEOMEMORY;
7302 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level,
7303 multisample_type, multisample_quality, device, usage, format_id, pool, flags, parent, parent_ops);
7304 if (FAILED(hr))
7306 WARN("Failed to initialize surface, returning %#x.\n", hr);
7307 HeapFree(GetProcessHeap(), 0, object);
7308 return hr;
7311 TRACE("Created surface %p.\n", object);
7312 *surface = object;
7314 return hr;