msxml3: Debug output support for VT_ERROR.
[wine/multimedia.git] / dlls / wined3d / surface.c
blob1ff55211afe339bd2b1fccb7d3af45231620ce7c
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, WINED3DTEXTUREFILTERTYPE 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 WINED3DTEXTUREFILTERTYPE 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, WINED3DTEXTUREFILTERTYPE 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, WINED3DTEXF_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] = WINED3DTEXF_POINT;
367 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
368 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_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 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
518 surface->resource.heapMemory = NULL;
520 return WINED3D_OK;
523 static BOOL surface_need_pbo(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
525 if (surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
526 return FALSE;
527 if (!(surface->flags & SFLAG_DYNLOCK))
528 return FALSE;
529 if (surface->flags & (SFLAG_CONVERTED | SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM))
530 return FALSE;
531 if (!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT])
532 return FALSE;
534 return TRUE;
537 static void surface_load_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
539 struct wined3d_context *context;
540 GLenum error;
542 context = context_acquire(surface->resource.device, NULL);
543 ENTER_GL();
545 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
546 error = glGetError();
547 if (!surface->pbo || error != GL_NO_ERROR)
548 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
550 TRACE("Binding PBO %u.\n", surface->pbo);
552 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
553 checkGLcall("glBindBufferARB");
555 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
556 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
557 checkGLcall("glBufferDataARB");
559 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
560 checkGLcall("glBindBufferARB");
562 /* We don't need the system memory anymore and we can't even use it for PBOs. */
563 if (!(surface->flags & SFLAG_CLIENT))
565 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
566 surface->resource.heapMemory = NULL;
568 surface->resource.allocatedMemory = NULL;
569 surface->flags |= SFLAG_PBO;
570 LEAVE_GL();
571 context_release(context);
574 static void surface_prepare_system_memory(struct wined3d_surface *surface)
576 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
578 TRACE("surface %p.\n", surface);
580 if (!(surface->flags & SFLAG_PBO) && surface_need_pbo(surface, gl_info))
581 surface_load_pbo(surface, gl_info);
582 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
584 /* Whatever surface we have, make sure that there is memory allocated
585 * for the downloaded copy, or a PBO to map. */
586 if (!surface->resource.heapMemory)
587 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
589 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
590 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
592 if (surface->flags & SFLAG_INSYSMEM)
593 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
597 static void surface_evict_sysmem(struct wined3d_surface *surface)
599 if (surface->flags & SFLAG_DONOTFREE)
600 return;
602 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
603 surface->resource.allocatedMemory = NULL;
604 surface->resource.heapMemory = NULL;
605 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
608 /* Context activation is done by the caller. */
609 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
610 struct wined3d_context *context, BOOL srgb)
612 struct wined3d_device *device = surface->resource.device;
613 DWORD active_sampler;
615 /* We don't need a specific texture unit, but after binding the texture
616 * the current unit is dirty. Read the unit back instead of switching to
617 * 0, this avoids messing around with the state manager's GL states. The
618 * current texture unit should always be a valid one.
620 * To be more specific, this is tricky because we can implicitly be
621 * called from sampler() in state.c. This means we can't touch anything
622 * other than whatever happens to be the currently active texture, or we
623 * would risk marking already applied sampler states dirty again. */
624 active_sampler = device->rev_tex_unit_map[context->active_texture];
626 if (active_sampler != WINED3D_UNMAPPED_STAGE)
627 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
628 surface_bind(surface, context, srgb);
631 static void surface_force_reload(struct wined3d_surface *surface)
633 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
636 static void surface_release_client_storage(struct wined3d_surface *surface)
638 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
640 ENTER_GL();
641 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
642 if (surface->texture_name)
644 surface_bind_and_dirtify(surface, context, FALSE);
645 glTexImage2D(surface->texture_target, surface->texture_level,
646 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
648 if (surface->texture_name_srgb)
650 surface_bind_and_dirtify(surface, context, TRUE);
651 glTexImage2D(surface->texture_target, surface->texture_level,
652 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
654 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
655 LEAVE_GL();
657 context_release(context);
659 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
660 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
661 surface_force_reload(surface);
664 static HRESULT surface_private_setup(struct wined3d_surface *surface)
666 /* TODO: Check against the maximum texture sizes supported by the video card. */
667 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
668 unsigned int pow2Width, pow2Height;
670 TRACE("surface %p.\n", surface);
672 surface->texture_name = 0;
673 surface->texture_target = GL_TEXTURE_2D;
675 /* Non-power2 support */
676 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
678 pow2Width = surface->resource.width;
679 pow2Height = surface->resource.height;
681 else
683 /* Find the nearest pow2 match */
684 pow2Width = pow2Height = 1;
685 while (pow2Width < surface->resource.width)
686 pow2Width <<= 1;
687 while (pow2Height < surface->resource.height)
688 pow2Height <<= 1;
690 surface->pow2Width = pow2Width;
691 surface->pow2Height = pow2Height;
693 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
695 /* TODO: Add support for non power two compressed textures. */
696 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
698 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
699 surface, surface->resource.width, surface->resource.height);
700 return WINED3DERR_NOTAVAILABLE;
704 if (pow2Width != surface->resource.width
705 || pow2Height != surface->resource.height)
707 surface->flags |= SFLAG_NONPOW2;
710 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
711 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
713 /* One of three options:
714 * 1: Do the same as we do with NPOT and scale the texture, (any
715 * texture ops would require the texture to be scaled which is
716 * potentially slow)
717 * 2: Set the texture to the maximum size (bad idea).
718 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
719 * 4: Create the surface, but allow it to be used only for DirectDraw
720 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
721 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
722 * the render target. */
723 if (surface->resource.pool == WINED3DPOOL_DEFAULT || surface->resource.pool == WINED3DPOOL_MANAGED)
725 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
726 return WINED3DERR_NOTAVAILABLE;
729 /* We should never use this surface in combination with OpenGL! */
730 TRACE("Creating an oversized surface: %ux%u.\n",
731 surface->pow2Width, surface->pow2Height);
733 else
735 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
736 * and EXT_PALETTED_TEXTURE is used in combination with texture
737 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
738 * EXT_PALETTED_TEXTURE doesn't work in combination with
739 * ARB_TEXTURE_RECTANGLE. */
740 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
741 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
742 && gl_info->supported[EXT_PALETTED_TEXTURE]
743 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
745 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
746 surface->pow2Width = surface->resource.width;
747 surface->pow2Height = surface->resource.height;
748 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
752 switch (wined3d_settings.offscreen_rendering_mode)
754 case ORM_FBO:
755 surface->get_drawable_size = get_drawable_size_fbo;
756 break;
758 case ORM_BACKBUFFER:
759 surface->get_drawable_size = get_drawable_size_backbuffer;
760 break;
762 default:
763 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
764 return WINED3DERR_INVALIDCALL;
767 surface->flags |= SFLAG_INSYSMEM;
769 return WINED3D_OK;
772 static void surface_realize_palette(struct wined3d_surface *surface)
774 struct wined3d_palette *palette = surface->palette;
776 TRACE("surface %p.\n", surface);
778 if (!palette) return;
780 if (surface->resource.format->id == WINED3DFMT_P8_UINT
781 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
783 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
785 /* Make sure the texture is up to date. This call doesn't do
786 * anything if the texture is already up to date. */
787 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
789 /* We want to force a palette refresh, so mark the drawable as not being up to date */
790 if (!surface_is_offscreen(surface))
791 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
793 else
795 if (!(surface->flags & SFLAG_INSYSMEM))
797 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
798 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
800 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
804 if (surface->flags & SFLAG_DIBSECTION)
806 RGBQUAD col[256];
807 unsigned int i;
809 TRACE("Updating the DC's palette.\n");
811 for (i = 0; i < 256; ++i)
813 col[i].rgbRed = palette->palents[i].peRed;
814 col[i].rgbGreen = palette->palents[i].peGreen;
815 col[i].rgbBlue = palette->palents[i].peBlue;
816 col[i].rgbReserved = 0;
818 SetDIBColorTable(surface->hDC, 0, 256, col);
821 /* Propagate the changes to the drawable when we have a palette. */
822 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
823 surface_load_location(surface, surface->draw_binding, NULL);
826 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
828 HRESULT hr;
830 /* If there's no destination surface there is nothing to do. */
831 if (!surface->overlay_dest)
832 return WINED3D_OK;
834 /* Blt calls ModifyLocation on the dest surface, which in turn calls
835 * DrawOverlay to update the overlay. Prevent an endless recursion. */
836 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
837 return WINED3D_OK;
839 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
840 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
841 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3DTEXF_LINEAR);
842 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
844 return hr;
847 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
849 struct wined3d_device *device = surface->resource.device;
850 const RECT *pass_rect = rect;
852 TRACE("surface %p, rect %s, flags %#x.\n",
853 surface, wine_dbgstr_rect(rect), flags);
855 if (flags & WINED3DLOCK_DISCARD)
857 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
858 surface_prepare_system_memory(surface);
859 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
861 else
863 /* surface_load_location() does not check if the rectangle specifies
864 * the full surface. Most callers don't need that, so do it here. */
865 if (rect && !rect->top && !rect->left
866 && rect->right == surface->resource.width
867 && rect->bottom == surface->resource.height)
868 pass_rect = NULL;
869 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
872 if (surface->flags & SFLAG_PBO)
874 const struct wined3d_gl_info *gl_info;
875 struct wined3d_context *context;
877 context = context_acquire(device, NULL);
878 gl_info = context->gl_info;
880 ENTER_GL();
881 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
882 checkGLcall("glBindBufferARB");
884 /* This shouldn't happen but could occur if some other function
885 * didn't handle the PBO properly. */
886 if (surface->resource.allocatedMemory)
887 ERR("The surface already has PBO memory allocated.\n");
889 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
890 checkGLcall("glMapBufferARB");
892 /* Make sure the PBO isn't set anymore in order not to break non-PBO
893 * calls. */
894 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
895 checkGLcall("glBindBufferARB");
897 LEAVE_GL();
898 context_release(context);
901 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
903 if (!rect)
904 surface_add_dirty_rect(surface, NULL);
905 else
907 struct wined3d_box b;
909 b.left = rect->left;
910 b.top = rect->top;
911 b.right = rect->right;
912 b.bottom = rect->bottom;
913 b.front = 0;
914 b.back = 1;
915 surface_add_dirty_rect(surface, &b);
920 static void surface_unmap(struct wined3d_surface *surface)
922 struct wined3d_device *device = surface->resource.device;
923 BOOL fullsurface;
925 TRACE("surface %p.\n", surface);
927 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
929 if (surface->flags & SFLAG_PBO)
931 const struct wined3d_gl_info *gl_info;
932 struct wined3d_context *context;
934 TRACE("Freeing PBO memory.\n");
936 context = context_acquire(device, NULL);
937 gl_info = context->gl_info;
939 ENTER_GL();
940 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
941 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
942 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
943 checkGLcall("glUnmapBufferARB");
944 LEAVE_GL();
945 context_release(context);
947 surface->resource.allocatedMemory = NULL;
950 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
952 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
954 TRACE("Not dirtified, nothing to do.\n");
955 goto done;
958 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
959 || (device->fb.render_targets && surface == device->fb.render_targets[0]))
961 if (!surface->dirtyRect.left && !surface->dirtyRect.top
962 && surface->dirtyRect.right == surface->resource.width
963 && surface->dirtyRect.bottom == surface->resource.height)
965 fullsurface = TRUE;
967 else
969 /* TODO: Proper partial rectangle tracking. */
970 fullsurface = FALSE;
971 surface->flags |= SFLAG_INSYSMEM;
974 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
976 /* Partial rectangle tracking is not commonly implemented, it is only
977 * done for render targets. INSYSMEM was set before to tell
978 * surface_load_location() where to read the rectangle from.
979 * Indrawable is set because all modifications from the partial
980 * sysmem copy are written back to the drawable, thus the surface is
981 * merged again in the drawable. The sysmem copy is not fully up to
982 * date because only a subrectangle was read in Map(). */
983 if (!fullsurface)
985 surface_modify_location(surface, surface->draw_binding, TRUE);
986 surface_evict_sysmem(surface);
989 surface->dirtyRect.left = surface->resource.width;
990 surface->dirtyRect.top = surface->resource.height;
991 surface->dirtyRect.right = 0;
992 surface->dirtyRect.bottom = 0;
994 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
996 FIXME("Depth / stencil buffer locking is not implemented.\n");
999 done:
1000 /* Overlays have to be redrawn manually after changes with the GL implementation */
1001 if (surface->overlay_dest)
1002 surface_draw_overlay(surface);
1005 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1007 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1008 return FALSE;
1009 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1010 return FALSE;
1011 return TRUE;
1014 static void wined3d_surface_depth_blt_fbo(const struct wined3d_device *device, struct wined3d_surface *src_surface,
1015 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1017 const struct wined3d_gl_info *gl_info;
1018 struct wined3d_context *context;
1019 DWORD src_mask, dst_mask;
1020 GLbitfield gl_mask;
1022 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1023 device, src_surface, wine_dbgstr_rect(src_rect),
1024 dst_surface, wine_dbgstr_rect(dst_rect));
1026 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1027 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1029 if (src_mask != dst_mask)
1031 ERR("Incompatible formats %s and %s.\n",
1032 debug_d3dformat(src_surface->resource.format->id),
1033 debug_d3dformat(dst_surface->resource.format->id));
1034 return;
1037 if (!src_mask)
1039 ERR("Not a depth / stencil format: %s.\n",
1040 debug_d3dformat(src_surface->resource.format->id));
1041 return;
1044 gl_mask = 0;
1045 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1046 gl_mask |= GL_DEPTH_BUFFER_BIT;
1047 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1048 gl_mask |= GL_STENCIL_BUFFER_BIT;
1050 /* Make sure the locations are up-to-date. Loading the destination
1051 * surface isn't required if the entire surface is overwritten. */
1052 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1053 if (!surface_is_full_rect(dst_surface, dst_rect))
1054 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1056 context = context_acquire(device, NULL);
1057 if (!context->valid)
1059 context_release(context);
1060 WARN("Invalid context, skipping blit.\n");
1061 return;
1064 gl_info = context->gl_info;
1066 ENTER_GL();
1068 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1069 glReadBuffer(GL_NONE);
1070 checkGLcall("glReadBuffer()");
1071 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1073 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1074 context_set_draw_buffer(context, GL_NONE);
1075 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1077 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1079 glDepthMask(GL_TRUE);
1080 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
1082 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1084 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1086 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1087 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
1089 glStencilMask(~0U);
1090 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
1093 glDisable(GL_SCISSOR_TEST);
1094 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1096 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1097 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1098 checkGLcall("glBlitFramebuffer()");
1100 LEAVE_GL();
1102 if (wined3d_settings.strict_draw_ordering)
1103 wglFlush(); /* Flush to ensure ordering across contexts. */
1105 context_release(context);
1108 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1109 * Depth / stencil is not supported. */
1110 static void surface_blt_fbo(const struct wined3d_device *device, const WINED3DTEXTUREFILTERTYPE filter,
1111 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1112 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1114 const struct wined3d_gl_info *gl_info;
1115 struct wined3d_context *context;
1116 RECT src_rect, dst_rect;
1117 GLenum gl_filter;
1118 GLenum buffer;
1120 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1121 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1122 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1123 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1124 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1126 src_rect = *src_rect_in;
1127 dst_rect = *dst_rect_in;
1129 switch (filter)
1131 case WINED3DTEXF_LINEAR:
1132 gl_filter = GL_LINEAR;
1133 break;
1135 default:
1136 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1137 case WINED3DTEXF_NONE:
1138 case WINED3DTEXF_POINT:
1139 gl_filter = GL_NEAREST;
1140 break;
1143 /* Resolve the source surface first if needed. */
1144 if (src_location == SFLAG_INRB_MULTISAMPLE
1145 && (src_surface->resource.format->id != dst_surface->resource.format->id
1146 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1147 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1148 src_location = SFLAG_INRB_RESOLVED;
1150 /* Make sure the locations are up-to-date. Loading the destination
1151 * surface isn't required if the entire surface is overwritten. (And is
1152 * in fact harmful if we're being called by surface_load_location() with
1153 * the purpose of loading the destination surface.) */
1154 surface_load_location(src_surface, src_location, NULL);
1155 if (!surface_is_full_rect(dst_surface, &dst_rect))
1156 surface_load_location(dst_surface, dst_location, NULL);
1158 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1159 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1160 else context = context_acquire(device, NULL);
1162 if (!context->valid)
1164 context_release(context);
1165 WARN("Invalid context, skipping blit.\n");
1166 return;
1169 gl_info = context->gl_info;
1171 if (src_location == SFLAG_INDRAWABLE)
1173 TRACE("Source surface %p is onscreen.\n", src_surface);
1174 buffer = surface_get_gl_buffer(src_surface);
1175 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1177 else
1179 TRACE("Source surface %p is offscreen.\n", src_surface);
1180 buffer = GL_COLOR_ATTACHMENT0;
1183 ENTER_GL();
1184 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1185 glReadBuffer(buffer);
1186 checkGLcall("glReadBuffer()");
1187 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1188 LEAVE_GL();
1190 if (dst_location == SFLAG_INDRAWABLE)
1192 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1193 buffer = surface_get_gl_buffer(dst_surface);
1194 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1196 else
1198 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1199 buffer = GL_COLOR_ATTACHMENT0;
1202 ENTER_GL();
1203 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1204 context_set_draw_buffer(context, buffer);
1205 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1206 context_invalidate_state(context, STATE_FRAMEBUFFER);
1208 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1209 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
1210 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
1211 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
1212 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
1214 glDisable(GL_SCISSOR_TEST);
1215 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1217 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1218 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1219 checkGLcall("glBlitFramebuffer()");
1221 LEAVE_GL();
1223 if (wined3d_settings.strict_draw_ordering
1224 || (dst_location == SFLAG_INDRAWABLE
1225 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1226 wglFlush();
1228 context_release(context);
1231 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1232 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
1233 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
1235 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1236 return FALSE;
1238 /* Source and/or destination need to be on the GL side */
1239 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
1240 return FALSE;
1242 switch (blit_op)
1244 case WINED3D_BLIT_OP_COLOR_BLIT:
1245 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1246 return FALSE;
1247 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1248 return FALSE;
1249 break;
1251 case WINED3D_BLIT_OP_DEPTH_BLIT:
1252 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1253 return FALSE;
1254 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1255 return FALSE;
1256 break;
1258 default:
1259 return FALSE;
1262 if (!(src_format->id == dst_format->id
1263 || (is_identity_fixup(src_format->color_fixup)
1264 && is_identity_fixup(dst_format->color_fixup))))
1265 return FALSE;
1267 return TRUE;
1270 /* This function checks if the primary render target uses the 8bit paletted format. */
1271 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1273 if (device->fb.render_targets && device->fb.render_targets[0])
1275 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1276 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1277 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1278 return TRUE;
1280 return FALSE;
1283 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1284 DWORD color, struct wined3d_color *float_color)
1286 const struct wined3d_format *format = surface->resource.format;
1287 const struct wined3d_device *device = surface->resource.device;
1289 switch (format->id)
1291 case WINED3DFMT_P8_UINT:
1292 if (surface->palette)
1294 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1295 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1296 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1298 else
1300 float_color->r = 0.0f;
1301 float_color->g = 0.0f;
1302 float_color->b = 0.0f;
1304 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1305 break;
1307 case WINED3DFMT_B5G6R5_UNORM:
1308 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1309 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1310 float_color->b = (color & 0x1f) / 31.0f;
1311 float_color->a = 1.0f;
1312 break;
1314 case WINED3DFMT_B8G8R8_UNORM:
1315 case WINED3DFMT_B8G8R8X8_UNORM:
1316 float_color->r = D3DCOLOR_R(color);
1317 float_color->g = D3DCOLOR_G(color);
1318 float_color->b = D3DCOLOR_B(color);
1319 float_color->a = 1.0f;
1320 break;
1322 case WINED3DFMT_B8G8R8A8_UNORM:
1323 float_color->r = D3DCOLOR_R(color);
1324 float_color->g = D3DCOLOR_G(color);
1325 float_color->b = D3DCOLOR_B(color);
1326 float_color->a = D3DCOLOR_A(color);
1327 break;
1329 default:
1330 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1331 return FALSE;
1334 return TRUE;
1337 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1339 const struct wined3d_format *format = surface->resource.format;
1341 switch (format->id)
1343 case WINED3DFMT_S1_UINT_D15_UNORM:
1344 *float_depth = depth / (float)0x00007fff;
1345 break;
1347 case WINED3DFMT_D16_UNORM:
1348 *float_depth = depth / (float)0x0000ffff;
1349 break;
1351 case WINED3DFMT_D24_UNORM_S8_UINT:
1352 case WINED3DFMT_X8D24_UNORM:
1353 *float_depth = depth / (float)0x00ffffff;
1354 break;
1356 case WINED3DFMT_D32_UNORM:
1357 *float_depth = depth / (float)0xffffffff;
1358 break;
1360 default:
1361 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1362 return FALSE;
1365 return TRUE;
1368 /* Do not call while under the GL lock. */
1369 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1371 const struct wined3d_resource *resource = &surface->resource;
1372 struct wined3d_device *device = resource->device;
1373 const struct blit_shader *blitter;
1375 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1376 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1377 if (!blitter)
1379 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1380 return WINED3DERR_INVALIDCALL;
1383 return blitter->depth_fill(device, surface, rect, depth);
1386 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1387 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1389 struct wined3d_device *device = src_surface->resource.device;
1391 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1392 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1393 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1394 return WINED3DERR_INVALIDCALL;
1396 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1398 surface_modify_ds_location(dst_surface, SFLAG_INTEXTURE,
1399 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1401 return WINED3D_OK;
1404 /* Do not call while under the GL lock. */
1405 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1406 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1407 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1409 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1410 struct wined3d_device *device = dst_surface->resource.device;
1411 DWORD src_ds_flags, dst_ds_flags;
1412 RECT src_rect, dst_rect;
1413 BOOL scale, convert;
1415 static const DWORD simple_blit = WINEDDBLT_ASYNC
1416 | WINEDDBLT_COLORFILL
1417 | WINEDDBLT_WAIT
1418 | WINEDDBLT_DEPTHFILL
1419 | WINEDDBLT_DONOTWAIT;
1421 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1422 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1423 flags, fx, debug_d3dtexturefiltertype(filter));
1424 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1426 if (fx)
1428 TRACE("dwSize %#x.\n", fx->dwSize);
1429 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
1430 TRACE("dwROP %#x.\n", fx->dwROP);
1431 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
1432 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
1433 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
1434 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
1435 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
1436 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
1437 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
1438 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
1439 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
1440 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
1441 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
1442 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
1443 TRACE("dwReserved %#x.\n", fx->dwReserved);
1444 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
1445 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
1446 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
1447 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
1448 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
1449 TRACE("ddckDestColorkey {%#x, %#x}.\n",
1450 fx->ddckDestColorkey.color_space_low_value,
1451 fx->ddckDestColorkey.color_space_high_value);
1452 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
1453 fx->ddckSrcColorkey.color_space_low_value,
1454 fx->ddckSrcColorkey.color_space_high_value);
1457 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1459 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1460 return WINEDDERR_SURFACEBUSY;
1463 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1465 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1466 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1467 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1468 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1469 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1471 WARN("The application gave us a bad destination rectangle.\n");
1472 return WINEDDERR_INVALIDRECT;
1475 if (src_surface)
1477 surface_get_rect(src_surface, src_rect_in, &src_rect);
1479 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1480 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1481 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1482 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1483 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1485 WARN("Application gave us bad source rectangle for Blt.\n");
1486 return WINEDDERR_INVALIDRECT;
1489 else
1491 memset(&src_rect, 0, sizeof(src_rect));
1494 if (!fx || !(fx->dwDDFX))
1495 flags &= ~WINEDDBLT_DDFX;
1497 if (flags & WINEDDBLT_WAIT)
1498 flags &= ~WINEDDBLT_WAIT;
1500 if (flags & WINEDDBLT_ASYNC)
1502 static unsigned int once;
1504 if (!once++)
1505 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1506 flags &= ~WINEDDBLT_ASYNC;
1509 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1510 if (flags & WINEDDBLT_DONOTWAIT)
1512 static unsigned int once;
1514 if (!once++)
1515 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1516 flags &= ~WINEDDBLT_DONOTWAIT;
1519 if (!device->d3d_initialized)
1521 WARN("D3D not initialized, using fallback.\n");
1522 goto cpu;
1525 /* We want to avoid invalidating the sysmem location for converted
1526 * surfaces, since otherwise we'd have to convert the data back when
1527 * locking them. */
1528 if (dst_surface->flags & SFLAG_CONVERTED)
1530 WARN("Converted surface, using CPU blit.\n");
1531 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1534 if (flags & ~simple_blit)
1536 WARN("Using fallback for complex blit (%#x).\n", flags);
1537 goto fallback;
1540 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1541 src_swapchain = src_surface->container.u.swapchain;
1542 else
1543 src_swapchain = NULL;
1545 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1546 dst_swapchain = dst_surface->container.u.swapchain;
1547 else
1548 dst_swapchain = NULL;
1550 /* This isn't strictly needed. FBO blits for example could deal with
1551 * cross-swapchain blits by first downloading the source to a texture
1552 * before switching to the destination context. We just have this here to
1553 * not have to deal with the issue, since cross-swapchain blits should be
1554 * rare. */
1555 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1557 FIXME("Using fallback for cross-swapchain blit.\n");
1558 goto fallback;
1561 scale = src_surface
1562 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
1563 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
1564 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
1566 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1567 if (src_surface)
1568 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1569 else
1570 src_ds_flags = 0;
1572 if (src_ds_flags || dst_ds_flags)
1574 if (flags & WINEDDBLT_DEPTHFILL)
1576 float depth;
1578 TRACE("Depth fill.\n");
1580 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1581 return WINED3DERR_INVALIDCALL;
1583 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1584 return WINED3D_OK;
1586 else
1588 /* Accessing depth / stencil surfaces is supposed to fail while in
1589 * a scene, except for fills, which seem to work. */
1590 if (device->inScene)
1592 WARN("Rejecting depth / stencil access while in scene.\n");
1593 return WINED3DERR_INVALIDCALL;
1596 if (src_ds_flags != dst_ds_flags)
1598 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1599 return WINED3DERR_INVALIDCALL;
1602 if (src_rect.top || src_rect.left
1603 || src_rect.bottom != src_surface->resource.height
1604 || src_rect.right != src_surface->resource.width)
1606 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1607 wine_dbgstr_rect(&src_rect));
1608 return WINED3DERR_INVALIDCALL;
1611 if (dst_rect.top || dst_rect.left
1612 || dst_rect.bottom != dst_surface->resource.height
1613 || dst_rect.right != dst_surface->resource.width)
1615 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1616 wine_dbgstr_rect(&src_rect));
1617 return WINED3DERR_INVALIDCALL;
1620 if (scale)
1622 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1623 return WINED3DERR_INVALIDCALL;
1626 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1627 return WINED3D_OK;
1630 else
1632 /* In principle this would apply to depth blits as well, but we don't
1633 * implement those in the CPU blitter at the moment. */
1634 if ((dst_surface->flags & SFLAG_INSYSMEM)
1635 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
1637 if (scale)
1638 TRACE("Not doing sysmem blit because of scaling.\n");
1639 else if (convert)
1640 TRACE("Not doing sysmem blit because of format conversion.\n");
1641 else
1642 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1645 if (flags & WINEDDBLT_COLORFILL)
1647 struct wined3d_color color;
1649 TRACE("Color fill.\n");
1651 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1652 goto fallback;
1654 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1655 return WINED3D_OK;
1657 else
1659 TRACE("Color blit.\n");
1661 /* Upload */
1662 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
1664 if (scale)
1665 TRACE("Not doing upload because of scaling.\n");
1666 else if (convert)
1667 TRACE("Not doing upload because of format conversion.\n");
1668 else
1670 POINT dst_point = {dst_rect.left, dst_rect.top};
1672 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
1674 if (!surface_is_offscreen(dst_surface))
1675 surface_load_location(dst_surface, dst_surface->draw_binding, NULL);
1676 return WINED3D_OK;
1681 /* Use present for back -> front blits. The idea behind this is
1682 * that present is potentially faster than a blit, in particular
1683 * when FBO blits aren't available. Some ddraw applications like
1684 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
1685 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
1686 * applications can't blit directly to the frontbuffer. */
1687 if (dst_swapchain && dst_swapchain->back_buffers
1688 && dst_surface == dst_swapchain->front_buffer
1689 && src_surface == dst_swapchain->back_buffers[0])
1691 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
1693 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1695 /* Set the swap effect to COPY, we don't want the backbuffer
1696 * to become undefined. */
1697 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
1698 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
1699 dst_swapchain->desc.swap_effect = swap_effect;
1701 return WINED3D_OK;
1704 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1705 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1706 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1708 TRACE("Using FBO blit.\n");
1710 surface_blt_fbo(device, filter,
1711 src_surface, src_surface->draw_binding, &src_rect,
1712 dst_surface, dst_surface->draw_binding, &dst_rect);
1713 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1714 return WINED3D_OK;
1717 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1718 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1719 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1721 TRACE("Using arbfp blit.\n");
1723 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1724 return WINED3D_OK;
1729 fallback:
1731 /* Special cases for render targets. */
1732 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1733 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1735 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1736 src_surface, &src_rect, flags, fx, filter)))
1737 return WINED3D_OK;
1740 cpu:
1742 /* For the rest call the X11 surface implementation. For render targets
1743 * this should be implemented OpenGL accelerated in BltOverride, other
1744 * blits are rather rare. */
1745 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1748 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1749 struct wined3d_surface *render_target)
1751 TRACE("surface %p, render_target %p.\n", surface, render_target);
1753 /* TODO: Check surface sizes, pools, etc. */
1755 if (render_target->resource.multisample_type)
1756 return WINED3DERR_INVALIDCALL;
1758 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3DTEXF_POINT);
1761 /* Context activation is done by the caller. */
1762 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1764 if (surface->flags & SFLAG_DIBSECTION)
1766 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1768 else
1770 if (!surface->resource.heapMemory)
1771 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1772 else if (!(surface->flags & SFLAG_CLIENT))
1773 ERR("Surface %p has heapMemory %p and flags %#x.\n",
1774 surface, surface->resource.heapMemory, surface->flags);
1776 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1777 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1780 ENTER_GL();
1781 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1782 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1783 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1784 surface->resource.size, surface->resource.allocatedMemory));
1785 checkGLcall("glGetBufferSubDataARB");
1786 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1787 checkGLcall("glDeleteBuffersARB");
1788 LEAVE_GL();
1790 surface->pbo = 0;
1791 surface->flags &= ~SFLAG_PBO;
1794 /* Do not call while under the GL lock. */
1795 static void surface_unload(struct wined3d_resource *resource)
1797 struct wined3d_surface *surface = surface_from_resource(resource);
1798 struct wined3d_renderbuffer_entry *entry, *entry2;
1799 struct wined3d_device *device = resource->device;
1800 const struct wined3d_gl_info *gl_info;
1801 struct wined3d_context *context;
1803 TRACE("surface %p.\n", surface);
1805 if (resource->pool == WINED3DPOOL_DEFAULT)
1807 /* Default pool resources are supposed to be destroyed before Reset is called.
1808 * Implicit resources stay however. So this means we have an implicit render target
1809 * or depth stencil. The content may be destroyed, but we still have to tear down
1810 * opengl resources, so we cannot leave early.
1812 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1813 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1814 * or the depth stencil into an FBO the texture or render buffer will be removed
1815 * and all flags get lost
1817 if (!(surface->flags & SFLAG_PBO))
1818 surface_init_sysmem(surface);
1819 /* We also get here when the ddraw swapchain is destroyed, for example
1820 * for a mode switch. In this case this surface won't necessarily be
1821 * an implicit surface. We have to mark it lost so that the
1822 * application can restore it after the mode switch. */
1823 surface->flags |= SFLAG_LOST;
1825 else
1827 /* Load the surface into system memory */
1828 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1829 surface_modify_location(surface, surface->draw_binding, FALSE);
1831 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1832 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1833 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1835 context = context_acquire(device, NULL);
1836 gl_info = context->gl_info;
1838 /* Destroy PBOs, but load them into real sysmem before */
1839 if (surface->flags & SFLAG_PBO)
1840 surface_remove_pbo(surface, gl_info);
1842 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1843 * all application-created targets the application has to release the surface
1844 * before calling _Reset
1846 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1848 ENTER_GL();
1849 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1850 LEAVE_GL();
1851 list_remove(&entry->entry);
1852 HeapFree(GetProcessHeap(), 0, entry);
1854 list_init(&surface->renderbuffers);
1855 surface->current_renderbuffer = NULL;
1857 ENTER_GL();
1859 /* If we're in a texture, the texture name belongs to the texture.
1860 * Otherwise, destroy it. */
1861 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1863 glDeleteTextures(1, &surface->texture_name);
1864 surface->texture_name = 0;
1865 glDeleteTextures(1, &surface->texture_name_srgb);
1866 surface->texture_name_srgb = 0;
1868 if (surface->rb_multisample)
1870 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1871 surface->rb_multisample = 0;
1873 if (surface->rb_resolved)
1875 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1876 surface->rb_resolved = 0;
1879 LEAVE_GL();
1881 context_release(context);
1883 resource_unload(resource);
1886 static const struct wined3d_resource_ops surface_resource_ops =
1888 surface_unload,
1891 static const struct wined3d_surface_ops surface_ops =
1893 surface_private_setup,
1894 surface_realize_palette,
1895 surface_map,
1896 surface_unmap,
1899 /*****************************************************************************
1900 * Initializes the GDI surface, aka creates the DIB section we render to
1901 * The DIB section creation is done by calling GetDC, which will create the
1902 * section and releasing the dc to allow the app to use it. The dib section
1903 * will stay until the surface is released
1905 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1906 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1907 * avoid confusion in the shared surface code.
1909 * Returns:
1910 * WINED3D_OK on success
1911 * The return values of called methods on failure
1913 *****************************************************************************/
1914 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1916 HRESULT hr;
1918 TRACE("surface %p.\n", surface);
1920 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1922 ERR("Overlays not yet supported by GDI surfaces.\n");
1923 return WINED3DERR_INVALIDCALL;
1926 /* Sysmem textures have memory already allocated - release it,
1927 * this avoids an unnecessary memcpy. */
1928 hr = surface_create_dib_section(surface);
1929 if (SUCCEEDED(hr))
1931 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1932 surface->resource.heapMemory = NULL;
1933 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1936 /* We don't mind the nonpow2 stuff in GDI. */
1937 surface->pow2Width = surface->resource.width;
1938 surface->pow2Height = surface->resource.height;
1940 return WINED3D_OK;
1943 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1945 struct wined3d_palette *palette = surface->palette;
1947 TRACE("surface %p.\n", surface);
1949 if (!palette) return;
1951 if (surface->flags & SFLAG_DIBSECTION)
1953 RGBQUAD col[256];
1954 unsigned int i;
1956 TRACE("Updating the DC's palette.\n");
1958 for (i = 0; i < 256; ++i)
1960 col[i].rgbRed = palette->palents[i].peRed;
1961 col[i].rgbGreen = palette->palents[i].peGreen;
1962 col[i].rgbBlue = palette->palents[i].peBlue;
1963 col[i].rgbReserved = 0;
1965 SetDIBColorTable(surface->hDC, 0, 256, col);
1968 /* Update the image because of the palette change. Some games like e.g.
1969 * Red Alert call SetEntries a lot to implement fading. */
1970 /* Tell the swapchain to update the screen. */
1971 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1973 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1974 if (surface == swapchain->front_buffer)
1976 x11_copy_to_screen(swapchain, NULL);
1981 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
1983 TRACE("surface %p, rect %s, flags %#x.\n",
1984 surface, wine_dbgstr_rect(rect), flags);
1986 if (!(surface->flags & SFLAG_DIBSECTION))
1988 /* This happens on gdi surfaces if the application set a user pointer
1989 * and resets it. Recreate the DIB section. */
1990 surface_create_dib_section(surface);
1991 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1995 static void gdi_surface_unmap(struct wined3d_surface *surface)
1997 TRACE("surface %p.\n", surface);
1999 /* Tell the swapchain to update the screen. */
2000 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2002 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2003 if (surface == swapchain->front_buffer)
2005 x11_copy_to_screen(swapchain, &surface->lockedRect);
2009 memset(&surface->lockedRect, 0, sizeof(RECT));
2012 static const struct wined3d_surface_ops gdi_surface_ops =
2014 gdi_surface_private_setup,
2015 gdi_surface_realize_palette,
2016 gdi_surface_map,
2017 gdi_surface_unmap,
2020 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2022 GLuint *name;
2023 DWORD flag;
2025 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2027 if(srgb)
2029 name = &surface->texture_name_srgb;
2030 flag = SFLAG_INSRGBTEX;
2032 else
2034 name = &surface->texture_name;
2035 flag = SFLAG_INTEXTURE;
2038 if (!*name && new_name)
2040 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2041 * surface has no texture name yet. See if we can get rid of this. */
2042 if (surface->flags & flag)
2044 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2045 surface_modify_location(surface, flag, FALSE);
2049 *name = new_name;
2050 surface_force_reload(surface);
2053 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2055 TRACE("surface %p, target %#x.\n", surface, target);
2057 if (surface->texture_target != target)
2059 if (target == GL_TEXTURE_RECTANGLE_ARB)
2061 surface->flags &= ~SFLAG_NORMCOORD;
2063 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2065 surface->flags |= SFLAG_NORMCOORD;
2068 surface->texture_target = target;
2069 surface_force_reload(surface);
2072 /* Context activation is done by the caller. */
2073 void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
2075 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
2077 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2079 struct wined3d_texture *texture = surface->container.u.texture;
2081 TRACE("Passing to container (%p).\n", texture);
2082 texture->texture_ops->texture_bind(texture, context, srgb);
2084 else
2086 if (surface->texture_level)
2088 ERR("Standalone surface %p is non-zero texture level %u.\n",
2089 surface, surface->texture_level);
2092 if (srgb)
2093 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2095 ENTER_GL();
2097 if (!surface->texture_name)
2099 glGenTextures(1, &surface->texture_name);
2100 checkGLcall("glGenTextures");
2102 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2104 context_bind_texture(context, surface->texture_target, surface->texture_name);
2105 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2106 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2107 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2108 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2109 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2110 checkGLcall("glTexParameteri");
2112 else
2114 context_bind_texture(context, surface->texture_target, surface->texture_name);
2117 LEAVE_GL();
2121 /* This call just downloads data, the caller is responsible for binding the
2122 * correct texture. */
2123 /* Context activation is done by the caller. */
2124 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2126 const struct wined3d_format *format = surface->resource.format;
2128 /* Only support read back of converted P8 surfaces. */
2129 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2131 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
2132 return;
2135 ENTER_GL();
2137 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2139 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2140 surface, surface->texture_level, format->glFormat, format->glType,
2141 surface->resource.allocatedMemory);
2143 if (surface->flags & SFLAG_PBO)
2145 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2146 checkGLcall("glBindBufferARB");
2147 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2148 checkGLcall("glGetCompressedTexImageARB");
2149 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2150 checkGLcall("glBindBufferARB");
2152 else
2154 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2155 surface->texture_level, surface->resource.allocatedMemory));
2156 checkGLcall("glGetCompressedTexImageARB");
2159 LEAVE_GL();
2161 else
2163 void *mem;
2164 GLenum gl_format = format->glFormat;
2165 GLenum gl_type = format->glType;
2166 int src_pitch = 0;
2167 int dst_pitch = 0;
2169 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2170 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2172 gl_format = GL_ALPHA;
2173 gl_type = GL_UNSIGNED_BYTE;
2176 if (surface->flags & SFLAG_NONPOW2)
2178 unsigned char alignment = surface->resource.device->surface_alignment;
2179 src_pitch = format->byte_count * surface->pow2Width;
2180 dst_pitch = wined3d_surface_get_pitch(surface);
2181 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2182 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2184 else
2186 mem = surface->resource.allocatedMemory;
2189 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2190 surface, surface->texture_level, gl_format, gl_type, mem);
2192 if (surface->flags & SFLAG_PBO)
2194 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2195 checkGLcall("glBindBufferARB");
2197 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2198 checkGLcall("glGetTexImage");
2200 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2201 checkGLcall("glBindBufferARB");
2203 else
2205 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2206 checkGLcall("glGetTexImage");
2208 LEAVE_GL();
2210 if (surface->flags & SFLAG_NONPOW2)
2212 const BYTE *src_data;
2213 BYTE *dst_data;
2214 UINT y;
2216 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2217 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2218 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2220 * We're doing this...
2222 * instead of boxing the texture :
2223 * |<-texture width ->| -->pow2width| /\
2224 * |111111111111111111| | |
2225 * |222 Texture 222222| boxed empty | texture height
2226 * |3333 Data 33333333| | |
2227 * |444444444444444444| | \/
2228 * ----------------------------------- |
2229 * | boxed empty | boxed empty | pow2height
2230 * | | | \/
2231 * -----------------------------------
2234 * we're repacking the data to the expected texture width
2236 * |<-texture width ->| -->pow2width| /\
2237 * |111111111111111111222222222222222| |
2238 * |222333333333333333333444444444444| texture height
2239 * |444444 | |
2240 * | | \/
2241 * | | |
2242 * | empty | pow2height
2243 * | | \/
2244 * -----------------------------------
2246 * == is the same as
2248 * |<-texture width ->| /\
2249 * |111111111111111111|
2250 * |222222222222222222|texture height
2251 * |333333333333333333|
2252 * |444444444444444444| \/
2253 * --------------------
2255 * this also means that any references to allocatedMemory should work with the data as if were a
2256 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2258 * internally the texture is still stored in a boxed format so any references to textureName will
2259 * get a boxed texture with width pow2width and not a texture of width resource.width.
2261 * Performance should not be an issue, because applications normally do not lock the surfaces when
2262 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2263 * and doesn't have to be re-read. */
2264 src_data = mem;
2265 dst_data = surface->resource.allocatedMemory;
2266 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2267 for (y = 1; y < surface->resource.height; ++y)
2269 /* skip the first row */
2270 src_data += src_pitch;
2271 dst_data += dst_pitch;
2272 memcpy(dst_data, src_data, dst_pitch);
2275 HeapFree(GetProcessHeap(), 0, mem);
2279 /* Surface has now been downloaded */
2280 surface->flags |= SFLAG_INSYSMEM;
2283 /* This call just uploads data, the caller is responsible for binding the
2284 * correct texture. */
2285 /* Context activation is done by the caller. */
2286 static void surface_upload_data(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2287 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
2288 BOOL srgb, const struct wined3d_bo_address *data)
2290 UINT update_w = src_rect->right - src_rect->left;
2291 UINT update_h = src_rect->bottom - src_rect->top;
2293 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
2294 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
2295 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2297 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2298 update_h *= format->heightscale;
2300 ENTER_GL();
2302 if (data->buffer_object)
2304 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2305 checkGLcall("glBindBufferARB");
2308 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2310 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2311 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2312 const BYTE *addr = data->addr;
2313 GLenum internal;
2315 addr += (src_rect->top / format->block_height) * src_pitch;
2316 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2318 if (srgb)
2319 internal = format->glGammaInternal;
2320 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2321 internal = format->rtInternal;
2322 else
2323 internal = format->glInternal;
2325 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2326 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2327 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2329 if (row_length == src_pitch)
2331 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2332 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2334 else
2336 UINT row, y;
2338 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2339 * can't use the unpack row length like below. */
2340 for (row = 0, y = dst_point->y; row < row_count; ++row)
2342 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2343 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2344 y += format->block_height;
2345 addr += src_pitch;
2348 checkGLcall("glCompressedTexSubImage2DARB");
2350 else
2352 const BYTE *addr = data->addr;
2354 addr += src_rect->top * src_pitch;
2355 addr += src_rect->left * format->byte_count;
2357 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2358 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2359 update_w, update_h, format->glFormat, format->glType, addr);
2361 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
2362 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2363 update_w, update_h, format->glFormat, format->glType, addr);
2364 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2365 checkGLcall("glTexSubImage2D");
2368 if (data->buffer_object)
2370 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2371 checkGLcall("glBindBufferARB");
2374 LEAVE_GL();
2376 if (wined3d_settings.strict_draw_ordering)
2377 wglFlush();
2379 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2381 struct wined3d_device *device = surface->resource.device;
2382 unsigned int i;
2384 for (i = 0; i < device->context_count; ++i)
2386 context_surface_update(device->contexts[i], surface);
2391 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
2392 struct wined3d_surface *src_surface, const RECT *src_rect)
2394 const struct wined3d_format *src_format;
2395 const struct wined3d_format *dst_format;
2396 const struct wined3d_gl_info *gl_info;
2397 struct wined3d_context *context;
2398 struct wined3d_bo_address data;
2399 struct wined3d_format format;
2400 UINT update_w, update_h;
2401 CONVERT_TYPES convert;
2402 UINT dst_w, dst_h;
2403 UINT src_w, src_h;
2404 UINT src_pitch;
2405 POINT p;
2406 RECT r;
2408 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
2409 dst_surface, wine_dbgstr_point(dst_point),
2410 src_surface, wine_dbgstr_rect(src_rect));
2412 src_format = src_surface->resource.format;
2413 dst_format = dst_surface->resource.format;
2415 if (src_format->id != dst_format->id)
2417 WARN("Source and destination surfaces should have the same format.\n");
2418 return WINED3DERR_INVALIDCALL;
2421 if (!dst_point)
2423 p.x = 0;
2424 p.y = 0;
2425 dst_point = &p;
2427 else if (dst_point->x < 0 || dst_point->y < 0)
2429 WARN("Invalid destination point.\n");
2430 return WINED3DERR_INVALIDCALL;
2433 if (!src_rect)
2435 r.left = 0;
2436 r.top = 0;
2437 r.right = src_surface->resource.width;
2438 r.bottom = src_surface->resource.height;
2439 src_rect = &r;
2441 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
2442 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
2444 WARN("Invalid source rectangle.\n");
2445 return WINED3DERR_INVALIDCALL;
2448 src_w = src_surface->resource.width;
2449 src_h = src_surface->resource.height;
2451 dst_w = dst_surface->resource.width;
2452 dst_h = dst_surface->resource.height;
2454 update_w = src_rect->right - src_rect->left;
2455 update_h = src_rect->bottom - src_rect->top;
2457 if (update_w > dst_w || dst_point->x > dst_w - update_w
2458 || update_h > dst_h || dst_point->y > dst_h - update_h)
2460 WARN("Destination out of bounds.\n");
2461 return WINED3DERR_INVALIDCALL;
2464 /* NPOT block sizes would be silly. */
2465 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS)
2466 && ((update_w & (src_format->block_width - 1) || update_h & (src_format->block_height - 1))
2467 && (src_w != update_w || dst_w != update_w || src_h != update_h || dst_h != update_h)))
2469 WARN("Update rect not block-aligned.\n");
2470 return WINED3DERR_INVALIDCALL;
2473 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
2474 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
2475 if (convert != NO_CONVERSION || format.convert)
2477 RECT dst_rect = {dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h};
2478 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3DTEXF_POINT);
2481 context = context_acquire(dst_surface->resource.device, NULL);
2482 gl_info = context->gl_info;
2484 /* Only load the surface for partial updates. For newly allocated texture
2485 * the texture wouldn't be the current location, and we'd upload zeroes
2486 * just to overwrite them again. */
2487 if (update_w == dst_w && update_h == dst_h)
2488 surface_prepare_texture(dst_surface, context, FALSE);
2489 else
2490 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
2491 surface_bind(dst_surface, context, FALSE);
2493 data.buffer_object = src_surface->pbo;
2494 data.addr = src_surface->resource.allocatedMemory;
2495 src_pitch = wined3d_surface_get_pitch(src_surface);
2497 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
2499 invalidate_active_texture(dst_surface->resource.device, context);
2501 context_release(context);
2503 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
2504 return WINED3D_OK;
2507 /* This call just allocates the texture, the caller is responsible for binding
2508 * the correct texture. */
2509 /* Context activation is done by the caller. */
2510 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2511 const struct wined3d_format *format, BOOL srgb)
2513 BOOL enable_client_storage = FALSE;
2514 GLsizei width = surface->pow2Width;
2515 GLsizei height = surface->pow2Height;
2516 const BYTE *mem = NULL;
2517 GLenum internal;
2519 if (srgb)
2521 internal = format->glGammaInternal;
2523 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2525 internal = format->rtInternal;
2527 else
2529 internal = format->glInternal;
2532 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2534 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",
2535 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2536 internal, width, height, format->glFormat, format->glType);
2538 ENTER_GL();
2540 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2542 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2543 || !surface->resource.allocatedMemory)
2545 /* In some cases we want to disable client storage.
2546 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2547 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2548 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2549 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2551 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2552 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2553 surface->flags &= ~SFLAG_CLIENT;
2554 enable_client_storage = TRUE;
2556 else
2558 surface->flags |= SFLAG_CLIENT;
2560 /* Point OpenGL to our allocated texture memory. Do not use
2561 * resource.allocatedMemory here because it might point into a
2562 * PBO. Instead use heapMemory, but get the alignment right. */
2563 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2564 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2568 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2570 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2571 internal, width, height, 0, surface->resource.size, mem));
2572 checkGLcall("glCompressedTexImage2DARB");
2574 else
2576 glTexImage2D(surface->texture_target, surface->texture_level,
2577 internal, width, height, 0, format->glFormat, format->glType, mem);
2578 checkGLcall("glTexImage2D");
2581 if(enable_client_storage) {
2582 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2583 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2585 LEAVE_GL();
2588 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2589 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2590 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2591 /* GL locking is done by the caller */
2592 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2594 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2595 struct wined3d_renderbuffer_entry *entry;
2596 GLuint renderbuffer = 0;
2597 unsigned int src_width, src_height;
2598 unsigned int width, height;
2600 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2602 width = rt->pow2Width;
2603 height = rt->pow2Height;
2605 else
2607 width = surface->pow2Width;
2608 height = surface->pow2Height;
2611 src_width = surface->pow2Width;
2612 src_height = surface->pow2Height;
2614 /* A depth stencil smaller than the render target is not valid */
2615 if (width > src_width || height > src_height) return;
2617 /* Remove any renderbuffer set if the sizes match */
2618 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2619 || (width == src_width && height == src_height))
2621 surface->current_renderbuffer = NULL;
2622 return;
2625 /* Look if we've already got a renderbuffer of the correct dimensions */
2626 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2628 if (entry->width == width && entry->height == height)
2630 renderbuffer = entry->id;
2631 surface->current_renderbuffer = entry;
2632 break;
2636 if (!renderbuffer)
2638 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2639 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2640 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2641 surface->resource.format->glInternal, width, height);
2643 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2644 entry->width = width;
2645 entry->height = height;
2646 entry->id = renderbuffer;
2647 list_add_head(&surface->renderbuffers, &entry->entry);
2649 surface->current_renderbuffer = entry;
2652 checkGLcall("set_compatible_renderbuffer");
2655 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2657 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2659 TRACE("surface %p.\n", surface);
2661 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2663 ERR("Surface %p is not on a swapchain.\n", surface);
2664 return GL_NONE;
2667 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2669 if (swapchain->render_to_fbo)
2671 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2672 return GL_COLOR_ATTACHMENT0;
2674 TRACE("Returning GL_BACK\n");
2675 return GL_BACK;
2677 else if (surface == swapchain->front_buffer)
2679 TRACE("Returning GL_FRONT\n");
2680 return GL_FRONT;
2683 FIXME("Higher back buffer, returning GL_BACK\n");
2684 return GL_BACK;
2687 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2688 void surface_add_dirty_rect(struct wined3d_surface *surface, const struct wined3d_box *dirty_rect)
2690 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2692 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2693 /* No partial locking for textures yet. */
2694 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2696 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2697 if (dirty_rect)
2699 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->left);
2700 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->top);
2701 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->right);
2702 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->bottom);
2704 else
2706 surface->dirtyRect.left = 0;
2707 surface->dirtyRect.top = 0;
2708 surface->dirtyRect.right = surface->resource.width;
2709 surface->dirtyRect.bottom = surface->resource.height;
2712 /* if the container is a texture then mark it dirty. */
2713 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2715 TRACE("Passing to container.\n");
2716 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2720 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2722 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2723 BOOL ck_changed;
2725 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2727 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2729 ERR("Not supported on scratch surfaces.\n");
2730 return WINED3DERR_INVALIDCALL;
2733 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2735 /* Reload if either the texture and sysmem have different ideas about the
2736 * color key, or the actual key values changed. */
2737 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2738 && (surface->gl_color_key.color_space_low_value != surface->src_blt_color_key.color_space_low_value
2739 || surface->gl_color_key.color_space_high_value != surface->src_blt_color_key.color_space_high_value)))
2741 TRACE("Reloading because of color keying\n");
2742 /* To perform the color key conversion we need a sysmem copy of
2743 * the surface. Make sure we have it. */
2745 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2746 /* Make sure the texture is reloaded because of the color key change,
2747 * this kills performance though :( */
2748 /* TODO: This is not necessarily needed with hw palettized texture support. */
2749 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2750 /* Switching color keying on / off may change the internal format. */
2751 if (ck_changed)
2752 surface_force_reload(surface);
2754 else if (!(surface->flags & flag))
2756 TRACE("Reloading because surface is dirty.\n");
2758 else
2760 TRACE("surface is already in texture\n");
2761 return WINED3D_OK;
2764 /* No partial locking for textures yet. */
2765 surface_load_location(surface, flag, NULL);
2766 surface_evict_sysmem(surface);
2768 return WINED3D_OK;
2771 /* See also float_16_to_32() in wined3d_private.h */
2772 static inline unsigned short float_32_to_16(const float *in)
2774 int exp = 0;
2775 float tmp = fabsf(*in);
2776 unsigned int mantissa;
2777 unsigned short ret;
2779 /* Deal with special numbers */
2780 if (*in == 0.0f)
2781 return 0x0000;
2782 if (isnan(*in))
2783 return 0x7c01;
2784 if (isinf(*in))
2785 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2787 if (tmp < powf(2, 10))
2791 tmp = tmp * 2.0f;
2792 exp--;
2793 } while (tmp < powf(2, 10));
2795 else if (tmp >= powf(2, 11))
2799 tmp /= 2.0f;
2800 exp++;
2801 } while (tmp >= powf(2, 11));
2804 mantissa = (unsigned int)tmp;
2805 if (tmp - mantissa >= 0.5f)
2806 ++mantissa; /* Round to nearest, away from zero. */
2808 exp += 10; /* Normalize the mantissa. */
2809 exp += 15; /* Exponent is encoded with excess 15. */
2811 if (exp > 30) /* too big */
2813 ret = 0x7c00; /* INF */
2815 else if (exp <= 0)
2817 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2818 while (exp <= 0)
2820 mantissa = mantissa >> 1;
2821 ++exp;
2823 ret = mantissa & 0x3ff;
2825 else
2827 ret = (exp << 10) | (mantissa & 0x3ff);
2830 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2831 return ret;
2834 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2836 ULONG refcount;
2838 TRACE("Surface %p, container %p of type %#x.\n",
2839 surface, surface->container.u.base, surface->container.type);
2841 switch (surface->container.type)
2843 case WINED3D_CONTAINER_TEXTURE:
2844 return wined3d_texture_incref(surface->container.u.texture);
2846 case WINED3D_CONTAINER_SWAPCHAIN:
2847 return wined3d_swapchain_incref(surface->container.u.swapchain);
2849 default:
2850 ERR("Unhandled container type %#x.\n", surface->container.type);
2851 case WINED3D_CONTAINER_NONE:
2852 break;
2855 refcount = InterlockedIncrement(&surface->resource.ref);
2856 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2858 return refcount;
2861 /* Do not call while under the GL lock. */
2862 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2864 ULONG refcount;
2866 TRACE("Surface %p, container %p of type %#x.\n",
2867 surface, surface->container.u.base, surface->container.type);
2869 switch (surface->container.type)
2871 case WINED3D_CONTAINER_TEXTURE:
2872 return wined3d_texture_decref(surface->container.u.texture);
2874 case WINED3D_CONTAINER_SWAPCHAIN:
2875 return wined3d_swapchain_decref(surface->container.u.swapchain);
2877 default:
2878 ERR("Unhandled container type %#x.\n", surface->container.type);
2879 case WINED3D_CONTAINER_NONE:
2880 break;
2883 refcount = InterlockedDecrement(&surface->resource.ref);
2884 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2886 if (!refcount)
2888 surface_cleanup(surface);
2889 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2891 TRACE("Destroyed surface %p.\n", surface);
2892 HeapFree(GetProcessHeap(), 0, surface);
2895 return refcount;
2898 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2900 return resource_set_priority(&surface->resource, priority);
2903 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2905 return resource_get_priority(&surface->resource);
2908 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2910 TRACE("surface %p.\n", surface);
2912 if (!surface->resource.device->d3d_initialized)
2914 ERR("D3D not initialized.\n");
2915 return;
2918 surface_internal_preload(surface, SRGB_ANY);
2921 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2923 TRACE("surface %p.\n", surface);
2925 return surface->resource.parent;
2928 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2930 TRACE("surface %p.\n", surface);
2932 return &surface->resource;
2935 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2937 TRACE("surface %p, flags %#x.\n", surface, flags);
2939 switch (flags)
2941 case WINEDDGBS_CANBLT:
2942 case WINEDDGBS_ISBLTDONE:
2943 return WINED3D_OK;
2945 default:
2946 return WINED3DERR_INVALIDCALL;
2950 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2952 TRACE("surface %p, flags %#x.\n", surface, flags);
2954 /* XXX: DDERR_INVALIDSURFACETYPE */
2956 switch (flags)
2958 case WINEDDGFS_CANFLIP:
2959 case WINEDDGFS_ISFLIPDONE:
2960 return WINED3D_OK;
2962 default:
2963 return WINED3DERR_INVALIDCALL;
2967 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2969 TRACE("surface %p.\n", surface);
2971 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2972 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2975 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2977 TRACE("surface %p.\n", surface);
2979 surface->flags &= ~SFLAG_LOST;
2980 return WINED3D_OK;
2983 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2985 TRACE("surface %p, palette %p.\n", surface, palette);
2987 if (surface->palette == palette)
2989 TRACE("Nop palette change.\n");
2990 return WINED3D_OK;
2993 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2994 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2996 surface->palette = palette;
2998 if (palette)
3000 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3001 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
3003 surface->surface_ops->surface_realize_palette(surface);
3006 return WINED3D_OK;
3009 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
3010 DWORD flags, const struct wined3d_color_key *color_key)
3012 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3014 if (flags & WINEDDCKEY_COLORSPACE)
3016 FIXME(" colorkey value not supported (%08x) !\n", flags);
3017 return WINED3DERR_INVALIDCALL;
3020 /* Dirtify the surface, but only if a key was changed. */
3021 if (color_key)
3023 switch (flags & ~WINEDDCKEY_COLORSPACE)
3025 case WINEDDCKEY_DESTBLT:
3026 surface->dst_blt_color_key = *color_key;
3027 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3028 break;
3030 case WINEDDCKEY_DESTOVERLAY:
3031 surface->dst_overlay_color_key = *color_key;
3032 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3033 break;
3035 case WINEDDCKEY_SRCOVERLAY:
3036 surface->src_overlay_color_key = *color_key;
3037 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3038 break;
3040 case WINEDDCKEY_SRCBLT:
3041 surface->src_blt_color_key = *color_key;
3042 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3043 break;
3046 else
3048 switch (flags & ~WINEDDCKEY_COLORSPACE)
3050 case WINEDDCKEY_DESTBLT:
3051 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3052 break;
3054 case WINEDDCKEY_DESTOVERLAY:
3055 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3056 break;
3058 case WINEDDCKEY_SRCOVERLAY:
3059 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3060 break;
3062 case WINEDDCKEY_SRCBLT:
3063 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3064 break;
3068 return WINED3D_OK;
3071 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3073 TRACE("surface %p.\n", surface);
3075 return surface->palette;
3078 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3080 const struct wined3d_format *format = surface->resource.format;
3081 DWORD pitch;
3083 TRACE("surface %p.\n", surface);
3085 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
3087 /* Since compressed formats are block based, pitch means the amount of
3088 * bytes to the next row of block rather than the next row of pixels. */
3089 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3090 pitch = row_block_count * format->block_byte_count;
3092 else
3094 unsigned char alignment = surface->resource.device->surface_alignment;
3095 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3096 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3099 TRACE("Returning %u.\n", pitch);
3101 return pitch;
3104 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3106 TRACE("surface %p, mem %p.\n", surface, mem);
3108 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3110 WARN("Surface is locked or the DC is in use.\n");
3111 return WINED3DERR_INVALIDCALL;
3114 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
3115 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3117 ERR("Not supported on render targets.\n");
3118 return WINED3DERR_INVALIDCALL;
3121 if (mem && mem != surface->resource.allocatedMemory)
3123 void *release = NULL;
3125 /* Do I have to copy the old surface content? */
3126 if (surface->flags & SFLAG_DIBSECTION)
3128 DeleteDC(surface->hDC);
3129 DeleteObject(surface->dib.DIBsection);
3130 surface->dib.bitmap_data = NULL;
3131 surface->resource.allocatedMemory = NULL;
3132 surface->hDC = NULL;
3133 surface->flags &= ~SFLAG_DIBSECTION;
3135 else if (!(surface->flags & SFLAG_USERPTR))
3137 release = surface->resource.heapMemory;
3138 surface->resource.heapMemory = NULL;
3140 surface->resource.allocatedMemory = mem;
3141 surface->flags |= SFLAG_USERPTR;
3143 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
3144 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3146 /* For client textures OpenGL has to be notified. */
3147 if (surface->flags & SFLAG_CLIENT)
3148 surface_release_client_storage(surface);
3150 /* Now free the old memory if any. */
3151 HeapFree(GetProcessHeap(), 0, release);
3153 else if (surface->flags & SFLAG_USERPTR)
3155 /* HeapMemory should be NULL already. */
3156 if (surface->resource.heapMemory)
3157 ERR("User pointer surface has heap memory allocated.\n");
3159 if (!mem)
3161 surface->resource.allocatedMemory = NULL;
3162 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
3164 if (surface->flags & SFLAG_CLIENT)
3165 surface_release_client_storage(surface);
3167 surface_prepare_system_memory(surface);
3170 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3173 return WINED3D_OK;
3176 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3178 LONG w, h;
3180 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3182 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3184 WARN("Not an overlay surface.\n");
3185 return WINEDDERR_NOTAOVERLAYSURFACE;
3188 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3189 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3190 surface->overlay_destrect.left = x;
3191 surface->overlay_destrect.top = y;
3192 surface->overlay_destrect.right = x + w;
3193 surface->overlay_destrect.bottom = y + h;
3195 surface_draw_overlay(surface);
3197 return WINED3D_OK;
3200 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3202 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3204 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3206 TRACE("Not an overlay surface.\n");
3207 return WINEDDERR_NOTAOVERLAYSURFACE;
3210 if (!surface->overlay_dest)
3212 TRACE("Overlay not visible.\n");
3213 *x = 0;
3214 *y = 0;
3215 return WINEDDERR_OVERLAYNOTVISIBLE;
3218 *x = surface->overlay_destrect.left;
3219 *y = surface->overlay_destrect.top;
3221 TRACE("Returning position %d, %d.\n", *x, *y);
3223 return WINED3D_OK;
3226 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3227 DWORD flags, struct wined3d_surface *ref)
3229 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3231 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3233 TRACE("Not an overlay surface.\n");
3234 return WINEDDERR_NOTAOVERLAYSURFACE;
3237 return WINED3D_OK;
3240 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3241 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3243 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3244 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3246 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3248 WARN("Not an overlay surface.\n");
3249 return WINEDDERR_NOTAOVERLAYSURFACE;
3251 else if (!dst_surface)
3253 WARN("Dest surface is NULL.\n");
3254 return WINED3DERR_INVALIDCALL;
3257 if (src_rect)
3259 surface->overlay_srcrect = *src_rect;
3261 else
3263 surface->overlay_srcrect.left = 0;
3264 surface->overlay_srcrect.top = 0;
3265 surface->overlay_srcrect.right = surface->resource.width;
3266 surface->overlay_srcrect.bottom = surface->resource.height;
3269 if (dst_rect)
3271 surface->overlay_destrect = *dst_rect;
3273 else
3275 surface->overlay_destrect.left = 0;
3276 surface->overlay_destrect.top = 0;
3277 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3278 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3281 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3283 surface->overlay_dest = NULL;
3284 list_remove(&surface->overlay_entry);
3287 if (flags & WINEDDOVER_SHOW)
3289 if (surface->overlay_dest != dst_surface)
3291 surface->overlay_dest = dst_surface;
3292 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3295 else if (flags & WINEDDOVER_HIDE)
3297 /* tests show that the rectangles are erased on hide */
3298 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3299 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3300 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3301 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3302 surface->overlay_dest = NULL;
3305 surface_draw_overlay(surface);
3307 return WINED3D_OK;
3310 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3312 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3314 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3316 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3318 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3319 return WINED3DERR_INVALIDCALL;
3322 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3323 surface->pow2Width, surface->pow2Height);
3324 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3325 surface->resource.format = format;
3327 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3328 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3329 format->glFormat, format->glInternal, format->glType);
3331 return WINED3D_OK;
3334 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3335 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3337 unsigned short *dst_s;
3338 const float *src_f;
3339 unsigned int x, y;
3341 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3343 for (y = 0; y < h; ++y)
3345 src_f = (const float *)(src + y * pitch_in);
3346 dst_s = (unsigned short *) (dst + y * pitch_out);
3347 for (x = 0; x < w; ++x)
3349 dst_s[x] = float_32_to_16(src_f + x);
3354 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3355 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3357 static const unsigned char convert_5to8[] =
3359 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3360 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3361 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3362 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3364 static const unsigned char convert_6to8[] =
3366 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3367 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3368 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3369 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3370 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3371 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3372 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3373 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3375 unsigned int x, y;
3377 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3379 for (y = 0; y < h; ++y)
3381 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3382 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3383 for (x = 0; x < w; ++x)
3385 WORD pixel = src_line[x];
3386 dst_line[x] = 0xff000000
3387 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3388 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3389 | convert_5to8[(pixel & 0x001f)];
3394 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3395 * in both cases we're just setting the X / Alpha channel to 0xff. */
3396 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3397 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3399 unsigned int x, y;
3401 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3403 for (y = 0; y < h; ++y)
3405 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3406 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3408 for (x = 0; x < w; ++x)
3410 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3415 static inline BYTE cliptobyte(int x)
3417 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3420 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3421 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3423 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3424 unsigned int x, y;
3426 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3428 for (y = 0; y < h; ++y)
3430 const BYTE *src_line = src + y * pitch_in;
3431 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3432 for (x = 0; x < w; ++x)
3434 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3435 * C = Y - 16; D = U - 128; E = V - 128;
3436 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3437 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3438 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3439 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3440 * U and V are shared between the pixels. */
3441 if (!(x & 1)) /* For every even pixel, read new U and V. */
3443 d = (int) src_line[1] - 128;
3444 e = (int) src_line[3] - 128;
3445 r2 = 409 * e + 128;
3446 g2 = - 100 * d - 208 * e + 128;
3447 b2 = 516 * d + 128;
3449 c2 = 298 * ((int) src_line[0] - 16);
3450 dst_line[x] = 0xff000000
3451 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3452 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3453 | cliptobyte((c2 + b2) >> 8); /* blue */
3454 /* Scale RGB values to 0..255 range,
3455 * then clip them if still not in range (may be negative),
3456 * then shift them within DWORD if necessary. */
3457 src_line += 2;
3462 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3463 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3465 unsigned int x, y;
3466 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3468 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3470 for (y = 0; y < h; ++y)
3472 const BYTE *src_line = src + y * pitch_in;
3473 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3474 for (x = 0; x < w; ++x)
3476 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3477 * C = Y - 16; D = U - 128; E = V - 128;
3478 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3479 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3480 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3481 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3482 * U and V are shared between the pixels. */
3483 if (!(x & 1)) /* For every even pixel, read new U and V. */
3485 d = (int) src_line[1] - 128;
3486 e = (int) src_line[3] - 128;
3487 r2 = 409 * e + 128;
3488 g2 = - 100 * d - 208 * e + 128;
3489 b2 = 516 * d + 128;
3491 c2 = 298 * ((int) src_line[0] - 16);
3492 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3493 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3494 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3495 /* Scale RGB values to 0..255 range,
3496 * then clip them if still not in range (may be negative),
3497 * then shift them within DWORD if necessary. */
3498 src_line += 2;
3503 struct d3dfmt_convertor_desc
3505 enum wined3d_format_id from, to;
3506 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3509 static const struct d3dfmt_convertor_desc convertors[] =
3511 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3512 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3513 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3514 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3515 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3516 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3519 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3520 enum wined3d_format_id to)
3522 unsigned int i;
3524 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3526 if (convertors[i].from == from && convertors[i].to == to)
3527 return &convertors[i];
3530 return NULL;
3533 /*****************************************************************************
3534 * surface_convert_format
3536 * Creates a duplicate of a surface in a different format. Is used by Blt to
3537 * blit between surfaces with different formats.
3539 * Parameters
3540 * source: Source surface
3541 * fmt: Requested destination format
3543 *****************************************************************************/
3544 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3546 struct wined3d_mapped_rect src_map, dst_map;
3547 const struct d3dfmt_convertor_desc *conv;
3548 struct wined3d_surface *ret = NULL;
3549 HRESULT hr;
3551 conv = find_convertor(source->resource.format->id, to_fmt);
3552 if (!conv)
3554 FIXME("Cannot find a conversion function from format %s to %s.\n",
3555 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3556 return NULL;
3559 wined3d_surface_create(source->resource.device, source->resource.width,
3560 source->resource.height, to_fmt, 0 /* level */, 0 /* usage */, WINED3DPOOL_SCRATCH,
3561 WINED3D_MULTISAMPLE_NONE /* TODO: Multisampled conversion */, 0 /* MultiSampleQuality */,
3562 source->surface_type, WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD,
3563 NULL /* parent */, &wined3d_null_parent_ops, &ret);
3564 if (!ret)
3566 ERR("Failed to create a destination surface for conversion.\n");
3567 return NULL;
3570 memset(&src_map, 0, sizeof(src_map));
3571 memset(&dst_map, 0, sizeof(dst_map));
3573 hr = wined3d_surface_map(source, &src_map, NULL, WINED3DLOCK_READONLY);
3574 if (FAILED(hr))
3576 ERR("Failed to lock the source surface.\n");
3577 wined3d_surface_decref(ret);
3578 return NULL;
3580 hr = wined3d_surface_map(ret, &dst_map, NULL, WINED3DLOCK_READONLY);
3581 if (FAILED(hr))
3583 ERR("Failed to lock the destination surface.\n");
3584 wined3d_surface_unmap(source);
3585 wined3d_surface_decref(ret);
3586 return NULL;
3589 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
3590 source->resource.width, source->resource.height);
3592 wined3d_surface_unmap(ret);
3593 wined3d_surface_unmap(source);
3595 return ret;
3598 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3599 unsigned int bpp, UINT pitch, DWORD color)
3601 BYTE *first;
3602 int x, y;
3604 /* Do first row */
3606 #define COLORFILL_ROW(type) \
3607 do { \
3608 type *d = (type *)buf; \
3609 for (x = 0; x < width; ++x) \
3610 d[x] = (type)color; \
3611 } while(0)
3613 switch (bpp)
3615 case 1:
3616 COLORFILL_ROW(BYTE);
3617 break;
3619 case 2:
3620 COLORFILL_ROW(WORD);
3621 break;
3623 case 3:
3625 BYTE *d = buf;
3626 for (x = 0; x < width; ++x, d += 3)
3628 d[0] = (color ) & 0xFF;
3629 d[1] = (color >> 8) & 0xFF;
3630 d[2] = (color >> 16) & 0xFF;
3632 break;
3634 case 4:
3635 COLORFILL_ROW(DWORD);
3636 break;
3638 default:
3639 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3640 return WINED3DERR_NOTAVAILABLE;
3643 #undef COLORFILL_ROW
3645 /* Now copy first row. */
3646 first = buf;
3647 for (y = 1; y < height; ++y)
3649 buf += pitch;
3650 memcpy(buf, first, width * bpp);
3653 return WINED3D_OK;
3656 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3658 TRACE("surface %p.\n", surface);
3660 if (!(surface->flags & SFLAG_LOCKED))
3662 WARN("Trying to unmap unmapped surface.\n");
3663 return WINEDDERR_NOTLOCKED;
3665 surface->flags &= ~SFLAG_LOCKED;
3667 surface->surface_ops->surface_unmap(surface);
3669 return WINED3D_OK;
3672 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3673 struct wined3d_mapped_rect *mapped_rect, const RECT *rect, DWORD flags)
3675 const struct wined3d_format *format = surface->resource.format;
3677 TRACE("surface %p, mapped_rect %p, rect %s, flags %#x.\n",
3678 surface, mapped_rect, wine_dbgstr_rect(rect), flags);
3680 if (surface->flags & SFLAG_LOCKED)
3682 WARN("Surface is already mapped.\n");
3683 return WINED3DERR_INVALIDCALL;
3685 if ((format->flags & WINED3DFMT_FLAG_BLOCKS)
3686 && rect && (rect->left || rect->top
3687 || rect->right != surface->resource.width
3688 || rect->bottom != surface->resource.height))
3690 UINT width_mask = format->block_width - 1;
3691 UINT height_mask = format->block_height - 1;
3693 if ((rect->left & width_mask) || (rect->right & width_mask)
3694 || (rect->top & height_mask) || (rect->bottom & height_mask))
3696 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3697 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3699 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3700 return WINED3DERR_INVALIDCALL;
3704 surface->flags |= SFLAG_LOCKED;
3706 if (!(surface->flags & SFLAG_LOCKABLE))
3707 WARN("Trying to lock unlockable surface.\n");
3709 /* Performance optimization: Count how often a surface is mapped, if it is
3710 * mapped regularly do not throw away the system memory copy. This avoids
3711 * the need to download the surface from OpenGL all the time. The surface
3712 * is still downloaded if the OpenGL texture is changed. */
3713 if (!(surface->flags & SFLAG_DYNLOCK))
3715 if (++surface->lockCount > MAXLOCKCOUNT)
3717 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3718 surface->flags |= SFLAG_DYNLOCK;
3722 surface->surface_ops->surface_map(surface, rect, flags);
3724 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3725 mapped_rect->row_pitch = surface->resource.width * format->byte_count;
3726 else
3727 mapped_rect->row_pitch = wined3d_surface_get_pitch(surface);
3729 if (!rect)
3731 mapped_rect->data = surface->resource.allocatedMemory;
3732 surface->lockedRect.left = 0;
3733 surface->lockedRect.top = 0;
3734 surface->lockedRect.right = surface->resource.width;
3735 surface->lockedRect.bottom = surface->resource.height;
3737 else
3739 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3741 /* Compressed textures are block based, so calculate the offset of
3742 * the block that contains the top-left pixel of the locked rectangle. */
3743 mapped_rect->data = surface->resource.allocatedMemory
3744 + ((rect->top / format->block_height) * mapped_rect->row_pitch)
3745 + ((rect->left / format->block_width) * format->block_byte_count);
3747 else
3749 mapped_rect->data = surface->resource.allocatedMemory
3750 + (mapped_rect->row_pitch * rect->top)
3751 + (rect->left * format->byte_count);
3753 surface->lockedRect.left = rect->left;
3754 surface->lockedRect.top = rect->top;
3755 surface->lockedRect.right = rect->right;
3756 surface->lockedRect.bottom = rect->bottom;
3759 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3760 TRACE("Returning memory %p, pitch %u.\n", mapped_rect->data, mapped_rect->row_pitch);
3762 return WINED3D_OK;
3765 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3767 struct wined3d_mapped_rect map;
3768 HRESULT hr;
3770 TRACE("surface %p, dc %p.\n", surface, dc);
3772 if (surface->flags & SFLAG_USERPTR)
3774 ERR("Not supported on surfaces with application-provided memory.\n");
3775 return WINEDDERR_NODC;
3778 /* Give more detailed info for ddraw. */
3779 if (surface->flags & SFLAG_DCINUSE)
3780 return WINEDDERR_DCALREADYCREATED;
3782 /* Can't GetDC if the surface is locked. */
3783 if (surface->flags & SFLAG_LOCKED)
3784 return WINED3DERR_INVALIDCALL;
3786 /* Create a DIB section if there isn't a dc yet. */
3787 if (!surface->hDC)
3789 if (surface->flags & SFLAG_CLIENT)
3791 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3792 surface_release_client_storage(surface);
3794 hr = surface_create_dib_section(surface);
3795 if (FAILED(hr))
3796 return WINED3DERR_INVALIDCALL;
3798 /* Use the DIB section from now on if we are not using a PBO. */
3799 if (!(surface->flags & SFLAG_PBO))
3800 surface->resource.allocatedMemory = surface->dib.bitmap_data;
3803 /* Map the surface. */
3804 hr = wined3d_surface_map(surface, &map, NULL, 0);
3805 if (FAILED(hr))
3807 ERR("Map failed, hr %#x.\n", hr);
3808 return hr;
3811 /* Sync the DIB with the PBO. This can't be done earlier because Map()
3812 * activates the allocatedMemory. */
3813 if (surface->flags & SFLAG_PBO)
3814 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
3816 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3817 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3819 /* GetDC on palettized formats is unsupported in D3D9, and the method
3820 * is missing in D3D8, so this should only be used for DX <=7
3821 * surfaces (with non-device palettes). */
3822 const PALETTEENTRY *pal = NULL;
3824 if (surface->palette)
3826 pal = surface->palette->palents;
3828 else
3830 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3831 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3833 if (dds_primary && dds_primary->palette)
3834 pal = dds_primary->palette->palents;
3837 if (pal)
3839 RGBQUAD col[256];
3840 unsigned int i;
3842 for (i = 0; i < 256; ++i)
3844 col[i].rgbRed = pal[i].peRed;
3845 col[i].rgbGreen = pal[i].peGreen;
3846 col[i].rgbBlue = pal[i].peBlue;
3847 col[i].rgbReserved = 0;
3849 SetDIBColorTable(surface->hDC, 0, 256, col);
3853 surface->flags |= SFLAG_DCINUSE;
3855 *dc = surface->hDC;
3856 TRACE("Returning dc %p.\n", *dc);
3858 return WINED3D_OK;
3861 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3863 TRACE("surface %p, dc %p.\n", surface, dc);
3865 if (!(surface->flags & SFLAG_DCINUSE))
3866 return WINEDDERR_NODC;
3868 if (surface->hDC != dc)
3870 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3871 dc, surface->hDC);
3872 return WINEDDERR_NODC;
3875 /* Copy the contents of the DIB over to the PBO. */
3876 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3877 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3879 /* We locked first, so unlock now. */
3880 wined3d_surface_unmap(surface);
3882 surface->flags &= ~SFLAG_DCINUSE;
3884 return WINED3D_OK;
3887 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3889 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3891 if (flags)
3893 static UINT once;
3894 if (!once++)
3895 FIXME("Ignoring flags %#x.\n", flags);
3896 else
3897 WARN("Ignoring flags %#x.\n", flags);
3900 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
3902 ERR("Not supported on swapchain surfaces.\n");
3903 return WINEDDERR_NOTFLIPPABLE;
3906 /* Flipping is only supported on render targets and overlays. */
3907 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
3909 WARN("Tried to flip a non-render target, non-overlay surface.\n");
3910 return WINEDDERR_NOTFLIPPABLE;
3913 flip_surface(surface, override);
3915 /* Update overlays if they're visible. */
3916 if ((surface->resource.usage & WINED3DUSAGE_OVERLAY) && surface->overlay_dest)
3917 return surface_draw_overlay(surface);
3919 return WINED3D_OK;
3922 /* Do not call while under the GL lock. */
3923 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3925 struct wined3d_device *device = surface->resource.device;
3927 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3929 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3931 struct wined3d_texture *texture = surface->container.u.texture;
3933 TRACE("Passing to container (%p).\n", texture);
3934 texture->texture_ops->texture_preload(texture, srgb);
3936 else
3938 struct wined3d_context *context;
3940 TRACE("(%p) : About to load surface\n", surface);
3942 /* TODO: Use already acquired context when possible. */
3943 context = context_acquire(device, NULL);
3945 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3947 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3949 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3950 GLclampf tmp;
3951 tmp = 0.9f;
3952 ENTER_GL();
3953 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3954 LEAVE_GL();
3957 context_release(context);
3961 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3963 if (!surface->resource.allocatedMemory)
3965 if (!surface->resource.heapMemory)
3967 if (!(surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3968 surface->resource.size + RESOURCE_ALIGNMENT)))
3970 ERR("Failed to allocate memory.\n");
3971 return FALSE;
3974 else if (!(surface->flags & SFLAG_CLIENT))
3976 ERR("Surface %p has heapMemory %p and flags %#x.\n",
3977 surface, surface->resource.heapMemory, surface->flags);
3980 surface->resource.allocatedMemory =
3981 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
3983 else
3985 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
3988 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3990 return TRUE;
3993 /* Read the framebuffer back into the surface */
3994 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
3996 struct wined3d_device *device = surface->resource.device;
3997 const struct wined3d_gl_info *gl_info;
3998 struct wined3d_context *context;
3999 BYTE *mem;
4000 GLint fmt;
4001 GLint type;
4002 BYTE *row, *top, *bottom;
4003 int i;
4004 BOOL bpp;
4005 RECT local_rect;
4006 BOOL srcIsUpsideDown;
4007 GLint rowLen = 0;
4008 GLint skipPix = 0;
4009 GLint skipRow = 0;
4011 context = context_acquire(device, surface);
4012 context_apply_blit_state(context, device);
4013 gl_info = context->gl_info;
4015 ENTER_GL();
4017 /* Select the correct read buffer, and give some debug output.
4018 * There is no need to keep track of the current read buffer or reset it, every part of the code
4019 * that reads sets the read buffer as desired.
4021 if (surface_is_offscreen(surface))
4023 /* Mapping the primary render target which is not on a swapchain.
4024 * Read from the back buffer. */
4025 TRACE("Mapping offscreen render target.\n");
4026 glReadBuffer(device->offscreenBuffer);
4027 srcIsUpsideDown = TRUE;
4029 else
4031 /* Onscreen surfaces are always part of a swapchain */
4032 GLenum buffer = surface_get_gl_buffer(surface);
4033 TRACE("Mapping %#x buffer.\n", buffer);
4034 glReadBuffer(buffer);
4035 checkGLcall("glReadBuffer");
4036 srcIsUpsideDown = FALSE;
4039 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
4040 if (!rect)
4042 local_rect.left = 0;
4043 local_rect.top = 0;
4044 local_rect.right = surface->resource.width;
4045 local_rect.bottom = surface->resource.height;
4047 else
4049 local_rect = *rect;
4051 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
4053 switch (surface->resource.format->id)
4055 case WINED3DFMT_P8_UINT:
4057 if (primary_render_target_is_p8(device))
4059 /* In case of P8 render targets the index is stored in the alpha component */
4060 fmt = GL_ALPHA;
4061 type = GL_UNSIGNED_BYTE;
4062 mem = dest;
4063 bpp = surface->resource.format->byte_count;
4065 else
4067 /* GL can't return palettized data, so read ARGB pixels into a
4068 * separate block of memory and convert them into palettized format
4069 * in software. Slow, but if the app means to use palettized render
4070 * targets and locks it...
4072 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4073 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4074 * for the color channels when palettizing the colors.
4076 fmt = GL_RGB;
4077 type = GL_UNSIGNED_BYTE;
4078 pitch *= 3;
4079 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4080 if (!mem)
4082 ERR("Out of memory\n");
4083 LEAVE_GL();
4084 return;
4086 bpp = surface->resource.format->byte_count * 3;
4089 break;
4091 default:
4092 mem = dest;
4093 fmt = surface->resource.format->glFormat;
4094 type = surface->resource.format->glType;
4095 bpp = surface->resource.format->byte_count;
4098 if (surface->flags & SFLAG_PBO)
4100 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4101 checkGLcall("glBindBufferARB");
4102 if (mem)
4104 ERR("mem not null for pbo -- unexpected\n");
4105 mem = NULL;
4109 /* Save old pixel store pack state */
4110 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4111 checkGLcall("glGetIntegerv");
4112 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4113 checkGLcall("glGetIntegerv");
4114 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4115 checkGLcall("glGetIntegerv");
4117 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4118 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4119 checkGLcall("glPixelStorei");
4120 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4121 checkGLcall("glPixelStorei");
4122 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4123 checkGLcall("glPixelStorei");
4125 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4126 local_rect.right - local_rect.left,
4127 local_rect.bottom - local_rect.top,
4128 fmt, type, mem);
4129 checkGLcall("glReadPixels");
4131 /* Reset previous pixel store pack state */
4132 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4133 checkGLcall("glPixelStorei");
4134 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4135 checkGLcall("glPixelStorei");
4136 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4137 checkGLcall("glPixelStorei");
4139 if (surface->flags & SFLAG_PBO)
4141 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4142 checkGLcall("glBindBufferARB");
4144 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4145 * to get a pointer to it and perform the flipping in software. This is a lot
4146 * faster than calling glReadPixels for each line. In case we want more speed
4147 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4148 if (!srcIsUpsideDown)
4150 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4151 checkGLcall("glBindBufferARB");
4153 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4154 checkGLcall("glMapBufferARB");
4158 /* TODO: Merge this with the palettization loop below for P8 targets */
4159 if(!srcIsUpsideDown) {
4160 UINT len, off;
4161 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4162 Flip the lines in software */
4163 len = (local_rect.right - local_rect.left) * bpp;
4164 off = local_rect.left * bpp;
4166 row = HeapAlloc(GetProcessHeap(), 0, len);
4167 if(!row) {
4168 ERR("Out of memory\n");
4169 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4170 HeapFree(GetProcessHeap(), 0, mem);
4171 LEAVE_GL();
4172 return;
4175 top = mem + pitch * local_rect.top;
4176 bottom = mem + pitch * (local_rect.bottom - 1);
4177 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4178 memcpy(row, top + off, len);
4179 memcpy(top + off, bottom + off, len);
4180 memcpy(bottom + off, row, len);
4181 top += pitch;
4182 bottom -= pitch;
4184 HeapFree(GetProcessHeap(), 0, row);
4186 /* Unmap the temp PBO buffer */
4187 if (surface->flags & SFLAG_PBO)
4189 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4190 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4194 LEAVE_GL();
4195 context_release(context);
4197 /* For P8 textures we need to perform an inverse palette lookup. This is
4198 * done by searching for a palette index which matches the RGB value.
4199 * Note this isn't guaranteed to work when there are multiple entries for
4200 * the same color but we have no choice. In case of P8 render targets,
4201 * the index is stored in the alpha component so no conversion is needed. */
4202 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4204 const PALETTEENTRY *pal = NULL;
4205 DWORD width = pitch / 3;
4206 int x, y, c;
4208 if (surface->palette)
4210 pal = surface->palette->palents;
4212 else
4214 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4215 HeapFree(GetProcessHeap(), 0, mem);
4216 return;
4219 for(y = local_rect.top; y < local_rect.bottom; y++) {
4220 for(x = local_rect.left; x < local_rect.right; x++) {
4221 /* start lines pixels */
4222 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4223 const BYTE *green = blue + 1;
4224 const BYTE *red = green + 1;
4226 for(c = 0; c < 256; c++) {
4227 if(*red == pal[c].peRed &&
4228 *green == pal[c].peGreen &&
4229 *blue == pal[c].peBlue)
4231 *((BYTE *) dest + y * width + x) = c;
4232 break;
4237 HeapFree(GetProcessHeap(), 0, mem);
4241 /* Read the framebuffer contents into a texture. Note that this function
4242 * doesn't do any kind of flipping. Using this on an onscreen surface will
4243 * result in a flipped D3D texture. */
4244 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
4246 struct wined3d_device *device = surface->resource.device;
4247 struct wined3d_context *context;
4249 context = context_acquire(device, surface);
4250 device_invalidate_state(device, STATE_FRAMEBUFFER);
4252 surface_prepare_texture(surface, context, srgb);
4253 surface_bind_and_dirtify(surface, context, srgb);
4255 TRACE("Reading back offscreen render target %p.\n", surface);
4257 ENTER_GL();
4259 if (surface_is_offscreen(surface))
4260 glReadBuffer(device->offscreenBuffer);
4261 else
4262 glReadBuffer(surface_get_gl_buffer(surface));
4263 checkGLcall("glReadBuffer");
4265 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4266 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4267 checkGLcall("glCopyTexSubImage2D");
4269 LEAVE_GL();
4271 context_release(context);
4274 /* Context activation is done by the caller. */
4275 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4276 struct wined3d_context *context, BOOL srgb)
4278 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4279 CONVERT_TYPES convert;
4280 struct wined3d_format format;
4282 if (surface->flags & alloc_flag) return;
4284 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4285 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4286 else surface->flags &= ~SFLAG_CONVERTED;
4288 surface_bind_and_dirtify(surface, context, srgb);
4289 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4290 surface->flags |= alloc_flag;
4293 /* Context activation is done by the caller. */
4294 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4296 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4298 struct wined3d_texture *texture = surface->container.u.texture;
4299 UINT sub_count = texture->level_count * texture->layer_count;
4300 UINT i;
4302 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4304 for (i = 0; i < sub_count; ++i)
4306 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4307 surface_prepare_texture_internal(s, context, srgb);
4310 return;
4313 surface_prepare_texture_internal(surface, context, srgb);
4316 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4318 if (multisample)
4320 if (surface->rb_multisample)
4321 return;
4323 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4324 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4325 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4326 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4327 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4329 else
4331 if (surface->rb_resolved)
4332 return;
4334 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4335 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4336 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4337 surface->pow2Width, surface->pow2Height);
4338 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4342 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4343 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4345 struct wined3d_device *device = surface->resource.device;
4346 UINT pitch = wined3d_surface_get_pitch(surface);
4347 const struct wined3d_gl_info *gl_info;
4348 struct wined3d_context *context;
4349 RECT local_rect;
4350 UINT w, h;
4352 surface_get_rect(surface, rect, &local_rect);
4354 mem += local_rect.top * pitch + local_rect.left * bpp;
4355 w = local_rect.right - local_rect.left;
4356 h = local_rect.bottom - local_rect.top;
4358 /* Activate the correct context for the render target */
4359 context = context_acquire(device, surface);
4360 context_apply_blit_state(context, device);
4361 gl_info = context->gl_info;
4363 ENTER_GL();
4365 if (!surface_is_offscreen(surface))
4367 GLenum buffer = surface_get_gl_buffer(surface);
4368 TRACE("Unlocking %#x buffer.\n", buffer);
4369 context_set_draw_buffer(context, buffer);
4371 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4372 glPixelZoom(1.0f, -1.0f);
4374 else
4376 /* Primary offscreen render target */
4377 TRACE("Offscreen render target.\n");
4378 context_set_draw_buffer(context, device->offscreenBuffer);
4380 glPixelZoom(1.0f, 1.0f);
4383 glRasterPos3i(local_rect.left, local_rect.top, 1);
4384 checkGLcall("glRasterPos3i");
4386 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4387 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4389 if (surface->flags & SFLAG_PBO)
4391 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4392 checkGLcall("glBindBufferARB");
4395 glDrawPixels(w, h, fmt, type, mem);
4396 checkGLcall("glDrawPixels");
4398 if (surface->flags & SFLAG_PBO)
4400 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4401 checkGLcall("glBindBufferARB");
4404 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4405 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4407 LEAVE_GL();
4409 if (wined3d_settings.strict_draw_ordering
4410 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4411 && surface->container.u.swapchain->front_buffer == surface))
4412 wglFlush();
4414 context_release(context);
4417 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4418 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4420 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4421 const struct wined3d_device *device = surface->resource.device;
4422 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4423 BOOL blit_supported = FALSE;
4425 /* Copy the default values from the surface. Below we might perform fixups */
4426 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4427 *format = *surface->resource.format;
4428 *convert = NO_CONVERSION;
4430 /* Ok, now look if we have to do any conversion */
4431 switch (surface->resource.format->id)
4433 case WINED3DFMT_P8_UINT:
4434 /* Below the call to blit_supported is disabled for Wine 1.2
4435 * because the function isn't operating correctly yet. At the
4436 * moment 8-bit blits are handled in software and if certain GL
4437 * extensions are around, surface conversion is performed at
4438 * upload time. The blit_supported call recognizes it as a
4439 * destination fixup. This type of upload 'fixup' and 8-bit to
4440 * 8-bit blits need to be handled by the blit_shader.
4441 * TODO: get rid of this #if 0. */
4442 #if 0
4443 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4444 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4445 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4446 #endif
4447 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4449 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4450 * texturing. Further also use conversion in case of color keying.
4451 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4452 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4453 * conflicts with this.
4455 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4456 || colorkey_active || !use_texturing)
4458 format->glFormat = GL_RGBA;
4459 format->glInternal = GL_RGBA;
4460 format->glType = GL_UNSIGNED_BYTE;
4461 format->conv_byte_count = 4;
4462 if (colorkey_active)
4463 *convert = CONVERT_PALETTED_CK;
4464 else
4465 *convert = CONVERT_PALETTED;
4467 break;
4469 case WINED3DFMT_B2G3R3_UNORM:
4470 /* **********************
4471 GL_UNSIGNED_BYTE_3_3_2
4472 ********************** */
4473 if (colorkey_active) {
4474 /* This texture format will never be used.. So do not care about color keying
4475 up until the point in time it will be needed :-) */
4476 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4478 break;
4480 case WINED3DFMT_B5G6R5_UNORM:
4481 if (colorkey_active)
4483 *convert = CONVERT_CK_565;
4484 format->glFormat = GL_RGBA;
4485 format->glInternal = GL_RGB5_A1;
4486 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4487 format->conv_byte_count = 2;
4489 break;
4491 case WINED3DFMT_B5G5R5X1_UNORM:
4492 if (colorkey_active)
4494 *convert = CONVERT_CK_5551;
4495 format->glFormat = GL_BGRA;
4496 format->glInternal = GL_RGB5_A1;
4497 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4498 format->conv_byte_count = 2;
4500 break;
4502 case WINED3DFMT_B8G8R8_UNORM:
4503 if (colorkey_active)
4505 *convert = CONVERT_CK_RGB24;
4506 format->glFormat = GL_RGBA;
4507 format->glInternal = GL_RGBA8;
4508 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4509 format->conv_byte_count = 4;
4511 break;
4513 case WINED3DFMT_B8G8R8X8_UNORM:
4514 if (colorkey_active)
4516 *convert = CONVERT_RGB32_888;
4517 format->glFormat = GL_RGBA;
4518 format->glInternal = GL_RGBA8;
4519 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4520 format->conv_byte_count = 4;
4522 break;
4524 default:
4525 break;
4528 return WINED3D_OK;
4531 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
4533 /* FIXME: Is this really how color keys are supposed to work? I think it
4534 * makes more sense to compare the individual channels. */
4535 return color >= color_key->color_space_low_value
4536 && color <= color_key->color_space_high_value;
4539 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4541 const struct wined3d_device *device = surface->resource.device;
4542 const struct wined3d_palette *pal = surface->palette;
4543 BOOL index_in_alpha = FALSE;
4544 unsigned int i;
4546 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4547 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4548 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4549 * duplicate entries. Store the color key in the unused alpha component to speed the
4550 * download up and to make conversion unneeded. */
4551 index_in_alpha = primary_render_target_is_p8(device);
4553 if (!pal)
4555 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4556 if (index_in_alpha)
4558 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4559 * there's no palette at this time. */
4560 for (i = 0; i < 256; i++) table[i][3] = i;
4563 else
4565 TRACE("Using surface palette %p\n", pal);
4566 /* Get the surface's palette */
4567 for (i = 0; i < 256; ++i)
4569 table[i][0] = pal->palents[i].peRed;
4570 table[i][1] = pal->palents[i].peGreen;
4571 table[i][2] = pal->palents[i].peBlue;
4573 /* When index_in_alpha is set the palette index is stored in the
4574 * alpha component. In case of a readback we can then read
4575 * GL_ALPHA. Color keying is handled in BltOverride using a
4576 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4577 * color key itself is passed to glAlphaFunc in other cases the
4578 * alpha component of pixels that should be masked away is set to 0. */
4579 if (index_in_alpha)
4580 table[i][3] = i;
4581 else if (colorkey && color_in_range(&surface->src_blt_color_key, i))
4582 table[i][3] = 0x00;
4583 else if (pal->flags & WINEDDPCAPS_ALPHA)
4584 table[i][3] = pal->palents[i].peFlags;
4585 else
4586 table[i][3] = 0xFF;
4591 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4592 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4594 const BYTE *source;
4595 BYTE *dest;
4596 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4598 switch (convert) {
4599 case NO_CONVERSION:
4601 memcpy(dst, src, pitch * height);
4602 break;
4604 case CONVERT_PALETTED:
4605 case CONVERT_PALETTED_CK:
4607 BYTE table[256][4];
4608 unsigned int x, y;
4610 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4612 for (y = 0; y < height; y++)
4614 source = src + pitch * y;
4615 dest = dst + outpitch * y;
4616 /* This is an 1 bpp format, using the width here is fine */
4617 for (x = 0; x < width; x++) {
4618 BYTE color = *source++;
4619 *dest++ = table[color][0];
4620 *dest++ = table[color][1];
4621 *dest++ = table[color][2];
4622 *dest++ = table[color][3];
4626 break;
4628 case CONVERT_CK_565:
4630 /* Converting the 565 format in 5551 packed to emulate color-keying.
4632 Note : in all these conversion, it would be best to average the averaging
4633 pixels to get the color of the pixel that will be color-keyed to
4634 prevent 'color bleeding'. This will be done later on if ever it is
4635 too visible.
4637 Note2: Nvidia documents say that their driver does not support alpha + color keying
4638 on the same surface and disables color keying in such a case
4640 unsigned int x, y;
4641 const WORD *Source;
4642 WORD *Dest;
4644 TRACE("Color keyed 565\n");
4646 for (y = 0; y < height; y++) {
4647 Source = (const WORD *)(src + y * pitch);
4648 Dest = (WORD *) (dst + y * outpitch);
4649 for (x = 0; x < width; x++ ) {
4650 WORD color = *Source++;
4651 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4652 if (!color_in_range(&surface->src_blt_color_key, color))
4653 *Dest |= 0x0001;
4654 Dest++;
4658 break;
4660 case CONVERT_CK_5551:
4662 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4663 unsigned int x, y;
4664 const WORD *Source;
4665 WORD *Dest;
4666 TRACE("Color keyed 5551\n");
4667 for (y = 0; y < height; y++) {
4668 Source = (const WORD *)(src + y * pitch);
4669 Dest = (WORD *) (dst + y * outpitch);
4670 for (x = 0; x < width; x++ ) {
4671 WORD color = *Source++;
4672 *Dest = color;
4673 if (!color_in_range(&surface->src_blt_color_key, color))
4674 *Dest |= (1 << 15);
4675 else
4676 *Dest &= ~(1 << 15);
4677 Dest++;
4681 break;
4683 case CONVERT_CK_RGB24:
4685 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4686 unsigned int x, y;
4687 for (y = 0; y < height; y++)
4689 source = src + pitch * y;
4690 dest = dst + outpitch * y;
4691 for (x = 0; x < width; x++) {
4692 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4693 DWORD dstcolor = color << 8;
4694 if (!color_in_range(&surface->src_blt_color_key, color))
4695 dstcolor |= 0xff;
4696 *(DWORD*)dest = dstcolor;
4697 source += 3;
4698 dest += 4;
4702 break;
4704 case CONVERT_RGB32_888:
4706 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4707 unsigned int x, y;
4708 for (y = 0; y < height; y++)
4710 source = src + pitch * y;
4711 dest = dst + outpitch * y;
4712 for (x = 0; x < width; x++) {
4713 DWORD color = 0xffffff & *(const DWORD*)source;
4714 DWORD dstcolor = color << 8;
4715 if (!color_in_range(&surface->src_blt_color_key, color))
4716 dstcolor |= 0xff;
4717 *(DWORD*)dest = dstcolor;
4718 source += 4;
4719 dest += 4;
4723 break;
4725 default:
4726 ERR("Unsupported conversion type %#x.\n", convert);
4728 return WINED3D_OK;
4731 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4733 /* Flip the surface contents */
4734 /* Flip the DC */
4736 HDC tmp;
4737 tmp = front->hDC;
4738 front->hDC = back->hDC;
4739 back->hDC = tmp;
4742 /* Flip the DIBsection */
4744 HBITMAP tmp = front->dib.DIBsection;
4745 front->dib.DIBsection = back->dib.DIBsection;
4746 back->dib.DIBsection = tmp;
4749 /* Flip the surface data */
4751 void* tmp;
4753 tmp = front->dib.bitmap_data;
4754 front->dib.bitmap_data = back->dib.bitmap_data;
4755 back->dib.bitmap_data = tmp;
4757 tmp = front->resource.allocatedMemory;
4758 front->resource.allocatedMemory = back->resource.allocatedMemory;
4759 back->resource.allocatedMemory = tmp;
4761 tmp = front->resource.heapMemory;
4762 front->resource.heapMemory = back->resource.heapMemory;
4763 back->resource.heapMemory = tmp;
4766 /* Flip the PBO */
4768 GLuint tmp_pbo = front->pbo;
4769 front->pbo = back->pbo;
4770 back->pbo = tmp_pbo;
4773 /* Flip the opengl texture */
4775 GLuint tmp;
4777 tmp = back->texture_name;
4778 back->texture_name = front->texture_name;
4779 front->texture_name = tmp;
4781 tmp = back->texture_name_srgb;
4782 back->texture_name_srgb = front->texture_name_srgb;
4783 front->texture_name_srgb = tmp;
4785 tmp = back->rb_multisample;
4786 back->rb_multisample = front->rb_multisample;
4787 front->rb_multisample = tmp;
4789 tmp = back->rb_resolved;
4790 back->rb_resolved = front->rb_resolved;
4791 front->rb_resolved = tmp;
4793 resource_unload(&back->resource);
4794 resource_unload(&front->resource);
4798 DWORD tmp_flags = back->flags;
4799 back->flags = front->flags;
4800 front->flags = tmp_flags;
4804 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4805 * pixel copy calls. */
4806 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4807 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4809 struct wined3d_device *device = dst_surface->resource.device;
4810 float xrel, yrel;
4811 UINT row;
4812 struct wined3d_context *context;
4813 BOOL upsidedown = FALSE;
4814 RECT dst_rect = *dst_rect_in;
4816 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4817 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4819 if(dst_rect.top > dst_rect.bottom) {
4820 UINT tmp = dst_rect.bottom;
4821 dst_rect.bottom = dst_rect.top;
4822 dst_rect.top = tmp;
4823 upsidedown = TRUE;
4826 context = context_acquire(device, src_surface);
4827 context_apply_blit_state(context, device);
4828 surface_internal_preload(dst_surface, SRGB_RGB);
4829 ENTER_GL();
4831 /* Bind the target texture */
4832 context_bind_texture(context, dst_surface->texture_target, dst_surface->texture_name);
4833 if (surface_is_offscreen(src_surface))
4835 TRACE("Reading from an offscreen target\n");
4836 upsidedown = !upsidedown;
4837 glReadBuffer(device->offscreenBuffer);
4839 else
4841 glReadBuffer(surface_get_gl_buffer(src_surface));
4843 checkGLcall("glReadBuffer");
4845 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4846 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4848 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4850 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4852 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4853 ERR("Texture filtering not supported in direct blit\n");
4856 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4857 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4859 ERR("Texture filtering not supported in direct blit\n");
4862 if (upsidedown
4863 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4864 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4866 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4868 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4869 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4870 src_rect->left, src_surface->resource.height - src_rect->bottom,
4871 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4873 else
4875 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4876 /* I have to process this row by row to swap the image,
4877 * otherwise it would be upside down, so stretching in y direction
4878 * doesn't cost extra time
4880 * However, stretching in x direction can be avoided if not necessary
4882 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4883 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4885 /* Well, that stuff works, but it's very slow.
4886 * find a better way instead
4888 UINT col;
4890 for (col = dst_rect.left; col < dst_rect.right; ++col)
4892 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4893 dst_rect.left + col /* x offset */, row /* y offset */,
4894 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4897 else
4899 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4900 dst_rect.left /* x offset */, row /* y offset */,
4901 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4905 checkGLcall("glCopyTexSubImage2D");
4907 LEAVE_GL();
4908 context_release(context);
4910 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4911 * path is never entered
4913 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4916 /* Uses the hardware to stretch and flip the image */
4917 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4918 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4920 struct wined3d_device *device = dst_surface->resource.device;
4921 struct wined3d_swapchain *src_swapchain = NULL;
4922 GLuint src, backup = 0;
4923 float left, right, top, bottom; /* Texture coordinates */
4924 UINT fbwidth = src_surface->resource.width;
4925 UINT fbheight = src_surface->resource.height;
4926 struct wined3d_context *context;
4927 GLenum drawBuffer = GL_BACK;
4928 GLenum texture_target;
4929 BOOL noBackBufferBackup;
4930 BOOL src_offscreen;
4931 BOOL upsidedown = FALSE;
4932 RECT dst_rect = *dst_rect_in;
4934 TRACE("Using hwstretch blit\n");
4935 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4936 context = context_acquire(device, src_surface);
4937 context_apply_blit_state(context, device);
4938 surface_internal_preload(dst_surface, SRGB_RGB);
4940 src_offscreen = surface_is_offscreen(src_surface);
4941 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4942 if (!noBackBufferBackup && !src_surface->texture_name)
4944 /* Get it a description */
4945 surface_internal_preload(src_surface, SRGB_RGB);
4947 ENTER_GL();
4949 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4950 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4952 if (context->aux_buffers >= 2)
4954 /* Got more than one aux buffer? Use the 2nd aux buffer */
4955 drawBuffer = GL_AUX1;
4957 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4959 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4960 drawBuffer = GL_AUX0;
4963 if(noBackBufferBackup) {
4964 glGenTextures(1, &backup);
4965 checkGLcall("glGenTextures");
4966 context_bind_texture(context, GL_TEXTURE_2D, backup);
4967 texture_target = GL_TEXTURE_2D;
4968 } else {
4969 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4970 * we are reading from the back buffer, the backup can be used as source texture
4972 texture_target = src_surface->texture_target;
4973 context_bind_texture(context, texture_target, src_surface->texture_name);
4974 glEnable(texture_target);
4975 checkGLcall("glEnable(texture_target)");
4977 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4978 src_surface->flags &= ~SFLAG_INTEXTURE;
4981 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4982 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4984 if(dst_rect.top > dst_rect.bottom) {
4985 UINT tmp = dst_rect.bottom;
4986 dst_rect.bottom = dst_rect.top;
4987 dst_rect.top = tmp;
4988 upsidedown = TRUE;
4991 if (src_offscreen)
4993 TRACE("Reading from an offscreen target\n");
4994 upsidedown = !upsidedown;
4995 glReadBuffer(device->offscreenBuffer);
4997 else
4999 glReadBuffer(surface_get_gl_buffer(src_surface));
5002 /* TODO: Only back up the part that will be overwritten */
5003 glCopyTexSubImage2D(texture_target, 0,
5004 0, 0 /* read offsets */,
5005 0, 0,
5006 fbwidth,
5007 fbheight);
5009 checkGLcall("glCopyTexSubImage2D");
5011 /* No issue with overriding these - the sampler is dirty due to blit usage */
5012 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
5013 wined3d_gl_mag_filter(magLookup, Filter));
5014 checkGLcall("glTexParameteri");
5015 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5016 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
5017 checkGLcall("glTexParameteri");
5019 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5020 src_swapchain = src_surface->container.u.swapchain;
5021 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5023 src = backup ? backup : src_surface->texture_name;
5025 else
5027 glReadBuffer(GL_FRONT);
5028 checkGLcall("glReadBuffer(GL_FRONT)");
5030 glGenTextures(1, &src);
5031 checkGLcall("glGenTextures(1, &src)");
5032 context_bind_texture(context, GL_TEXTURE_2D, src);
5034 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5035 * out for power of 2 sizes
5037 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5038 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5039 checkGLcall("glTexImage2D");
5040 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5041 0, 0 /* read offsets */,
5042 0, 0,
5043 fbwidth,
5044 fbheight);
5046 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5047 checkGLcall("glTexParameteri");
5048 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5049 checkGLcall("glTexParameteri");
5051 glReadBuffer(GL_BACK);
5052 checkGLcall("glReadBuffer(GL_BACK)");
5054 if(texture_target != GL_TEXTURE_2D) {
5055 glDisable(texture_target);
5056 glEnable(GL_TEXTURE_2D);
5057 texture_target = GL_TEXTURE_2D;
5060 checkGLcall("glEnd and previous");
5062 left = src_rect->left;
5063 right = src_rect->right;
5065 if (!upsidedown)
5067 top = src_surface->resource.height - src_rect->top;
5068 bottom = src_surface->resource.height - src_rect->bottom;
5070 else
5072 top = src_surface->resource.height - src_rect->bottom;
5073 bottom = src_surface->resource.height - src_rect->top;
5076 if (src_surface->flags & SFLAG_NORMCOORD)
5078 left /= src_surface->pow2Width;
5079 right /= src_surface->pow2Width;
5080 top /= src_surface->pow2Height;
5081 bottom /= src_surface->pow2Height;
5084 /* draw the source texture stretched and upside down. The correct surface is bound already */
5085 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5086 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5088 context_set_draw_buffer(context, drawBuffer);
5089 glReadBuffer(drawBuffer);
5091 glBegin(GL_QUADS);
5092 /* bottom left */
5093 glTexCoord2f(left, bottom);
5094 glVertex2i(0, 0);
5096 /* top left */
5097 glTexCoord2f(left, top);
5098 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5100 /* top right */
5101 glTexCoord2f(right, top);
5102 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5104 /* bottom right */
5105 glTexCoord2f(right, bottom);
5106 glVertex2i(dst_rect.right - dst_rect.left, 0);
5107 glEnd();
5108 checkGLcall("glEnd and previous");
5110 if (texture_target != dst_surface->texture_target)
5112 glDisable(texture_target);
5113 glEnable(dst_surface->texture_target);
5114 texture_target = dst_surface->texture_target;
5117 /* Now read the stretched and upside down image into the destination texture */
5118 context_bind_texture(context, texture_target, dst_surface->texture_name);
5119 glCopyTexSubImage2D(texture_target,
5121 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5122 0, 0, /* We blitted the image to the origin */
5123 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5124 checkGLcall("glCopyTexSubImage2D");
5126 if(drawBuffer == GL_BACK) {
5127 /* Write the back buffer backup back */
5128 if(backup) {
5129 if(texture_target != GL_TEXTURE_2D) {
5130 glDisable(texture_target);
5131 glEnable(GL_TEXTURE_2D);
5132 texture_target = GL_TEXTURE_2D;
5134 context_bind_texture(context, GL_TEXTURE_2D, backup);
5136 else
5138 if (texture_target != src_surface->texture_target)
5140 glDisable(texture_target);
5141 glEnable(src_surface->texture_target);
5142 texture_target = src_surface->texture_target;
5144 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5147 glBegin(GL_QUADS);
5148 /* top left */
5149 glTexCoord2f(0.0f, 0.0f);
5150 glVertex2i(0, fbheight);
5152 /* bottom left */
5153 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5154 glVertex2i(0, 0);
5156 /* bottom right */
5157 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5158 (float)fbheight / (float)src_surface->pow2Height);
5159 glVertex2i(fbwidth, 0);
5161 /* top right */
5162 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5163 glVertex2i(fbwidth, fbheight);
5164 glEnd();
5166 glDisable(texture_target);
5167 checkGLcall("glDisable(texture_target)");
5169 /* Cleanup */
5170 if (src != src_surface->texture_name && src != backup)
5172 glDeleteTextures(1, &src);
5173 checkGLcall("glDeleteTextures(1, &src)");
5175 if(backup) {
5176 glDeleteTextures(1, &backup);
5177 checkGLcall("glDeleteTextures(1, &backup)");
5180 LEAVE_GL();
5182 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5184 context_release(context);
5186 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5187 * path is never entered
5189 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5192 /* Front buffer coordinates are always full screen coordinates, but our GL
5193 * drawable is limited to the window's client area. The sysmem and texture
5194 * copies do have the full screen size. Note that GL has a bottom-left
5195 * origin, while D3D has a top-left origin. */
5196 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5198 UINT drawable_height;
5200 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5201 && surface == surface->container.u.swapchain->front_buffer)
5203 POINT offset = {0, 0};
5204 RECT windowsize;
5206 ScreenToClient(window, &offset);
5207 OffsetRect(rect, offset.x, offset.y);
5209 GetClientRect(window, &windowsize);
5210 drawable_height = windowsize.bottom - windowsize.top;
5212 else
5214 drawable_height = surface->resource.height;
5217 rect->top = drawable_height - rect->top;
5218 rect->bottom = drawable_height - rect->bottom;
5221 static void surface_blt_to_drawable(const struct wined3d_device *device,
5222 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5223 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5224 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5226 struct wined3d_context *context;
5227 RECT src_rect, dst_rect;
5229 src_rect = *src_rect_in;
5230 dst_rect = *dst_rect_in;
5232 /* Make sure the surface is up-to-date. This should probably use
5233 * surface_load_location() and worry about the destination surface too,
5234 * unless we're overwriting it completely. */
5235 surface_internal_preload(src_surface, SRGB_RGB);
5237 /* Activate the destination context, set it up for blitting */
5238 context = context_acquire(device, dst_surface);
5239 context_apply_blit_state(context, device);
5241 if (!surface_is_offscreen(dst_surface))
5242 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5244 device->blitter->set_shader(device->blit_priv, context, src_surface);
5246 ENTER_GL();
5248 if (color_key)
5250 glEnable(GL_ALPHA_TEST);
5251 checkGLcall("glEnable(GL_ALPHA_TEST)");
5253 /* When the primary render target uses P8, the alpha component
5254 * contains the palette index. Which means that the colorkey is one of
5255 * the palette entries. In other cases pixels that should be masked
5256 * away have alpha set to 0. */
5257 if (primary_render_target_is_p8(device))
5258 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->src_blt_color_key.color_space_low_value / 256.0f);
5259 else
5260 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5261 checkGLcall("glAlphaFunc");
5263 else
5265 glDisable(GL_ALPHA_TEST);
5266 checkGLcall("glDisable(GL_ALPHA_TEST)");
5269 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5271 if (color_key)
5273 glDisable(GL_ALPHA_TEST);
5274 checkGLcall("glDisable(GL_ALPHA_TEST)");
5277 LEAVE_GL();
5279 /* Leave the opengl state valid for blitting */
5280 device->blitter->unset_shader(context->gl_info);
5282 if (wined3d_settings.strict_draw_ordering
5283 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5284 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5285 wglFlush(); /* Flush to ensure ordering across contexts. */
5287 context_release(context);
5290 /* Do not call while under the GL lock. */
5291 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
5293 struct wined3d_device *device = s->resource.device;
5294 const struct blit_shader *blitter;
5296 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5297 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5298 if (!blitter)
5300 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5301 return WINED3DERR_INVALIDCALL;
5304 return blitter->color_fill(device, s, rect, color);
5307 /* Do not call while under the GL lock. */
5308 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5309 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5310 WINED3DTEXTUREFILTERTYPE Filter)
5312 struct wined3d_device *device = dst_surface->resource.device;
5313 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5314 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5316 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5317 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5318 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5320 /* Get the swapchain. One of the surfaces has to be a primary surface */
5321 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5323 WARN("Destination is in sysmem, rejecting gl blt\n");
5324 return WINED3DERR_INVALIDCALL;
5327 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5328 dstSwapchain = dst_surface->container.u.swapchain;
5330 if (src_surface)
5332 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5334 WARN("Src is in sysmem, rejecting gl blt\n");
5335 return WINED3DERR_INVALIDCALL;
5338 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5339 srcSwapchain = src_surface->container.u.swapchain;
5342 /* Early sort out of cases where no render target is used */
5343 if (!dstSwapchain && !srcSwapchain
5344 && src_surface != device->fb.render_targets[0]
5345 && dst_surface != device->fb.render_targets[0])
5347 TRACE("No surface is render target, not using hardware blit.\n");
5348 return WINED3DERR_INVALIDCALL;
5351 /* No destination color keying supported */
5352 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5354 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5355 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5356 return WINED3DERR_INVALIDCALL;
5359 if (dstSwapchain && dstSwapchain == srcSwapchain)
5361 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5362 return WINED3DERR_INVALIDCALL;
5365 if (dstSwapchain && srcSwapchain)
5367 FIXME("Implement hardware blit between two different swapchains\n");
5368 return WINED3DERR_INVALIDCALL;
5371 if (dstSwapchain)
5373 /* Handled with regular texture -> swapchain blit */
5374 if (src_surface == device->fb.render_targets[0])
5375 TRACE("Blit from active render target to a swapchain\n");
5377 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5379 FIXME("Implement blit from a swapchain to the active render target\n");
5380 return WINED3DERR_INVALIDCALL;
5383 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5385 /* Blit from render target to texture */
5386 BOOL stretchx;
5388 /* P8 read back is not implemented */
5389 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5390 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5392 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5393 return WINED3DERR_INVALIDCALL;
5396 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5398 TRACE("Color keying not supported by frame buffer to texture blit\n");
5399 return WINED3DERR_INVALIDCALL;
5400 /* Destination color key is checked above */
5403 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5404 stretchx = TRUE;
5405 else
5406 stretchx = FALSE;
5408 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5409 * flip the image nor scale it.
5411 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5412 * -> If the app wants a image width an unscaled width, copy it line per line
5413 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5414 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5415 * back buffer. This is slower than reading line per line, thus not used for flipping
5416 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5417 * pixel by pixel. */
5418 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5419 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5421 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5422 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, Filter);
5423 } else {
5424 TRACE("Using hardware stretching to flip / stretch the texture\n");
5425 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5428 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5430 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5431 dst_surface->resource.allocatedMemory = NULL;
5432 dst_surface->resource.heapMemory = NULL;
5434 else
5436 dst_surface->flags &= ~SFLAG_INSYSMEM;
5439 return WINED3D_OK;
5441 else if (src_surface)
5443 /* Blit from offscreen surface to render target */
5444 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
5445 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5447 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5449 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5450 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5451 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5453 FIXME("Unsupported blit operation falling back to software\n");
5454 return WINED3DERR_INVALIDCALL;
5457 /* Color keying: Check if we have to do a color keyed blt,
5458 * and if not check if a color key is activated.
5460 * Just modify the color keying parameters in the surface and restore them afterwards
5461 * The surface keeps track of the color key last used to load the opengl surface.
5462 * PreLoad will catch the change to the flags and color key and reload if necessary.
5464 if (flags & WINEDDBLT_KEYSRC)
5466 /* Use color key from surface */
5468 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5470 /* Use color key from DDBltFx */
5471 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5472 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
5474 else
5476 /* Do not use color key */
5477 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5480 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5481 src_surface, src_rect, dst_surface, dst_rect);
5483 /* Restore the color key parameters */
5484 src_surface->CKeyFlags = oldCKeyFlags;
5485 src_surface->src_blt_color_key = old_blt_key;
5487 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5489 return WINED3D_OK;
5492 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5493 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5494 return WINED3DERR_INVALIDCALL;
5497 /* GL locking is done by the caller */
5498 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5499 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5501 struct wined3d_device *device = surface->resource.device;
5502 const struct wined3d_gl_info *gl_info = context->gl_info;
5503 GLint compare_mode = GL_NONE;
5504 struct blt_info info;
5505 GLint old_binding = 0;
5506 RECT rect;
5508 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5510 glDisable(GL_CULL_FACE);
5511 glDisable(GL_BLEND);
5512 glDisable(GL_ALPHA_TEST);
5513 glDisable(GL_SCISSOR_TEST);
5514 glDisable(GL_STENCIL_TEST);
5515 glEnable(GL_DEPTH_TEST);
5516 glDepthFunc(GL_ALWAYS);
5517 glDepthMask(GL_TRUE);
5518 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5519 glViewport(x, y, w, h);
5521 SetRect(&rect, 0, h, w, 0);
5522 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5523 context_active_texture(context, context->gl_info, 0);
5524 glGetIntegerv(info.binding, &old_binding);
5525 glBindTexture(info.bind_target, texture);
5526 if (gl_info->supported[ARB_SHADOW])
5528 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5529 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5532 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5533 gl_info, info.tex_type, &surface->ds_current_size);
5535 glBegin(GL_TRIANGLE_STRIP);
5536 glTexCoord3fv(info.coords[0]);
5537 glVertex2f(-1.0f, -1.0f);
5538 glTexCoord3fv(info.coords[1]);
5539 glVertex2f(1.0f, -1.0f);
5540 glTexCoord3fv(info.coords[2]);
5541 glVertex2f(-1.0f, 1.0f);
5542 glTexCoord3fv(info.coords[3]);
5543 glVertex2f(1.0f, 1.0f);
5544 glEnd();
5546 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5547 glBindTexture(info.bind_target, old_binding);
5549 glPopAttrib();
5551 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5554 void surface_modify_ds_location(struct wined3d_surface *surface,
5555 DWORD location, UINT w, UINT h)
5557 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5559 if (location & ~SFLAG_LOCATIONS)
5560 FIXME("Invalid location (%#x) specified.\n", location);
5562 if (!(surface->flags & SFLAG_ALLOCATED))
5563 location &= ~SFLAG_INTEXTURE;
5564 if (!(surface->rb_resolved))
5565 location &= ~SFLAG_INRB_RESOLVED;
5566 if (!(surface->rb_multisample))
5567 location &= ~SFLAG_INRB_MULTISAMPLE;
5569 surface->ds_current_size.cx = w;
5570 surface->ds_current_size.cy = h;
5571 surface->flags &= ~SFLAG_LOCATIONS;
5572 surface->flags |= location;
5575 /* Context activation is done by the caller. */
5576 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5578 struct wined3d_device *device = surface->resource.device;
5579 GLsizei w, h;
5581 TRACE("surface %p, new location %#x.\n", surface, location);
5583 /* TODO: Make this work for modes other than FBO */
5584 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5586 if (!(surface->flags & location))
5588 w = surface->ds_current_size.cx;
5589 h = surface->ds_current_size.cy;
5590 surface->ds_current_size.cx = 0;
5591 surface->ds_current_size.cy = 0;
5593 else
5595 w = surface->resource.width;
5596 h = surface->resource.height;
5599 if (surface->ds_current_size.cx == surface->resource.width
5600 && surface->ds_current_size.cy == surface->resource.height)
5602 TRACE("Location (%#x) is already up to date.\n", location);
5603 return;
5606 if (surface->current_renderbuffer)
5608 FIXME("Not supported with fixed up depth stencil.\n");
5609 return;
5612 if (!(surface->flags & SFLAG_LOCATIONS))
5614 /* This mostly happens when a depth / stencil is used without being
5615 * cleared first. In principle we could upload from sysmem, or
5616 * explicitly clear before first usage. For the moment there don't
5617 * appear to be a lot of applications depending on this, so a FIXME
5618 * should do. */
5619 FIXME("No up to date depth stencil location.\n");
5620 surface->flags |= location;
5621 surface->ds_current_size.cx = surface->resource.width;
5622 surface->ds_current_size.cy = surface->resource.height;
5623 return;
5626 if (location == SFLAG_INTEXTURE)
5628 GLint old_binding = 0;
5629 GLenum bind_target;
5631 /* The render target is allowed to be smaller than the depth/stencil
5632 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5633 * than the offscreen surface. Don't overwrite the offscreen surface
5634 * with undefined data. */
5635 w = min(w, context->swapchain->desc.backbuffer_width);
5636 h = min(h, context->swapchain->desc.backbuffer_height);
5638 TRACE("Copying onscreen depth buffer to depth texture.\n");
5640 ENTER_GL();
5642 if (!device->depth_blt_texture)
5644 glGenTextures(1, &device->depth_blt_texture);
5647 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5648 * directly on the FBO texture. That's because we need to flip. */
5649 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5650 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5651 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5653 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5654 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5656 else
5658 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5659 bind_target = GL_TEXTURE_2D;
5661 glBindTexture(bind_target, device->depth_blt_texture);
5662 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5663 * internal format, because the internal format might include stencil
5664 * data. In principle we should copy stencil data as well, but unless
5665 * the driver supports stencil export it's hard to do, and doesn't
5666 * seem to be needed in practice. If the hardware doesn't support
5667 * writing stencil data, the glCopyTexImage2D() call might trigger
5668 * software fallbacks. */
5669 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5670 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5671 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5672 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5673 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5674 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5675 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5676 glBindTexture(bind_target, old_binding);
5678 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5679 NULL, surface, SFLAG_INTEXTURE);
5680 context_set_draw_buffer(context, GL_NONE);
5681 glReadBuffer(GL_NONE);
5683 /* Do the actual blit */
5684 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5685 checkGLcall("depth_blt");
5687 context_invalidate_state(context, STATE_FRAMEBUFFER);
5689 LEAVE_GL();
5691 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5693 else if (location == SFLAG_INDRAWABLE)
5695 TRACE("Copying depth texture to onscreen depth buffer.\n");
5697 ENTER_GL();
5699 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5700 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5701 surface_depth_blt(surface, context, surface->texture_name,
5702 0, surface->pow2Height - h, w, h, surface->texture_target);
5703 checkGLcall("depth_blt");
5705 context_invalidate_state(context, STATE_FRAMEBUFFER);
5707 LEAVE_GL();
5709 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5711 else
5713 ERR("Invalid location (%#x) specified.\n", location);
5716 surface->flags |= location;
5717 surface->ds_current_size.cx = surface->resource.width;
5718 surface->ds_current_size.cy = surface->resource.height;
5721 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5723 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5724 struct wined3d_surface *overlay;
5726 TRACE("surface %p, location %s, persistent %#x.\n",
5727 surface, debug_surflocation(location), persistent);
5729 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5730 && !(surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5731 && (location & SFLAG_INDRAWABLE))
5732 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5734 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5735 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5736 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5738 if (persistent)
5740 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5741 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5743 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5745 TRACE("Passing to container.\n");
5746 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5749 surface->flags &= ~SFLAG_LOCATIONS;
5750 surface->flags |= location;
5752 /* Redraw emulated overlays, if any */
5753 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5755 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5757 surface_draw_overlay(overlay);
5761 else
5763 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5765 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5767 TRACE("Passing to container\n");
5768 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5771 surface->flags &= ~location;
5774 if (!(surface->flags & SFLAG_LOCATIONS))
5776 ERR("Surface %p does not have any up to date location.\n", surface);
5780 static DWORD resource_access_from_location(DWORD location)
5782 switch (location)
5784 case SFLAG_INSYSMEM:
5785 return WINED3D_RESOURCE_ACCESS_CPU;
5787 case SFLAG_INDRAWABLE:
5788 case SFLAG_INSRGBTEX:
5789 case SFLAG_INTEXTURE:
5790 case SFLAG_INRB_MULTISAMPLE:
5791 case SFLAG_INRB_RESOLVED:
5792 return WINED3D_RESOURCE_ACCESS_GPU;
5794 default:
5795 FIXME("Unhandled location %#x.\n", location);
5796 return 0;
5800 static void surface_load_sysmem(struct wined3d_surface *surface,
5801 const struct wined3d_gl_info *gl_info, const RECT *rect)
5803 surface_prepare_system_memory(surface);
5805 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5806 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5808 /* Download the surface to system memory. */
5809 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5811 struct wined3d_device *device = surface->resource.device;
5812 struct wined3d_context *context;
5814 /* TODO: Use already acquired context when possible. */
5815 context = context_acquire(device, NULL);
5817 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5818 surface_download_data(surface, gl_info);
5820 context_release(context);
5822 return;
5825 if (surface->flags & SFLAG_INDRAWABLE)
5827 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5828 wined3d_surface_get_pitch(surface));
5829 return;
5832 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5833 surface, surface->flags & SFLAG_LOCATIONS);
5836 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5837 const struct wined3d_gl_info *gl_info, const RECT *rect)
5839 struct wined3d_device *device = surface->resource.device;
5840 struct wined3d_format format;
5841 CONVERT_TYPES convert;
5842 UINT byte_count;
5843 BYTE *mem;
5845 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5847 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5848 return WINED3DERR_INVALIDCALL;
5851 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5852 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5854 if (surface->flags & SFLAG_INTEXTURE)
5856 RECT r;
5858 surface_get_rect(surface, rect, &r);
5859 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5861 return WINED3D_OK;
5864 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5866 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5867 * path through sysmem. */
5868 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5871 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5873 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5874 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5875 * called. */
5876 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5878 struct wined3d_context *context;
5880 TRACE("Removing the pbo attached to surface %p.\n", surface);
5882 /* TODO: Use already acquired context when possible. */
5883 context = context_acquire(device, NULL);
5885 surface_remove_pbo(surface, gl_info);
5887 context_release(context);
5890 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5892 UINT height = surface->resource.height;
5893 UINT width = surface->resource.width;
5894 UINT src_pitch, dst_pitch;
5896 byte_count = format.conv_byte_count;
5897 src_pitch = wined3d_surface_get_pitch(surface);
5899 /* Stick to the alignment for the converted surface too, makes it
5900 * easier to load the surface. */
5901 dst_pitch = width * byte_count;
5902 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5904 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5906 ERR("Out of memory (%u).\n", dst_pitch * height);
5907 return E_OUTOFMEMORY;
5910 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5911 src_pitch, width, height, dst_pitch, convert, surface);
5913 surface->flags |= SFLAG_CONVERTED;
5915 else
5917 surface->flags &= ~SFLAG_CONVERTED;
5918 mem = surface->resource.allocatedMemory;
5919 byte_count = format.byte_count;
5922 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5924 /* Don't delete PBO memory. */
5925 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5926 HeapFree(GetProcessHeap(), 0, mem);
5928 return WINED3D_OK;
5931 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5932 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5934 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5935 struct wined3d_device *device = surface->resource.device;
5936 struct wined3d_context *context;
5937 UINT width, src_pitch, dst_pitch;
5938 struct wined3d_bo_address data;
5939 struct wined3d_format format;
5940 POINT dst_point = {0, 0};
5941 CONVERT_TYPES convert;
5942 BYTE *mem;
5944 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
5945 && surface_is_offscreen(surface)
5946 && (surface->flags & SFLAG_INDRAWABLE))
5948 surface_load_fb_texture(surface, srgb);
5950 return WINED3D_OK;
5953 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
5954 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
5955 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5956 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5957 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5959 if (srgb)
5960 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
5961 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
5962 else
5963 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
5964 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
5966 return WINED3D_OK;
5969 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
5970 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
5971 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5972 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5973 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5975 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
5976 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
5977 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5979 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, src_location,
5980 &rect, surface, dst_location, &rect);
5982 return WINED3D_OK;
5985 /* Upload from system memory */
5987 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
5988 TRUE /* We will use textures */, &format, &convert);
5990 if (srgb)
5992 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
5994 /* Performance warning... */
5995 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
5996 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5999 else
6001 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6003 /* Performance warning... */
6004 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6005 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6009 if (!(surface->flags & SFLAG_INSYSMEM))
6011 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6012 /* Lets hope we get it from somewhere... */
6013 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6016 /* TODO: Use already acquired context when possible. */
6017 context = context_acquire(device, NULL);
6019 surface_prepare_texture(surface, context, srgb);
6020 surface_bind_and_dirtify(surface, context, srgb);
6022 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6024 surface->flags |= SFLAG_GLCKEY;
6025 surface->gl_color_key = surface->src_blt_color_key;
6027 else surface->flags &= ~SFLAG_GLCKEY;
6029 width = surface->resource.width;
6030 src_pitch = wined3d_surface_get_pitch(surface);
6032 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6033 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6034 * called. */
6035 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6037 TRACE("Removing the pbo attached to surface %p.\n", surface);
6038 surface_remove_pbo(surface, gl_info);
6041 if (format.convert)
6043 /* This code is entered for texture formats which need a fixup. */
6044 UINT height = surface->resource.height;
6046 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6047 dst_pitch = width * format.conv_byte_count;
6048 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6050 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6052 ERR("Out of memory (%u).\n", dst_pitch * height);
6053 context_release(context);
6054 return E_OUTOFMEMORY;
6056 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6057 format.byte_count = format.conv_byte_count;
6058 src_pitch = dst_pitch;
6060 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6062 /* This code is only entered for color keying fixups */
6063 UINT height = surface->resource.height;
6065 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6066 dst_pitch = width * format.conv_byte_count;
6067 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6069 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6071 ERR("Out of memory (%u).\n", dst_pitch * height);
6072 context_release(context);
6073 return E_OUTOFMEMORY;
6075 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6076 width, height, dst_pitch, convert, surface);
6077 format.byte_count = format.conv_byte_count;
6078 src_pitch = dst_pitch;
6080 else
6082 mem = surface->resource.allocatedMemory;
6085 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6086 data.addr = mem;
6087 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
6089 context_release(context);
6091 /* Don't delete PBO memory. */
6092 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6093 HeapFree(GetProcessHeap(), 0, mem);
6095 return WINED3D_OK;
6098 static void surface_multisample_resolve(struct wined3d_surface *surface)
6100 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6102 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6103 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6105 surface_blt_fbo(surface->resource.device, WINED3DTEXF_POINT,
6106 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6109 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6111 struct wined3d_device *device = surface->resource.device;
6112 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6113 HRESULT hr;
6115 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6117 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6119 if (location == SFLAG_INTEXTURE)
6121 struct wined3d_context *context = context_acquire(device, NULL);
6122 surface_load_ds_location(surface, context, location);
6123 context_release(context);
6124 return WINED3D_OK;
6126 else
6128 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6129 return WINED3DERR_INVALIDCALL;
6133 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6134 location = SFLAG_INTEXTURE;
6136 if (surface->flags & location)
6138 TRACE("Location already up to date.\n");
6140 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
6141 && surface_need_pbo(surface, gl_info))
6142 surface_load_pbo(surface, gl_info);
6144 return WINED3D_OK;
6147 if (WARN_ON(d3d_surface))
6149 DWORD required_access = resource_access_from_location(location);
6150 if ((surface->resource.access_flags & required_access) != required_access)
6151 WARN("Operation requires %#x access, but surface only has %#x.\n",
6152 required_access, surface->resource.access_flags);
6155 if (!(surface->flags & SFLAG_LOCATIONS))
6157 ERR("Surface %p does not have any up to date location.\n", surface);
6158 surface->flags |= SFLAG_LOST;
6159 return WINED3DERR_DEVICELOST;
6162 switch (location)
6164 case SFLAG_INSYSMEM:
6165 surface_load_sysmem(surface, gl_info, rect);
6166 break;
6168 case SFLAG_INDRAWABLE:
6169 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6170 return hr;
6171 break;
6173 case SFLAG_INRB_RESOLVED:
6174 surface_multisample_resolve(surface);
6175 break;
6177 case SFLAG_INTEXTURE:
6178 case SFLAG_INSRGBTEX:
6179 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6180 return hr;
6181 break;
6183 default:
6184 ERR("Don't know how to handle location %#x.\n", location);
6185 break;
6188 if (!rect)
6190 surface->flags |= location;
6192 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6193 surface_evict_sysmem(surface);
6196 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6197 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6199 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6202 return WINED3D_OK;
6205 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6207 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6209 /* Not on a swapchain - must be offscreen */
6210 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6212 /* The front buffer is always onscreen */
6213 if (surface == swapchain->front_buffer) return FALSE;
6215 /* If the swapchain is rendered to an FBO, the backbuffer is
6216 * offscreen, otherwise onscreen */
6217 return swapchain->render_to_fbo;
6220 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6221 /* Context activation is done by the caller. */
6222 static void ffp_blit_free(struct wined3d_device *device) { }
6224 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6225 /* Context activation is done by the caller. */
6226 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6228 BYTE table[256][4];
6229 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6231 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6233 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6234 ENTER_GL();
6235 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6236 LEAVE_GL();
6239 /* Context activation is done by the caller. */
6240 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6242 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6244 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6245 * else the surface is converted in software at upload time in LoadLocation.
6247 if (!(surface->flags & SFLAG_CONVERTED) && fixup == COMPLEX_FIXUP_P8
6248 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6249 ffp_blit_p8_upload_palette(surface, context->gl_info);
6251 ENTER_GL();
6252 glEnable(surface->texture_target);
6253 checkGLcall("glEnable(surface->texture_target)");
6254 LEAVE_GL();
6255 return WINED3D_OK;
6258 /* Context activation is done by the caller. */
6259 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6261 ENTER_GL();
6262 glDisable(GL_TEXTURE_2D);
6263 checkGLcall("glDisable(GL_TEXTURE_2D)");
6264 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6266 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6267 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6269 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6271 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6272 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6274 LEAVE_GL();
6277 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6278 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6279 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6281 enum complex_fixup src_fixup;
6283 switch (blit_op)
6285 case WINED3D_BLIT_OP_COLOR_BLIT:
6286 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
6287 return FALSE;
6289 src_fixup = get_complex_fixup(src_format->color_fixup);
6290 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6292 TRACE("Checking support for fixup:\n");
6293 dump_color_fixup_desc(src_format->color_fixup);
6296 if (!is_identity_fixup(dst_format->color_fixup))
6298 TRACE("Destination fixups are not supported\n");
6299 return FALSE;
6302 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6304 TRACE("P8 fixup supported\n");
6305 return TRUE;
6308 /* We only support identity conversions. */
6309 if (is_identity_fixup(src_format->color_fixup))
6311 TRACE("[OK]\n");
6312 return TRUE;
6315 TRACE("[FAILED]\n");
6316 return FALSE;
6318 case WINED3D_BLIT_OP_COLOR_FILL:
6319 if (dst_pool == WINED3DPOOL_SYSTEMMEM)
6320 return FALSE;
6322 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6324 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6325 return FALSE;
6327 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6329 TRACE("Color fill not supported\n");
6330 return FALSE;
6333 /* FIXME: We should reject color fills on formats with fixups,
6334 * but this would break P8 color fills for example. */
6336 return TRUE;
6338 case WINED3D_BLIT_OP_DEPTH_FILL:
6339 return TRUE;
6341 default:
6342 TRACE("Unsupported blit_op=%d\n", blit_op);
6343 return FALSE;
6347 /* Do not call while under the GL lock. */
6348 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6349 const RECT *dst_rect, const struct wined3d_color *color)
6351 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6352 struct wined3d_fb_state fb = {&dst_surface, NULL};
6354 return device_clear_render_targets(device, 1, &fb,
6355 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6358 /* Do not call while under the GL lock. */
6359 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6360 struct wined3d_surface *surface, const RECT *rect, float depth)
6362 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6363 struct wined3d_fb_state fb = {NULL, surface};
6365 return device_clear_render_targets(device, 0, &fb,
6366 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6369 const struct blit_shader ffp_blit = {
6370 ffp_blit_alloc,
6371 ffp_blit_free,
6372 ffp_blit_set,
6373 ffp_blit_unset,
6374 ffp_blit_supported,
6375 ffp_blit_color_fill,
6376 ffp_blit_depth_fill,
6379 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6381 return WINED3D_OK;
6384 /* Context activation is done by the caller. */
6385 static void cpu_blit_free(struct wined3d_device *device)
6389 /* Context activation is done by the caller. */
6390 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6392 return WINED3D_OK;
6395 /* Context activation is done by the caller. */
6396 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6400 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6401 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6402 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6404 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6406 return TRUE;
6409 return FALSE;
6412 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
6413 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
6414 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
6416 UINT row_block_count;
6417 const BYTE *src_row;
6418 BYTE *dst_row;
6419 UINT x, y;
6421 src_row = src_data;
6422 dst_row = dst_data;
6424 row_block_count = (update_w + format->block_width - 1) / format->block_width;
6426 if (!flags)
6428 for (y = 0; y < update_h; y += format->block_height)
6430 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
6431 src_row += src_pitch;
6432 dst_row += dst_pitch;
6435 return WINED3D_OK;
6438 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
6440 src_row += (((update_h / format->block_height) - 1) * src_pitch);
6442 switch (format->id)
6444 case WINED3DFMT_DXT1:
6445 for (y = 0; y < update_h; y += format->block_height)
6447 struct block
6449 WORD color[2];
6450 BYTE control_row[4];
6453 const struct block *s = (const struct block *)src_row;
6454 struct block *d = (struct block *)dst_row;
6456 for (x = 0; x < row_block_count; ++x)
6458 d[x].color[0] = s[x].color[0];
6459 d[x].color[1] = s[x].color[1];
6460 d[x].control_row[0] = s[x].control_row[3];
6461 d[x].control_row[1] = s[x].control_row[2];
6462 d[x].control_row[2] = s[x].control_row[1];
6463 d[x].control_row[3] = s[x].control_row[0];
6465 src_row -= src_pitch;
6466 dst_row += dst_pitch;
6468 return WINED3D_OK;
6470 case WINED3DFMT_DXT3:
6471 for (y = 0; y < update_h; y += format->block_height)
6473 struct block
6475 WORD alpha_row[4];
6476 WORD color[2];
6477 BYTE control_row[4];
6480 const struct block *s = (const struct block *)src_row;
6481 struct block *d = (struct block *)dst_row;
6483 for (x = 0; x < row_block_count; ++x)
6485 d[x].alpha_row[0] = s[x].alpha_row[3];
6486 d[x].alpha_row[1] = s[x].alpha_row[2];
6487 d[x].alpha_row[2] = s[x].alpha_row[1];
6488 d[x].alpha_row[3] = s[x].alpha_row[0];
6489 d[x].color[0] = s[x].color[0];
6490 d[x].color[1] = s[x].color[1];
6491 d[x].control_row[0] = s[x].control_row[3];
6492 d[x].control_row[1] = s[x].control_row[2];
6493 d[x].control_row[2] = s[x].control_row[1];
6494 d[x].control_row[3] = s[x].control_row[0];
6496 src_row -= src_pitch;
6497 dst_row += dst_pitch;
6499 return WINED3D_OK;
6501 default:
6502 FIXME("Compressed flip not implemented for format %s.\n",
6503 debug_d3dformat(format->id));
6504 return E_NOTIMPL;
6508 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
6509 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
6511 return E_NOTIMPL;
6514 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6515 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6516 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6518 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6519 const struct wined3d_format *src_format, *dst_format;
6520 struct wined3d_surface *orig_src = src_surface;
6521 struct wined3d_mapped_rect dst_map, src_map;
6522 HRESULT hr = WINED3D_OK;
6523 const BYTE *sbuf;
6524 RECT xdst,xsrc;
6525 BYTE *dbuf;
6526 int x, y;
6528 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6529 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6530 flags, fx, debug_d3dtexturefiltertype(filter));
6532 xsrc = *src_rect;
6534 if (!src_surface)
6536 RECT full_rect;
6538 full_rect.left = 0;
6539 full_rect.top = 0;
6540 full_rect.right = dst_surface->resource.width;
6541 full_rect.bottom = dst_surface->resource.height;
6542 IntersectRect(&xdst, &full_rect, dst_rect);
6544 else
6546 BOOL clip_horiz, clip_vert;
6548 xdst = *dst_rect;
6549 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6550 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6552 if (clip_vert || clip_horiz)
6554 /* Now check if this is a special case or not... */
6555 if ((flags & WINEDDBLT_DDFX)
6556 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6557 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6559 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6560 return WINED3D_OK;
6563 if (clip_horiz)
6565 if (xdst.left < 0)
6567 xsrc.left -= xdst.left;
6568 xdst.left = 0;
6570 if (xdst.right > dst_surface->resource.width)
6572 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6573 xdst.right = (int)dst_surface->resource.width;
6577 if (clip_vert)
6579 if (xdst.top < 0)
6581 xsrc.top -= xdst.top;
6582 xdst.top = 0;
6584 if (xdst.bottom > dst_surface->resource.height)
6586 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6587 xdst.bottom = (int)dst_surface->resource.height;
6591 /* And check if after clipping something is still to be done... */
6592 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6593 || (xdst.left >= (int)dst_surface->resource.width)
6594 || (xdst.top >= (int)dst_surface->resource.height)
6595 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6596 || (xsrc.left >= (int)src_surface->resource.width)
6597 || (xsrc.top >= (int)src_surface->resource.height))
6599 TRACE("Nothing to be done after clipping.\n");
6600 return WINED3D_OK;
6605 if (src_surface == dst_surface)
6607 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6608 src_map = dst_map;
6609 src_format = dst_surface->resource.format;
6610 dst_format = src_format;
6612 else
6614 dst_format = dst_surface->resource.format;
6615 if (src_surface)
6617 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6619 src_surface = surface_convert_format(src_surface, dst_format->id);
6620 if (!src_surface)
6622 /* The conv function writes a FIXME */
6623 WARN("Cannot convert source surface format to dest format.\n");
6624 goto release;
6627 wined3d_surface_map(src_surface, &src_map, NULL, WINED3DLOCK_READONLY);
6628 src_format = src_surface->resource.format;
6630 else
6632 src_format = dst_format;
6634 if (dst_rect)
6635 wined3d_surface_map(dst_surface, &dst_map, &xdst, 0);
6636 else
6637 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6640 bpp = dst_surface->resource.format->byte_count;
6641 srcheight = xsrc.bottom - xsrc.top;
6642 srcwidth = xsrc.right - xsrc.left;
6643 dstheight = xdst.bottom - xdst.top;
6644 dstwidth = xdst.right - xdst.left;
6645 width = (xdst.right - xdst.left) * bpp;
6647 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
6649 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6651 if (src_surface == dst_surface)
6653 FIXME("Only plain blits supported on compressed surfaces.\n");
6654 hr = E_NOTIMPL;
6655 goto release;
6658 if (srcheight != dstheight || srcwidth != dstwidth)
6660 WARN("Stretching not supported on compressed surfaces.\n");
6661 hr = WINED3DERR_INVALIDCALL;
6662 goto release;
6665 if (srcwidth & (src_format->block_width - 1) || srcheight & (src_format->block_height - 1))
6667 WARN("Rectangle not block-aligned.\n");
6668 hr = WINED3DERR_INVALIDCALL;
6669 goto release;
6672 hr = surface_cpu_blt_compressed(src_map.data, dst_map.data,
6673 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
6674 src_format, flags, fx);
6675 goto release;
6678 if (dst_rect && src_surface != dst_surface)
6679 dbuf = dst_map.data;
6680 else
6681 dbuf = (BYTE *)dst_map.data + (xdst.top * dst_map.row_pitch) + (xdst.left * bpp);
6683 /* First, all the 'source-less' blits */
6684 if (flags & WINEDDBLT_COLORFILL)
6686 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
6687 flags &= ~WINEDDBLT_COLORFILL;
6690 if (flags & WINEDDBLT_DEPTHFILL)
6692 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6694 if (flags & WINEDDBLT_ROP)
6696 /* Catch some degenerate cases here. */
6697 switch (fx->dwROP)
6699 case BLACKNESS:
6700 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
6701 break;
6702 case 0xAA0029: /* No-op */
6703 break;
6704 case WHITENESS:
6705 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
6706 break;
6707 case SRCCOPY: /* Well, we do that below? */
6708 break;
6709 default:
6710 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6711 goto error;
6713 flags &= ~WINEDDBLT_ROP;
6715 if (flags & WINEDDBLT_DDROPS)
6717 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6719 /* Now the 'with source' blits. */
6720 if (src_surface)
6722 const BYTE *sbase;
6723 int sx, xinc, sy, yinc;
6725 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6726 goto release;
6728 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6729 && (srcwidth != dstwidth || srcheight != dstheight))
6731 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6732 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6735 sbase = (BYTE *)src_map.data + (xsrc.top * src_map.row_pitch) + xsrc.left * bpp;
6736 xinc = (srcwidth << 16) / dstwidth;
6737 yinc = (srcheight << 16) / dstheight;
6739 if (!flags)
6741 /* No effects, we can cheat here. */
6742 if (dstwidth == srcwidth)
6744 if (dstheight == srcheight)
6746 /* No stretching in either direction. This needs to be as
6747 * fast as possible. */
6748 sbuf = sbase;
6750 /* Check for overlapping surfaces. */
6751 if (src_surface != dst_surface || xdst.top < xsrc.top
6752 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6754 /* No overlap, or dst above src, so copy from top downwards. */
6755 for (y = 0; y < dstheight; ++y)
6757 memcpy(dbuf, sbuf, width);
6758 sbuf += src_map.row_pitch;
6759 dbuf += dst_map.row_pitch;
6762 else if (xdst.top > xsrc.top)
6764 /* Copy from bottom upwards. */
6765 sbuf += src_map.row_pitch * dstheight;
6766 dbuf += dst_map.row_pitch * dstheight;
6767 for (y = 0; y < dstheight; ++y)
6769 sbuf -= src_map.row_pitch;
6770 dbuf -= dst_map.row_pitch;
6771 memcpy(dbuf, sbuf, width);
6774 else
6776 /* Src and dst overlapping on the same line, use memmove. */
6777 for (y = 0; y < dstheight; ++y)
6779 memmove(dbuf, sbuf, width);
6780 sbuf += src_map.row_pitch;
6781 dbuf += dst_map.row_pitch;
6785 else
6787 /* Stretching in y direction only. */
6788 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6790 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6791 memcpy(dbuf, sbuf, width);
6792 dbuf += dst_map.row_pitch;
6796 else
6798 /* Stretching in X direction. */
6799 int last_sy = -1;
6800 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6802 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6804 if ((sy >> 16) == (last_sy >> 16))
6806 /* This source row is the same as last source row -
6807 * Copy the already stretched row. */
6808 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
6810 else
6812 #define STRETCH_ROW(type) \
6813 do { \
6814 const type *s = (const type *)sbuf; \
6815 type *d = (type *)dbuf; \
6816 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6817 d[x] = s[sx >> 16]; \
6818 } while(0)
6820 switch(bpp)
6822 case 1:
6823 STRETCH_ROW(BYTE);
6824 break;
6825 case 2:
6826 STRETCH_ROW(WORD);
6827 break;
6828 case 4:
6829 STRETCH_ROW(DWORD);
6830 break;
6831 case 3:
6833 const BYTE *s;
6834 BYTE *d = dbuf;
6835 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6837 DWORD pixel;
6839 s = sbuf + 3 * (sx >> 16);
6840 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6841 d[0] = (pixel ) & 0xff;
6842 d[1] = (pixel >> 8) & 0xff;
6843 d[2] = (pixel >> 16) & 0xff;
6844 d += 3;
6846 break;
6848 default:
6849 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6850 hr = WINED3DERR_NOTAVAILABLE;
6851 goto error;
6853 #undef STRETCH_ROW
6855 dbuf += dst_map.row_pitch;
6856 last_sy = sy;
6860 else
6862 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
6863 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6864 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6865 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6867 /* The color keying flags are checked for correctness in ddraw */
6868 if (flags & WINEDDBLT_KEYSRC)
6870 keylow = src_surface->src_blt_color_key.color_space_low_value;
6871 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
6873 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6875 keylow = fx->ddckSrcColorkey.color_space_low_value;
6876 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
6879 if (flags & WINEDDBLT_KEYDEST)
6881 /* Destination color keys are taken from the source surface! */
6882 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
6883 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
6885 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6887 destkeylow = fx->ddckDestColorkey.color_space_low_value;
6888 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
6891 if (bpp == 1)
6893 keymask = 0xff;
6895 else
6897 keymask = src_format->red_mask
6898 | src_format->green_mask
6899 | src_format->blue_mask;
6901 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6904 if (flags & WINEDDBLT_DDFX)
6906 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6907 LONG tmpxy;
6908 dTopLeft = dbuf;
6909 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6910 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
6911 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6913 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6915 /* I don't think we need to do anything about this flag */
6916 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6918 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6920 tmp = dTopRight;
6921 dTopRight = dTopLeft;
6922 dTopLeft = tmp;
6923 tmp = dBottomRight;
6924 dBottomRight = dBottomLeft;
6925 dBottomLeft = tmp;
6926 dstxinc = dstxinc * -1;
6928 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6930 tmp = dTopLeft;
6931 dTopLeft = dBottomLeft;
6932 dBottomLeft = tmp;
6933 tmp = dTopRight;
6934 dTopRight = dBottomRight;
6935 dBottomRight = tmp;
6936 dstyinc = dstyinc * -1;
6938 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6940 /* I don't think we need to do anything about this flag */
6941 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6943 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6945 tmp = dBottomRight;
6946 dBottomRight = dTopLeft;
6947 dTopLeft = tmp;
6948 tmp = dBottomLeft;
6949 dBottomLeft = dTopRight;
6950 dTopRight = tmp;
6951 dstxinc = dstxinc * -1;
6952 dstyinc = dstyinc * -1;
6954 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6956 tmp = dTopLeft;
6957 dTopLeft = dBottomLeft;
6958 dBottomLeft = dBottomRight;
6959 dBottomRight = dTopRight;
6960 dTopRight = tmp;
6961 tmpxy = dstxinc;
6962 dstxinc = dstyinc;
6963 dstyinc = tmpxy;
6964 dstxinc = dstxinc * -1;
6966 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6968 tmp = dTopLeft;
6969 dTopLeft = dTopRight;
6970 dTopRight = dBottomRight;
6971 dBottomRight = dBottomLeft;
6972 dBottomLeft = tmp;
6973 tmpxy = dstxinc;
6974 dstxinc = dstyinc;
6975 dstyinc = tmpxy;
6976 dstyinc = dstyinc * -1;
6978 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6980 /* I don't think we need to do anything about this flag */
6981 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6983 dbuf = dTopLeft;
6984 flags &= ~(WINEDDBLT_DDFX);
6987 #define COPY_COLORKEY_FX(type) \
6988 do { \
6989 const type *s; \
6990 type *d = (type *)dbuf, *dx, tmp; \
6991 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6993 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
6994 dx = d; \
6995 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6997 tmp = s[sx >> 16]; \
6998 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6999 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
7001 dx[0] = tmp; \
7003 dx = (type *)(((BYTE *)dx) + dstxinc); \
7005 d = (type *)(((BYTE *)d) + dstyinc); \
7007 } while(0)
7009 switch (bpp)
7011 case 1:
7012 COPY_COLORKEY_FX(BYTE);
7013 break;
7014 case 2:
7015 COPY_COLORKEY_FX(WORD);
7016 break;
7017 case 4:
7018 COPY_COLORKEY_FX(DWORD);
7019 break;
7020 case 3:
7022 const BYTE *s;
7023 BYTE *d = dbuf, *dx;
7024 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
7026 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
7027 dx = d;
7028 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
7030 DWORD pixel, dpixel = 0;
7031 s = sbuf + 3 * (sx>>16);
7032 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
7033 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
7034 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
7035 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
7037 dx[0] = (pixel ) & 0xff;
7038 dx[1] = (pixel >> 8) & 0xff;
7039 dx[2] = (pixel >> 16) & 0xff;
7041 dx += dstxinc;
7043 d += dstyinc;
7045 break;
7047 default:
7048 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7049 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7050 hr = WINED3DERR_NOTAVAILABLE;
7051 goto error;
7052 #undef COPY_COLORKEY_FX
7057 error:
7058 if (flags && FIXME_ON(d3d_surface))
7060 FIXME("\tUnsupported flags: %#x.\n", flags);
7063 release:
7064 wined3d_surface_unmap(dst_surface);
7065 if (src_surface && src_surface != dst_surface)
7066 wined3d_surface_unmap(src_surface);
7067 /* Release the converted surface, if any. */
7068 if (src_surface && src_surface != orig_src)
7069 wined3d_surface_decref(src_surface);
7071 return hr;
7074 /* Do not call while under the GL lock. */
7075 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7076 const RECT *dst_rect, const struct wined3d_color *color)
7078 static const RECT src_rect;
7079 WINEDDBLTFX BltFx;
7081 memset(&BltFx, 0, sizeof(BltFx));
7082 BltFx.dwSize = sizeof(BltFx);
7083 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7084 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7085 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7088 /* Do not call while under the GL lock. */
7089 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7090 struct wined3d_surface *surface, const RECT *rect, float depth)
7092 FIXME("Depth filling not implemented by cpu_blit.\n");
7093 return WINED3DERR_INVALIDCALL;
7096 const struct blit_shader cpu_blit = {
7097 cpu_blit_alloc,
7098 cpu_blit_free,
7099 cpu_blit_set,
7100 cpu_blit_unset,
7101 cpu_blit_supported,
7102 cpu_blit_color_fill,
7103 cpu_blit_depth_fill,
7106 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7107 UINT width, UINT height, UINT level, enum wined3d_multisample_type multisample_type,
7108 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7109 WINED3DPOOL pool, DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops)
7111 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7112 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7113 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
7114 unsigned int resource_size;
7115 HRESULT hr;
7117 if (multisample_quality > 0)
7119 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7120 multisample_quality = 0;
7123 /* Quick lockable sanity check.
7124 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7125 * this function is too deep to need to care about things like this.
7126 * Levels need to be checked too, since they all affect what can be done. */
7127 switch (pool)
7129 case WINED3DPOOL_SCRATCH:
7130 if (!lockable)
7132 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7133 "which are mutually exclusive, setting lockable to TRUE.\n");
7134 lockable = TRUE;
7136 break;
7138 case WINED3DPOOL_SYSTEMMEM:
7139 if (!lockable)
7140 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7141 break;
7143 case WINED3DPOOL_MANAGED:
7144 if (usage & WINED3DUSAGE_DYNAMIC)
7145 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7146 break;
7148 case WINED3DPOOL_DEFAULT:
7149 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7150 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7151 break;
7153 default:
7154 FIXME("Unknown pool %#x.\n", pool);
7155 break;
7158 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7159 FIXME("Trying to create a render target that isn't in the default pool.\n");
7161 /* FIXME: Check that the format is supported by the device. */
7163 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7164 if (!resource_size)
7165 return WINED3DERR_INVALIDCALL;
7167 surface->surface_type = surface_type;
7169 switch (surface_type)
7171 case SURFACE_OPENGL:
7172 surface->surface_ops = &surface_ops;
7173 break;
7175 case SURFACE_GDI:
7176 surface->surface_ops = &gdi_surface_ops;
7177 break;
7179 default:
7180 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7181 return WINED3DERR_INVALIDCALL;
7184 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7185 multisample_type, multisample_quality, usage, pool, width, height, 1,
7186 resource_size, parent, parent_ops, &surface_resource_ops);
7187 if (FAILED(hr))
7189 WARN("Failed to initialize resource, returning %#x.\n", hr);
7190 return hr;
7193 /* "Standalone" surface. */
7194 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7196 surface->texture_level = level;
7197 list_init(&surface->overlays);
7199 /* Flags */
7200 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7201 if (flags & WINED3D_SURFACE_DISCARD)
7202 surface->flags |= SFLAG_DISCARD;
7203 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
7204 surface->flags |= SFLAG_PIN_SYSMEM;
7205 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7206 surface->flags |= SFLAG_LOCKABLE;
7207 /* I'm not sure if this qualifies as a hack or as an optimization. It
7208 * seems reasonable to assume that lockable render targets will get
7209 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7210 * creation. However, the other reason we want to do this is that several
7211 * ddraw applications access surface memory while the surface isn't
7212 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7213 * future locks prevents these from crashing. */
7214 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7215 surface->flags |= SFLAG_DYNLOCK;
7217 /* Mark the texture as dirty so that it gets loaded first time around. */
7218 surface_add_dirty_rect(surface, NULL);
7219 list_init(&surface->renderbuffers);
7221 TRACE("surface %p, memory %p, size %u\n",
7222 surface, surface->resource.allocatedMemory, surface->resource.size);
7224 /* Call the private setup routine */
7225 hr = surface->surface_ops->surface_private_setup(surface);
7226 if (FAILED(hr))
7228 ERR("Private setup failed, returning %#x\n", hr);
7229 surface_cleanup(surface);
7230 return hr;
7233 /* Similar to lockable rendertargets above, creating the DIB section
7234 * during surface initialization prevents the sysmem pointer from changing
7235 * after a wined3d_surface_getdc() call. */
7236 if ((usage & WINED3DUSAGE_OWNDC) && !surface->hDC
7237 && SUCCEEDED(surface_create_dib_section(surface)))
7239 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
7240 surface->resource.heapMemory = NULL;
7241 surface->resource.allocatedMemory = surface->dib.bitmap_data;
7244 return hr;
7247 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7248 enum wined3d_format_id format_id, UINT level, DWORD usage, WINED3DPOOL pool,
7249 enum wined3d_multisample_type multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7250 DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7252 struct wined3d_surface *object;
7253 HRESULT hr;
7255 TRACE("device %p, width %u, height %u, format %s, level %u\n",
7256 device, width, height, debug_d3dformat(format_id), level);
7257 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7258 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7259 TRACE("surface_type %#x, flags %#x, parent %p, parent_ops %p.\n", surface_type, flags, parent, parent_ops);
7261 if (surface_type == SURFACE_OPENGL && !device->adapter)
7263 ERR("OpenGL surfaces are not available without OpenGL.\n");
7264 return WINED3DERR_NOTAVAILABLE;
7267 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7268 if (!object)
7270 ERR("Failed to allocate surface memory.\n");
7271 return WINED3DERR_OUTOFVIDEOMEMORY;
7274 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level,
7275 multisample_type, multisample_quality, device, usage, format_id, pool, flags, parent, parent_ops);
7276 if (FAILED(hr))
7278 WARN("Failed to initialize surface, returning %#x.\n", hr);
7279 HeapFree(GetProcessHeap(), 0, object);
7280 return hr;
7283 TRACE("Created surface %p.\n", object);
7284 *surface = object;
7286 return hr;