ddraw: Implement width and height changes in ddraw_surface7_SetSurfaceDesc().
[wine/wine-gecko.git] / dlls / wined3d / surface.c
blob2cf01f988e964befb879af374e1beef67bc97285
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 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
768 surface->flags |= SFLAG_LOST;
770 return WINED3D_OK;
773 static void surface_realize_palette(struct wined3d_surface *surface)
775 struct wined3d_palette *palette = surface->palette;
777 TRACE("surface %p.\n", surface);
779 if (!palette) return;
781 if (surface->resource.format->id == WINED3DFMT_P8_UINT
782 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
784 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
786 /* Make sure the texture is up to date. This call doesn't do
787 * anything if the texture is already up to date. */
788 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
790 /* We want to force a palette refresh, so mark the drawable as not being up to date */
791 if (!surface_is_offscreen(surface))
792 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
794 else
796 if (!(surface->flags & SFLAG_INSYSMEM))
798 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
799 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
801 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
805 if (surface->flags & SFLAG_DIBSECTION)
807 RGBQUAD col[256];
808 unsigned int i;
810 TRACE("Updating the DC's palette.\n");
812 for (i = 0; i < 256; ++i)
814 col[i].rgbRed = palette->palents[i].peRed;
815 col[i].rgbGreen = palette->palents[i].peGreen;
816 col[i].rgbBlue = palette->palents[i].peBlue;
817 col[i].rgbReserved = 0;
819 SetDIBColorTable(surface->hDC, 0, 256, col);
822 /* Propagate the changes to the drawable when we have a palette. */
823 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
824 surface_load_location(surface, surface->draw_binding, NULL);
827 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
829 HRESULT hr;
831 /* If there's no destination surface there is nothing to do. */
832 if (!surface->overlay_dest)
833 return WINED3D_OK;
835 /* Blt calls ModifyLocation on the dest surface, which in turn calls
836 * DrawOverlay to update the overlay. Prevent an endless recursion. */
837 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
838 return WINED3D_OK;
840 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
841 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
842 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3DTEXF_LINEAR);
843 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
845 return hr;
848 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
850 struct wined3d_device *device = surface->resource.device;
851 const RECT *pass_rect = rect;
853 TRACE("surface %p, rect %s, flags %#x.\n",
854 surface, wine_dbgstr_rect(rect), flags);
856 if (flags & WINED3DLOCK_DISCARD)
858 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
859 surface_prepare_system_memory(surface);
860 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
862 else
864 /* surface_load_location() does not check if the rectangle specifies
865 * the full surface. Most callers don't need that, so do it here. */
866 if (rect && !rect->top && !rect->left
867 && rect->right == surface->resource.width
868 && rect->bottom == surface->resource.height)
869 pass_rect = NULL;
870 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
873 if (surface->flags & SFLAG_PBO)
875 const struct wined3d_gl_info *gl_info;
876 struct wined3d_context *context;
878 context = context_acquire(device, NULL);
879 gl_info = context->gl_info;
881 ENTER_GL();
882 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
883 checkGLcall("glBindBufferARB");
885 /* This shouldn't happen but could occur if some other function
886 * didn't handle the PBO properly. */
887 if (surface->resource.allocatedMemory)
888 ERR("The surface already has PBO memory allocated.\n");
890 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
891 checkGLcall("glMapBufferARB");
893 /* Make sure the PBO isn't set anymore in order not to break non-PBO
894 * calls. */
895 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
896 checkGLcall("glBindBufferARB");
898 LEAVE_GL();
899 context_release(context);
902 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
904 if (!rect)
905 surface_add_dirty_rect(surface, NULL);
906 else
908 struct wined3d_box b;
910 b.left = rect->left;
911 b.top = rect->top;
912 b.right = rect->right;
913 b.bottom = rect->bottom;
914 b.front = 0;
915 b.back = 1;
916 surface_add_dirty_rect(surface, &b);
921 static void surface_unmap(struct wined3d_surface *surface)
923 struct wined3d_device *device = surface->resource.device;
924 BOOL fullsurface;
926 TRACE("surface %p.\n", surface);
928 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
930 if (surface->flags & SFLAG_PBO)
932 const struct wined3d_gl_info *gl_info;
933 struct wined3d_context *context;
935 TRACE("Freeing PBO memory.\n");
937 context = context_acquire(device, NULL);
938 gl_info = context->gl_info;
940 ENTER_GL();
941 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
942 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
943 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
944 checkGLcall("glUnmapBufferARB");
945 LEAVE_GL();
946 context_release(context);
948 surface->resource.allocatedMemory = NULL;
951 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
953 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
955 TRACE("Not dirtified, nothing to do.\n");
956 goto done;
959 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
960 || (device->fb.render_targets && surface == device->fb.render_targets[0]))
962 if (!surface->dirtyRect.left && !surface->dirtyRect.top
963 && surface->dirtyRect.right == surface->resource.width
964 && surface->dirtyRect.bottom == surface->resource.height)
966 fullsurface = TRUE;
968 else
970 /* TODO: Proper partial rectangle tracking. */
971 fullsurface = FALSE;
972 surface->flags |= SFLAG_INSYSMEM;
975 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
977 /* Partial rectangle tracking is not commonly implemented, it is only
978 * done for render targets. INSYSMEM was set before to tell
979 * surface_load_location() where to read the rectangle from.
980 * Indrawable is set because all modifications from the partial
981 * sysmem copy are written back to the drawable, thus the surface is
982 * merged again in the drawable. The sysmem copy is not fully up to
983 * date because only a subrectangle was read in Map(). */
984 if (!fullsurface)
986 surface_modify_location(surface, surface->draw_binding, TRUE);
987 surface_evict_sysmem(surface);
990 surface->dirtyRect.left = surface->resource.width;
991 surface->dirtyRect.top = surface->resource.height;
992 surface->dirtyRect.right = 0;
993 surface->dirtyRect.bottom = 0;
995 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
997 FIXME("Depth / stencil buffer locking is not implemented.\n");
1000 done:
1001 /* Overlays have to be redrawn manually after changes with the GL implementation */
1002 if (surface->overlay_dest)
1003 surface_draw_overlay(surface);
1006 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1008 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1009 return FALSE;
1010 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1011 return FALSE;
1012 return TRUE;
1015 static void wined3d_surface_depth_blt_fbo(const struct wined3d_device *device, struct wined3d_surface *src_surface,
1016 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1018 const struct wined3d_gl_info *gl_info;
1019 struct wined3d_context *context;
1020 DWORD src_mask, dst_mask;
1021 GLbitfield gl_mask;
1023 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1024 device, src_surface, wine_dbgstr_rect(src_rect),
1025 dst_surface, wine_dbgstr_rect(dst_rect));
1027 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1028 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1030 if (src_mask != dst_mask)
1032 ERR("Incompatible formats %s and %s.\n",
1033 debug_d3dformat(src_surface->resource.format->id),
1034 debug_d3dformat(dst_surface->resource.format->id));
1035 return;
1038 if (!src_mask)
1040 ERR("Not a depth / stencil format: %s.\n",
1041 debug_d3dformat(src_surface->resource.format->id));
1042 return;
1045 gl_mask = 0;
1046 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1047 gl_mask |= GL_DEPTH_BUFFER_BIT;
1048 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1049 gl_mask |= GL_STENCIL_BUFFER_BIT;
1051 /* Make sure the locations are up-to-date. Loading the destination
1052 * surface isn't required if the entire surface is overwritten. */
1053 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1054 if (!surface_is_full_rect(dst_surface, dst_rect))
1055 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1057 context = context_acquire(device, NULL);
1058 if (!context->valid)
1060 context_release(context);
1061 WARN("Invalid context, skipping blit.\n");
1062 return;
1065 gl_info = context->gl_info;
1067 ENTER_GL();
1069 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1070 glReadBuffer(GL_NONE);
1071 checkGLcall("glReadBuffer()");
1072 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1074 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1075 context_set_draw_buffer(context, GL_NONE);
1076 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1078 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1080 glDepthMask(GL_TRUE);
1081 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
1083 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1085 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1087 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1088 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
1090 glStencilMask(~0U);
1091 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
1094 glDisable(GL_SCISSOR_TEST);
1095 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1097 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1098 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1099 checkGLcall("glBlitFramebuffer()");
1101 LEAVE_GL();
1103 if (wined3d_settings.strict_draw_ordering)
1104 wglFlush(); /* Flush to ensure ordering across contexts. */
1106 context_release(context);
1109 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1110 * Depth / stencil is not supported. */
1111 static void surface_blt_fbo(const struct wined3d_device *device, const WINED3DTEXTUREFILTERTYPE filter,
1112 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1113 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1115 const struct wined3d_gl_info *gl_info;
1116 struct wined3d_context *context;
1117 RECT src_rect, dst_rect;
1118 GLenum gl_filter;
1119 GLenum buffer;
1121 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1122 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1123 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1124 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1125 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1127 src_rect = *src_rect_in;
1128 dst_rect = *dst_rect_in;
1130 switch (filter)
1132 case WINED3DTEXF_LINEAR:
1133 gl_filter = GL_LINEAR;
1134 break;
1136 default:
1137 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1138 case WINED3DTEXF_NONE:
1139 case WINED3DTEXF_POINT:
1140 gl_filter = GL_NEAREST;
1141 break;
1144 /* Resolve the source surface first if needed. */
1145 if (src_location == SFLAG_INRB_MULTISAMPLE
1146 && (src_surface->resource.format->id != dst_surface->resource.format->id
1147 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1148 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1149 src_location = SFLAG_INRB_RESOLVED;
1151 /* Make sure the locations are up-to-date. Loading the destination
1152 * surface isn't required if the entire surface is overwritten. (And is
1153 * in fact harmful if we're being called by surface_load_location() with
1154 * the purpose of loading the destination surface.) */
1155 surface_load_location(src_surface, src_location, NULL);
1156 if (!surface_is_full_rect(dst_surface, &dst_rect))
1157 surface_load_location(dst_surface, dst_location, NULL);
1159 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1160 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1161 else context = context_acquire(device, NULL);
1163 if (!context->valid)
1165 context_release(context);
1166 WARN("Invalid context, skipping blit.\n");
1167 return;
1170 gl_info = context->gl_info;
1172 if (src_location == SFLAG_INDRAWABLE)
1174 TRACE("Source surface %p is onscreen.\n", src_surface);
1175 buffer = surface_get_gl_buffer(src_surface);
1176 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1178 else
1180 TRACE("Source surface %p is offscreen.\n", src_surface);
1181 buffer = GL_COLOR_ATTACHMENT0;
1184 ENTER_GL();
1185 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1186 glReadBuffer(buffer);
1187 checkGLcall("glReadBuffer()");
1188 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1189 LEAVE_GL();
1191 if (dst_location == SFLAG_INDRAWABLE)
1193 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1194 buffer = surface_get_gl_buffer(dst_surface);
1195 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1197 else
1199 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1200 buffer = GL_COLOR_ATTACHMENT0;
1203 ENTER_GL();
1204 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1205 context_set_draw_buffer(context, buffer);
1206 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1207 context_invalidate_state(context, STATE_FRAMEBUFFER);
1209 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1210 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
1211 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
1212 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
1213 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
1215 glDisable(GL_SCISSOR_TEST);
1216 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1218 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1219 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1220 checkGLcall("glBlitFramebuffer()");
1222 LEAVE_GL();
1224 if (wined3d_settings.strict_draw_ordering
1225 || (dst_location == SFLAG_INDRAWABLE
1226 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1227 wglFlush();
1229 context_release(context);
1232 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1233 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
1234 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
1236 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1237 return FALSE;
1239 /* Source and/or destination need to be on the GL side */
1240 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
1241 return FALSE;
1243 switch (blit_op)
1245 case WINED3D_BLIT_OP_COLOR_BLIT:
1246 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1247 return FALSE;
1248 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1249 return FALSE;
1250 break;
1252 case WINED3D_BLIT_OP_DEPTH_BLIT:
1253 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1254 return FALSE;
1255 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1256 return FALSE;
1257 break;
1259 default:
1260 return FALSE;
1263 if (!(src_format->id == dst_format->id
1264 || (is_identity_fixup(src_format->color_fixup)
1265 && is_identity_fixup(dst_format->color_fixup))))
1266 return FALSE;
1268 return TRUE;
1271 /* This function checks if the primary render target uses the 8bit paletted format. */
1272 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1274 if (device->fb.render_targets && device->fb.render_targets[0])
1276 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1277 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1278 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1279 return TRUE;
1281 return FALSE;
1284 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1285 DWORD color, struct wined3d_color *float_color)
1287 const struct wined3d_format *format = surface->resource.format;
1288 const struct wined3d_device *device = surface->resource.device;
1290 switch (format->id)
1292 case WINED3DFMT_P8_UINT:
1293 if (surface->palette)
1295 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1296 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1297 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1299 else
1301 float_color->r = 0.0f;
1302 float_color->g = 0.0f;
1303 float_color->b = 0.0f;
1305 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1306 break;
1308 case WINED3DFMT_B5G6R5_UNORM:
1309 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1310 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1311 float_color->b = (color & 0x1f) / 31.0f;
1312 float_color->a = 1.0f;
1313 break;
1315 case WINED3DFMT_B8G8R8_UNORM:
1316 case WINED3DFMT_B8G8R8X8_UNORM:
1317 float_color->r = D3DCOLOR_R(color);
1318 float_color->g = D3DCOLOR_G(color);
1319 float_color->b = D3DCOLOR_B(color);
1320 float_color->a = 1.0f;
1321 break;
1323 case WINED3DFMT_B8G8R8A8_UNORM:
1324 float_color->r = D3DCOLOR_R(color);
1325 float_color->g = D3DCOLOR_G(color);
1326 float_color->b = D3DCOLOR_B(color);
1327 float_color->a = D3DCOLOR_A(color);
1328 break;
1330 default:
1331 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1332 return FALSE;
1335 return TRUE;
1338 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1340 const struct wined3d_format *format = surface->resource.format;
1342 switch (format->id)
1344 case WINED3DFMT_S1_UINT_D15_UNORM:
1345 *float_depth = depth / (float)0x00007fff;
1346 break;
1348 case WINED3DFMT_D16_UNORM:
1349 *float_depth = depth / (float)0x0000ffff;
1350 break;
1352 case WINED3DFMT_D24_UNORM_S8_UINT:
1353 case WINED3DFMT_X8D24_UNORM:
1354 *float_depth = depth / (float)0x00ffffff;
1355 break;
1357 case WINED3DFMT_D32_UNORM:
1358 *float_depth = depth / (float)0xffffffff;
1359 break;
1361 default:
1362 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1363 return FALSE;
1366 return TRUE;
1369 /* Do not call while under the GL lock. */
1370 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1372 const struct wined3d_resource *resource = &surface->resource;
1373 struct wined3d_device *device = resource->device;
1374 const struct blit_shader *blitter;
1376 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1377 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1378 if (!blitter)
1380 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1381 return WINED3DERR_INVALIDCALL;
1384 return blitter->depth_fill(device, surface, rect, depth);
1387 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1388 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1390 struct wined3d_device *device = src_surface->resource.device;
1392 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1393 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1394 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1395 return WINED3DERR_INVALIDCALL;
1397 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1399 surface_modify_ds_location(dst_surface, SFLAG_INTEXTURE,
1400 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1402 return WINED3D_OK;
1405 /* Do not call while under the GL lock. */
1406 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1407 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1408 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1410 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1411 struct wined3d_device *device = dst_surface->resource.device;
1412 DWORD src_ds_flags, dst_ds_flags;
1413 RECT src_rect, dst_rect;
1414 BOOL scale, convert;
1416 static const DWORD simple_blit = WINEDDBLT_ASYNC
1417 | WINEDDBLT_COLORFILL
1418 | WINEDDBLT_WAIT
1419 | WINEDDBLT_DEPTHFILL
1420 | WINEDDBLT_DONOTWAIT;
1422 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1423 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1424 flags, fx, debug_d3dtexturefiltertype(filter));
1425 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1427 if (fx)
1429 TRACE("dwSize %#x.\n", fx->dwSize);
1430 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
1431 TRACE("dwROP %#x.\n", fx->dwROP);
1432 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
1433 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
1434 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
1435 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
1436 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
1437 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
1438 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
1439 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
1440 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
1441 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
1442 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
1443 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
1444 TRACE("dwReserved %#x.\n", fx->dwReserved);
1445 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
1446 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
1447 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
1448 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
1449 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
1450 TRACE("ddckDestColorkey {%#x, %#x}.\n",
1451 fx->ddckDestColorkey.color_space_low_value,
1452 fx->ddckDestColorkey.color_space_high_value);
1453 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
1454 fx->ddckSrcColorkey.color_space_low_value,
1455 fx->ddckSrcColorkey.color_space_high_value);
1458 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1460 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1461 return WINEDDERR_SURFACEBUSY;
1464 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1466 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1467 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1468 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1469 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1470 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1472 WARN("The application gave us a bad destination rectangle.\n");
1473 return WINEDDERR_INVALIDRECT;
1476 if (src_surface)
1478 surface_get_rect(src_surface, src_rect_in, &src_rect);
1480 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1481 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1482 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1483 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1484 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1486 WARN("Application gave us bad source rectangle for Blt.\n");
1487 return WINEDDERR_INVALIDRECT;
1490 else
1492 memset(&src_rect, 0, sizeof(src_rect));
1495 if (!fx || !(fx->dwDDFX))
1496 flags &= ~WINEDDBLT_DDFX;
1498 if (flags & WINEDDBLT_WAIT)
1499 flags &= ~WINEDDBLT_WAIT;
1501 if (flags & WINEDDBLT_ASYNC)
1503 static unsigned int once;
1505 if (!once++)
1506 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1507 flags &= ~WINEDDBLT_ASYNC;
1510 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1511 if (flags & WINEDDBLT_DONOTWAIT)
1513 static unsigned int once;
1515 if (!once++)
1516 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1517 flags &= ~WINEDDBLT_DONOTWAIT;
1520 if (!device->d3d_initialized)
1522 WARN("D3D not initialized, using fallback.\n");
1523 goto cpu;
1526 /* We want to avoid invalidating the sysmem location for converted
1527 * surfaces, since otherwise we'd have to convert the data back when
1528 * locking them. */
1529 if (dst_surface->flags & SFLAG_CONVERTED)
1531 WARN("Converted surface, using CPU blit.\n");
1532 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1535 if (flags & ~simple_blit)
1537 WARN("Using fallback for complex blit (%#x).\n", flags);
1538 goto fallback;
1541 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1542 src_swapchain = src_surface->container.u.swapchain;
1543 else
1544 src_swapchain = NULL;
1546 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1547 dst_swapchain = dst_surface->container.u.swapchain;
1548 else
1549 dst_swapchain = NULL;
1551 /* This isn't strictly needed. FBO blits for example could deal with
1552 * cross-swapchain blits by first downloading the source to a texture
1553 * before switching to the destination context. We just have this here to
1554 * not have to deal with the issue, since cross-swapchain blits should be
1555 * rare. */
1556 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1558 FIXME("Using fallback for cross-swapchain blit.\n");
1559 goto fallback;
1562 scale = src_surface
1563 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
1564 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
1565 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
1567 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1568 if (src_surface)
1569 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1570 else
1571 src_ds_flags = 0;
1573 if (src_ds_flags || dst_ds_flags)
1575 if (flags & WINEDDBLT_DEPTHFILL)
1577 float depth;
1579 TRACE("Depth fill.\n");
1581 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1582 return WINED3DERR_INVALIDCALL;
1584 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1585 return WINED3D_OK;
1587 else
1589 /* Accessing depth / stencil surfaces is supposed to fail while in
1590 * a scene, except for fills, which seem to work. */
1591 if (device->inScene)
1593 WARN("Rejecting depth / stencil access while in scene.\n");
1594 return WINED3DERR_INVALIDCALL;
1597 if (src_ds_flags != dst_ds_flags)
1599 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1600 return WINED3DERR_INVALIDCALL;
1603 if (src_rect.top || src_rect.left
1604 || src_rect.bottom != src_surface->resource.height
1605 || src_rect.right != src_surface->resource.width)
1607 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1608 wine_dbgstr_rect(&src_rect));
1609 return WINED3DERR_INVALIDCALL;
1612 if (dst_rect.top || dst_rect.left
1613 || dst_rect.bottom != dst_surface->resource.height
1614 || dst_rect.right != dst_surface->resource.width)
1616 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1617 wine_dbgstr_rect(&src_rect));
1618 return WINED3DERR_INVALIDCALL;
1621 if (scale)
1623 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1624 return WINED3DERR_INVALIDCALL;
1627 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1628 return WINED3D_OK;
1631 else
1633 /* In principle this would apply to depth blits as well, but we don't
1634 * implement those in the CPU blitter at the moment. */
1635 if ((dst_surface->flags & SFLAG_INSYSMEM)
1636 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
1638 if (scale)
1639 TRACE("Not doing sysmem blit because of scaling.\n");
1640 else if (convert)
1641 TRACE("Not doing sysmem blit because of format conversion.\n");
1642 else
1643 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1646 if (flags & WINEDDBLT_COLORFILL)
1648 struct wined3d_color color;
1650 TRACE("Color fill.\n");
1652 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1653 goto fallback;
1655 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1656 return WINED3D_OK;
1658 else
1660 TRACE("Color blit.\n");
1662 /* Upload */
1663 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
1665 if (scale)
1666 TRACE("Not doing upload because of scaling.\n");
1667 else if (convert)
1668 TRACE("Not doing upload because of format conversion.\n");
1669 else
1671 POINT dst_point = {dst_rect.left, dst_rect.top};
1673 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
1675 if (!surface_is_offscreen(dst_surface))
1676 surface_load_location(dst_surface, dst_surface->draw_binding, NULL);
1677 return WINED3D_OK;
1682 /* Use present for back -> front blits. The idea behind this is
1683 * that present is potentially faster than a blit, in particular
1684 * when FBO blits aren't available. Some ddraw applications like
1685 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
1686 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
1687 * applications can't blit directly to the frontbuffer. */
1688 if (dst_swapchain && dst_swapchain->back_buffers
1689 && dst_surface == dst_swapchain->front_buffer
1690 && src_surface == dst_swapchain->back_buffers[0])
1692 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
1694 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1696 /* Set the swap effect to COPY, we don't want the backbuffer
1697 * to become undefined. */
1698 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
1699 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
1700 dst_swapchain->desc.swap_effect = swap_effect;
1702 return WINED3D_OK;
1705 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1706 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1707 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1709 TRACE("Using FBO blit.\n");
1711 surface_blt_fbo(device, filter,
1712 src_surface, src_surface->draw_binding, &src_rect,
1713 dst_surface, dst_surface->draw_binding, &dst_rect);
1714 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1715 return WINED3D_OK;
1718 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1719 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1720 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1722 TRACE("Using arbfp blit.\n");
1724 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1725 return WINED3D_OK;
1730 fallback:
1732 /* Special cases for render targets. */
1733 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1734 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1736 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1737 src_surface, &src_rect, flags, fx, filter)))
1738 return WINED3D_OK;
1741 cpu:
1743 /* For the rest call the X11 surface implementation. For render targets
1744 * this should be implemented OpenGL accelerated in BltOverride, other
1745 * blits are rather rare. */
1746 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1749 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1750 struct wined3d_surface *render_target)
1752 TRACE("surface %p, render_target %p.\n", surface, render_target);
1754 /* TODO: Check surface sizes, pools, etc. */
1756 if (render_target->resource.multisample_type)
1757 return WINED3DERR_INVALIDCALL;
1759 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3DTEXF_POINT);
1762 /* Context activation is done by the caller. */
1763 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1765 if (surface->flags & SFLAG_DIBSECTION)
1767 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1769 else
1771 if (!surface->resource.heapMemory)
1772 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1773 else if (!(surface->flags & SFLAG_CLIENT))
1774 ERR("Surface %p has heapMemory %p and flags %#x.\n",
1775 surface, surface->resource.heapMemory, surface->flags);
1777 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1778 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1781 ENTER_GL();
1782 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1783 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1784 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1785 surface->resource.size, surface->resource.allocatedMemory));
1786 checkGLcall("glGetBufferSubDataARB");
1787 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1788 checkGLcall("glDeleteBuffersARB");
1789 LEAVE_GL();
1791 surface->pbo = 0;
1792 surface->flags &= ~SFLAG_PBO;
1795 /* Do not call while under the GL lock. */
1796 static void surface_unload(struct wined3d_resource *resource)
1798 struct wined3d_surface *surface = surface_from_resource(resource);
1799 struct wined3d_renderbuffer_entry *entry, *entry2;
1800 struct wined3d_device *device = resource->device;
1801 const struct wined3d_gl_info *gl_info;
1802 struct wined3d_context *context;
1804 TRACE("surface %p.\n", surface);
1806 if (resource->pool == WINED3DPOOL_DEFAULT)
1808 /* Default pool resources are supposed to be destroyed before Reset is called.
1809 * Implicit resources stay however. So this means we have an implicit render target
1810 * or depth stencil. The content may be destroyed, but we still have to tear down
1811 * opengl resources, so we cannot leave early.
1813 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1814 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1815 * or the depth stencil into an FBO the texture or render buffer will be removed
1816 * and all flags get lost
1818 if (!(surface->flags & SFLAG_PBO))
1819 surface_init_sysmem(surface);
1820 /* We also get here when the ddraw swapchain is destroyed, for example
1821 * for a mode switch. In this case this surface won't necessarily be
1822 * an implicit surface. We have to mark it lost so that the
1823 * application can restore it after the mode switch. */
1824 surface->flags |= SFLAG_LOST;
1826 else
1828 /* Load the surface into system memory */
1829 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1830 surface_modify_location(surface, surface->draw_binding, FALSE);
1832 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1833 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1834 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1836 context = context_acquire(device, NULL);
1837 gl_info = context->gl_info;
1839 /* Destroy PBOs, but load them into real sysmem before */
1840 if (surface->flags & SFLAG_PBO)
1841 surface_remove_pbo(surface, gl_info);
1843 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1844 * all application-created targets the application has to release the surface
1845 * before calling _Reset
1847 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1849 ENTER_GL();
1850 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1851 LEAVE_GL();
1852 list_remove(&entry->entry);
1853 HeapFree(GetProcessHeap(), 0, entry);
1855 list_init(&surface->renderbuffers);
1856 surface->current_renderbuffer = NULL;
1858 ENTER_GL();
1860 /* If we're in a texture, the texture name belongs to the texture.
1861 * Otherwise, destroy it. */
1862 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1864 glDeleteTextures(1, &surface->texture_name);
1865 surface->texture_name = 0;
1866 glDeleteTextures(1, &surface->texture_name_srgb);
1867 surface->texture_name_srgb = 0;
1869 if (surface->rb_multisample)
1871 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1872 surface->rb_multisample = 0;
1874 if (surface->rb_resolved)
1876 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1877 surface->rb_resolved = 0;
1880 LEAVE_GL();
1882 context_release(context);
1884 resource_unload(resource);
1887 static const struct wined3d_resource_ops surface_resource_ops =
1889 surface_unload,
1892 static const struct wined3d_surface_ops surface_ops =
1894 surface_private_setup,
1895 surface_realize_palette,
1896 surface_map,
1897 surface_unmap,
1900 /*****************************************************************************
1901 * Initializes the GDI surface, aka creates the DIB section we render to
1902 * The DIB section creation is done by calling GetDC, which will create the
1903 * section and releasing the dc to allow the app to use it. The dib section
1904 * will stay until the surface is released
1906 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1907 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1908 * avoid confusion in the shared surface code.
1910 * Returns:
1911 * WINED3D_OK on success
1912 * The return values of called methods on failure
1914 *****************************************************************************/
1915 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1917 HRESULT hr;
1919 TRACE("surface %p.\n", surface);
1921 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1923 ERR("Overlays not yet supported by GDI surfaces.\n");
1924 return WINED3DERR_INVALIDCALL;
1927 /* Sysmem textures have memory already allocated - release it,
1928 * this avoids an unnecessary memcpy. */
1929 hr = surface_create_dib_section(surface);
1930 if (SUCCEEDED(hr))
1932 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1933 surface->resource.heapMemory = NULL;
1934 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1937 /* We don't mind the nonpow2 stuff in GDI. */
1938 surface->pow2Width = surface->resource.width;
1939 surface->pow2Height = surface->resource.height;
1941 return WINED3D_OK;
1944 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1946 struct wined3d_palette *palette = surface->palette;
1948 TRACE("surface %p.\n", surface);
1950 if (!palette) return;
1952 if (surface->flags & SFLAG_DIBSECTION)
1954 RGBQUAD col[256];
1955 unsigned int i;
1957 TRACE("Updating the DC's palette.\n");
1959 for (i = 0; i < 256; ++i)
1961 col[i].rgbRed = palette->palents[i].peRed;
1962 col[i].rgbGreen = palette->palents[i].peGreen;
1963 col[i].rgbBlue = palette->palents[i].peBlue;
1964 col[i].rgbReserved = 0;
1966 SetDIBColorTable(surface->hDC, 0, 256, col);
1969 /* Update the image because of the palette change. Some games like e.g.
1970 * Red Alert call SetEntries a lot to implement fading. */
1971 /* Tell the swapchain to update the screen. */
1972 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1974 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1975 if (surface == swapchain->front_buffer)
1977 x11_copy_to_screen(swapchain, NULL);
1982 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
1984 TRACE("surface %p, rect %s, flags %#x.\n",
1985 surface, wine_dbgstr_rect(rect), flags);
1987 if (!(surface->flags & SFLAG_DIBSECTION))
1989 /* This happens on gdi surfaces if the application set a user pointer
1990 * and resets it. Recreate the DIB section. */
1991 surface_create_dib_section(surface);
1992 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1996 static void gdi_surface_unmap(struct wined3d_surface *surface)
1998 TRACE("surface %p.\n", surface);
2000 /* Tell the swapchain to update the screen. */
2001 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2003 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2004 if (surface == swapchain->front_buffer)
2006 x11_copy_to_screen(swapchain, &surface->lockedRect);
2010 memset(&surface->lockedRect, 0, sizeof(RECT));
2013 static const struct wined3d_surface_ops gdi_surface_ops =
2015 gdi_surface_private_setup,
2016 gdi_surface_realize_palette,
2017 gdi_surface_map,
2018 gdi_surface_unmap,
2021 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2023 GLuint *name;
2024 DWORD flag;
2026 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2028 if(srgb)
2030 name = &surface->texture_name_srgb;
2031 flag = SFLAG_INSRGBTEX;
2033 else
2035 name = &surface->texture_name;
2036 flag = SFLAG_INTEXTURE;
2039 if (!*name && new_name)
2041 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2042 * surface has no texture name yet. See if we can get rid of this. */
2043 if (surface->flags & flag)
2045 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2046 surface_modify_location(surface, flag, FALSE);
2050 *name = new_name;
2051 surface_force_reload(surface);
2054 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2056 TRACE("surface %p, target %#x.\n", surface, target);
2058 if (surface->texture_target != target)
2060 if (target == GL_TEXTURE_RECTANGLE_ARB)
2062 surface->flags &= ~SFLAG_NORMCOORD;
2064 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2066 surface->flags |= SFLAG_NORMCOORD;
2069 surface->texture_target = target;
2070 surface_force_reload(surface);
2073 /* Context activation is done by the caller. */
2074 void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
2076 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
2078 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2080 struct wined3d_texture *texture = surface->container.u.texture;
2082 TRACE("Passing to container (%p).\n", texture);
2083 texture->texture_ops->texture_bind(texture, context, srgb);
2085 else
2087 if (surface->texture_level)
2089 ERR("Standalone surface %p is non-zero texture level %u.\n",
2090 surface, surface->texture_level);
2093 if (srgb)
2094 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2096 ENTER_GL();
2098 if (!surface->texture_name)
2100 glGenTextures(1, &surface->texture_name);
2101 checkGLcall("glGenTextures");
2103 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2105 context_bind_texture(context, surface->texture_target, surface->texture_name);
2106 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2107 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2108 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2109 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2110 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2111 checkGLcall("glTexParameteri");
2113 else
2115 context_bind_texture(context, surface->texture_target, surface->texture_name);
2118 LEAVE_GL();
2122 /* This call just downloads data, the caller is responsible for binding the
2123 * correct texture. */
2124 /* Context activation is done by the caller. */
2125 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2127 const struct wined3d_format *format = surface->resource.format;
2129 /* Only support read back of converted P8 surfaces. */
2130 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2132 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
2133 return;
2136 ENTER_GL();
2138 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2140 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2141 surface, surface->texture_level, format->glFormat, format->glType,
2142 surface->resource.allocatedMemory);
2144 if (surface->flags & SFLAG_PBO)
2146 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2147 checkGLcall("glBindBufferARB");
2148 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2149 checkGLcall("glGetCompressedTexImageARB");
2150 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2151 checkGLcall("glBindBufferARB");
2153 else
2155 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2156 surface->texture_level, surface->resource.allocatedMemory));
2157 checkGLcall("glGetCompressedTexImageARB");
2160 LEAVE_GL();
2162 else
2164 void *mem;
2165 GLenum gl_format = format->glFormat;
2166 GLenum gl_type = format->glType;
2167 int src_pitch = 0;
2168 int dst_pitch = 0;
2170 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2171 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2173 gl_format = GL_ALPHA;
2174 gl_type = GL_UNSIGNED_BYTE;
2177 if (surface->flags & SFLAG_NONPOW2)
2179 unsigned char alignment = surface->resource.device->surface_alignment;
2180 src_pitch = format->byte_count * surface->pow2Width;
2181 dst_pitch = wined3d_surface_get_pitch(surface);
2182 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2183 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2185 else
2187 mem = surface->resource.allocatedMemory;
2190 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2191 surface, surface->texture_level, gl_format, gl_type, mem);
2193 if (surface->flags & SFLAG_PBO)
2195 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2196 checkGLcall("glBindBufferARB");
2198 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2199 checkGLcall("glGetTexImage");
2201 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2202 checkGLcall("glBindBufferARB");
2204 else
2206 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2207 checkGLcall("glGetTexImage");
2209 LEAVE_GL();
2211 if (surface->flags & SFLAG_NONPOW2)
2213 const BYTE *src_data;
2214 BYTE *dst_data;
2215 UINT y;
2217 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2218 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2219 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2221 * We're doing this...
2223 * instead of boxing the texture :
2224 * |<-texture width ->| -->pow2width| /\
2225 * |111111111111111111| | |
2226 * |222 Texture 222222| boxed empty | texture height
2227 * |3333 Data 33333333| | |
2228 * |444444444444444444| | \/
2229 * ----------------------------------- |
2230 * | boxed empty | boxed empty | pow2height
2231 * | | | \/
2232 * -----------------------------------
2235 * we're repacking the data to the expected texture width
2237 * |<-texture width ->| -->pow2width| /\
2238 * |111111111111111111222222222222222| |
2239 * |222333333333333333333444444444444| texture height
2240 * |444444 | |
2241 * | | \/
2242 * | | |
2243 * | empty | pow2height
2244 * | | \/
2245 * -----------------------------------
2247 * == is the same as
2249 * |<-texture width ->| /\
2250 * |111111111111111111|
2251 * |222222222222222222|texture height
2252 * |333333333333333333|
2253 * |444444444444444444| \/
2254 * --------------------
2256 * this also means that any references to allocatedMemory should work with the data as if were a
2257 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2259 * internally the texture is still stored in a boxed format so any references to textureName will
2260 * get a boxed texture with width pow2width and not a texture of width resource.width.
2262 * Performance should not be an issue, because applications normally do not lock the surfaces when
2263 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2264 * and doesn't have to be re-read. */
2265 src_data = mem;
2266 dst_data = surface->resource.allocatedMemory;
2267 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2268 for (y = 1; y < surface->resource.height; ++y)
2270 /* skip the first row */
2271 src_data += src_pitch;
2272 dst_data += dst_pitch;
2273 memcpy(dst_data, src_data, dst_pitch);
2276 HeapFree(GetProcessHeap(), 0, mem);
2280 /* Surface has now been downloaded */
2281 surface->flags |= SFLAG_INSYSMEM;
2284 /* This call just uploads data, the caller is responsible for binding the
2285 * correct texture. */
2286 /* Context activation is done by the caller. */
2287 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2288 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
2289 BOOL srgb, const struct wined3d_bo_address *data)
2291 UINT update_w = src_rect->right - src_rect->left;
2292 UINT update_h = src_rect->bottom - src_rect->top;
2294 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
2295 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
2296 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2298 if (surface->flags & SFLAG_LOCKED)
2300 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
2301 surface->flags |= SFLAG_PIN_SYSMEM;
2304 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2305 update_h *= format->heightscale;
2307 ENTER_GL();
2309 if (data->buffer_object)
2311 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2312 checkGLcall("glBindBufferARB");
2315 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2317 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2318 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2319 const BYTE *addr = data->addr;
2320 GLenum internal;
2322 addr += (src_rect->top / format->block_height) * src_pitch;
2323 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2325 if (srgb)
2326 internal = format->glGammaInternal;
2327 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2328 internal = format->rtInternal;
2329 else
2330 internal = format->glInternal;
2332 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2333 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2334 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2336 if (row_length == src_pitch)
2338 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2339 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2341 else
2343 UINT row, y;
2345 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2346 * can't use the unpack row length like below. */
2347 for (row = 0, y = dst_point->y; row < row_count; ++row)
2349 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2350 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2351 y += format->block_height;
2352 addr += src_pitch;
2355 checkGLcall("glCompressedTexSubImage2DARB");
2357 else
2359 const BYTE *addr = data->addr;
2361 addr += src_rect->top * src_pitch;
2362 addr += src_rect->left * format->byte_count;
2364 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2365 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2366 update_w, update_h, format->glFormat, format->glType, addr);
2368 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
2369 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2370 update_w, update_h, format->glFormat, format->glType, addr);
2371 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2372 checkGLcall("glTexSubImage2D");
2375 if (data->buffer_object)
2377 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2378 checkGLcall("glBindBufferARB");
2381 LEAVE_GL();
2383 if (wined3d_settings.strict_draw_ordering)
2384 wglFlush();
2386 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2388 struct wined3d_device *device = surface->resource.device;
2389 unsigned int i;
2391 for (i = 0; i < device->context_count; ++i)
2393 context_surface_update(device->contexts[i], surface);
2398 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
2399 struct wined3d_surface *src_surface, const RECT *src_rect)
2401 const struct wined3d_format *src_format;
2402 const struct wined3d_format *dst_format;
2403 const struct wined3d_gl_info *gl_info;
2404 struct wined3d_context *context;
2405 struct wined3d_bo_address data;
2406 struct wined3d_format format;
2407 UINT update_w, update_h;
2408 CONVERT_TYPES convert;
2409 UINT dst_w, dst_h;
2410 UINT src_w, src_h;
2411 UINT src_pitch;
2412 POINT p;
2413 RECT r;
2415 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
2416 dst_surface, wine_dbgstr_point(dst_point),
2417 src_surface, wine_dbgstr_rect(src_rect));
2419 src_format = src_surface->resource.format;
2420 dst_format = dst_surface->resource.format;
2422 if (src_format->id != dst_format->id)
2424 WARN("Source and destination surfaces should have the same format.\n");
2425 return WINED3DERR_INVALIDCALL;
2428 if (!dst_point)
2430 p.x = 0;
2431 p.y = 0;
2432 dst_point = &p;
2434 else if (dst_point->x < 0 || dst_point->y < 0)
2436 WARN("Invalid destination point.\n");
2437 return WINED3DERR_INVALIDCALL;
2440 if (!src_rect)
2442 r.left = 0;
2443 r.top = 0;
2444 r.right = src_surface->resource.width;
2445 r.bottom = src_surface->resource.height;
2446 src_rect = &r;
2448 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
2449 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
2451 WARN("Invalid source rectangle.\n");
2452 return WINED3DERR_INVALIDCALL;
2455 src_w = src_surface->resource.width;
2456 src_h = src_surface->resource.height;
2458 dst_w = dst_surface->resource.width;
2459 dst_h = dst_surface->resource.height;
2461 update_w = src_rect->right - src_rect->left;
2462 update_h = src_rect->bottom - src_rect->top;
2464 if (update_w > dst_w || dst_point->x > dst_w - update_w
2465 || update_h > dst_h || dst_point->y > dst_h - update_h)
2467 WARN("Destination out of bounds.\n");
2468 return WINED3DERR_INVALIDCALL;
2471 /* NPOT block sizes would be silly. */
2472 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS)
2473 && ((update_w & (src_format->block_width - 1) || update_h & (src_format->block_height - 1))
2474 && (src_w != update_w || dst_w != update_w || src_h != update_h || dst_h != update_h)))
2476 WARN("Update rect not block-aligned.\n");
2477 return WINED3DERR_INVALIDCALL;
2480 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
2481 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
2482 if (convert != NO_CONVERSION || format.convert)
2484 RECT dst_rect = {dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h};
2485 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3DTEXF_POINT);
2488 context = context_acquire(dst_surface->resource.device, NULL);
2489 gl_info = context->gl_info;
2491 /* Only load the surface for partial updates. For newly allocated texture
2492 * the texture wouldn't be the current location, and we'd upload zeroes
2493 * just to overwrite them again. */
2494 if (update_w == dst_w && update_h == dst_h)
2495 surface_prepare_texture(dst_surface, context, FALSE);
2496 else
2497 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
2498 surface_bind(dst_surface, context, FALSE);
2500 data.buffer_object = src_surface->pbo;
2501 data.addr = src_surface->resource.allocatedMemory;
2502 src_pitch = wined3d_surface_get_pitch(src_surface);
2504 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
2506 invalidate_active_texture(dst_surface->resource.device, context);
2508 context_release(context);
2510 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
2511 return WINED3D_OK;
2514 /* This call just allocates the texture, the caller is responsible for binding
2515 * the correct texture. */
2516 /* Context activation is done by the caller. */
2517 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2518 const struct wined3d_format *format, BOOL srgb)
2520 BOOL enable_client_storage = FALSE;
2521 GLsizei width = surface->pow2Width;
2522 GLsizei height = surface->pow2Height;
2523 const BYTE *mem = NULL;
2524 GLenum internal;
2526 if (srgb)
2528 internal = format->glGammaInternal;
2530 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2532 internal = format->rtInternal;
2534 else
2536 internal = format->glInternal;
2539 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2541 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",
2542 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2543 internal, width, height, format->glFormat, format->glType);
2545 ENTER_GL();
2547 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2549 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2550 || !surface->resource.allocatedMemory)
2552 /* In some cases we want to disable client storage.
2553 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2554 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2555 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2556 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2558 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2559 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2560 surface->flags &= ~SFLAG_CLIENT;
2561 enable_client_storage = TRUE;
2563 else
2565 surface->flags |= SFLAG_CLIENT;
2567 /* Point OpenGL to our allocated texture memory. Do not use
2568 * resource.allocatedMemory here because it might point into a
2569 * PBO. Instead use heapMemory, but get the alignment right. */
2570 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2571 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2575 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2577 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2578 internal, width, height, 0, surface->resource.size, mem));
2579 checkGLcall("glCompressedTexImage2DARB");
2581 else
2583 glTexImage2D(surface->texture_target, surface->texture_level,
2584 internal, width, height, 0, format->glFormat, format->glType, mem);
2585 checkGLcall("glTexImage2D");
2588 if(enable_client_storage) {
2589 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2590 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2592 LEAVE_GL();
2595 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2596 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2597 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2598 /* GL locking is done by the caller */
2599 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2601 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2602 struct wined3d_renderbuffer_entry *entry;
2603 GLuint renderbuffer = 0;
2604 unsigned int src_width, src_height;
2605 unsigned int width, height;
2607 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2609 width = rt->pow2Width;
2610 height = rt->pow2Height;
2612 else
2614 width = surface->pow2Width;
2615 height = surface->pow2Height;
2618 src_width = surface->pow2Width;
2619 src_height = surface->pow2Height;
2621 /* A depth stencil smaller than the render target is not valid */
2622 if (width > src_width || height > src_height) return;
2624 /* Remove any renderbuffer set if the sizes match */
2625 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2626 || (width == src_width && height == src_height))
2628 surface->current_renderbuffer = NULL;
2629 return;
2632 /* Look if we've already got a renderbuffer of the correct dimensions */
2633 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2635 if (entry->width == width && entry->height == height)
2637 renderbuffer = entry->id;
2638 surface->current_renderbuffer = entry;
2639 break;
2643 if (!renderbuffer)
2645 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2646 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2647 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2648 surface->resource.format->glInternal, width, height);
2650 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2651 entry->width = width;
2652 entry->height = height;
2653 entry->id = renderbuffer;
2654 list_add_head(&surface->renderbuffers, &entry->entry);
2656 surface->current_renderbuffer = entry;
2659 checkGLcall("set_compatible_renderbuffer");
2662 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2664 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2666 TRACE("surface %p.\n", surface);
2668 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2670 ERR("Surface %p is not on a swapchain.\n", surface);
2671 return GL_NONE;
2674 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2676 if (swapchain->render_to_fbo)
2678 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2679 return GL_COLOR_ATTACHMENT0;
2681 TRACE("Returning GL_BACK\n");
2682 return GL_BACK;
2684 else if (surface == swapchain->front_buffer)
2686 TRACE("Returning GL_FRONT\n");
2687 return GL_FRONT;
2690 FIXME("Higher back buffer, returning GL_BACK\n");
2691 return GL_BACK;
2694 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2695 void surface_add_dirty_rect(struct wined3d_surface *surface, const struct wined3d_box *dirty_rect)
2697 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2699 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2700 /* No partial locking for textures yet. */
2701 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2703 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2704 if (dirty_rect)
2706 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->left);
2707 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->top);
2708 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->right);
2709 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->bottom);
2711 else
2713 surface->dirtyRect.left = 0;
2714 surface->dirtyRect.top = 0;
2715 surface->dirtyRect.right = surface->resource.width;
2716 surface->dirtyRect.bottom = surface->resource.height;
2719 /* if the container is a texture then mark it dirty. */
2720 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2722 TRACE("Passing to container.\n");
2723 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2727 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2729 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2730 BOOL ck_changed;
2732 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2734 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2736 ERR("Not supported on scratch surfaces.\n");
2737 return WINED3DERR_INVALIDCALL;
2740 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2742 /* Reload if either the texture and sysmem have different ideas about the
2743 * color key, or the actual key values changed. */
2744 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2745 && (surface->gl_color_key.color_space_low_value != surface->src_blt_color_key.color_space_low_value
2746 || surface->gl_color_key.color_space_high_value != surface->src_blt_color_key.color_space_high_value)))
2748 TRACE("Reloading because of color keying\n");
2749 /* To perform the color key conversion we need a sysmem copy of
2750 * the surface. Make sure we have it. */
2752 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2753 /* Make sure the texture is reloaded because of the color key change,
2754 * this kills performance though :( */
2755 /* TODO: This is not necessarily needed with hw palettized texture support. */
2756 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2757 /* Switching color keying on / off may change the internal format. */
2758 if (ck_changed)
2759 surface_force_reload(surface);
2761 else if (!(surface->flags & flag))
2763 TRACE("Reloading because surface is dirty.\n");
2765 else
2767 TRACE("surface is already in texture\n");
2768 return WINED3D_OK;
2771 /* No partial locking for textures yet. */
2772 surface_load_location(surface, flag, NULL);
2773 surface_evict_sysmem(surface);
2775 return WINED3D_OK;
2778 /* See also float_16_to_32() in wined3d_private.h */
2779 static inline unsigned short float_32_to_16(const float *in)
2781 int exp = 0;
2782 float tmp = fabsf(*in);
2783 unsigned int mantissa;
2784 unsigned short ret;
2786 /* Deal with special numbers */
2787 if (*in == 0.0f)
2788 return 0x0000;
2789 if (isnan(*in))
2790 return 0x7c01;
2791 if (isinf(*in))
2792 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2794 if (tmp < powf(2, 10))
2798 tmp = tmp * 2.0f;
2799 exp--;
2800 } while (tmp < powf(2, 10));
2802 else if (tmp >= powf(2, 11))
2806 tmp /= 2.0f;
2807 exp++;
2808 } while (tmp >= powf(2, 11));
2811 mantissa = (unsigned int)tmp;
2812 if (tmp - mantissa >= 0.5f)
2813 ++mantissa; /* Round to nearest, away from zero. */
2815 exp += 10; /* Normalize the mantissa. */
2816 exp += 15; /* Exponent is encoded with excess 15. */
2818 if (exp > 30) /* too big */
2820 ret = 0x7c00; /* INF */
2822 else if (exp <= 0)
2824 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2825 while (exp <= 0)
2827 mantissa = mantissa >> 1;
2828 ++exp;
2830 ret = mantissa & 0x3ff;
2832 else
2834 ret = (exp << 10) | (mantissa & 0x3ff);
2837 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2838 return ret;
2841 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2843 ULONG refcount;
2845 TRACE("Surface %p, container %p of type %#x.\n",
2846 surface, surface->container.u.base, surface->container.type);
2848 switch (surface->container.type)
2850 case WINED3D_CONTAINER_TEXTURE:
2851 return wined3d_texture_incref(surface->container.u.texture);
2853 case WINED3D_CONTAINER_SWAPCHAIN:
2854 return wined3d_swapchain_incref(surface->container.u.swapchain);
2856 default:
2857 ERR("Unhandled container type %#x.\n", surface->container.type);
2858 case WINED3D_CONTAINER_NONE:
2859 break;
2862 refcount = InterlockedIncrement(&surface->resource.ref);
2863 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2865 return refcount;
2868 /* Do not call while under the GL lock. */
2869 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2871 ULONG refcount;
2873 TRACE("Surface %p, container %p of type %#x.\n",
2874 surface, surface->container.u.base, surface->container.type);
2876 switch (surface->container.type)
2878 case WINED3D_CONTAINER_TEXTURE:
2879 return wined3d_texture_decref(surface->container.u.texture);
2881 case WINED3D_CONTAINER_SWAPCHAIN:
2882 return wined3d_swapchain_decref(surface->container.u.swapchain);
2884 default:
2885 ERR("Unhandled container type %#x.\n", surface->container.type);
2886 case WINED3D_CONTAINER_NONE:
2887 break;
2890 refcount = InterlockedDecrement(&surface->resource.ref);
2891 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2893 if (!refcount)
2895 surface_cleanup(surface);
2896 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2898 TRACE("Destroyed surface %p.\n", surface);
2899 HeapFree(GetProcessHeap(), 0, surface);
2902 return refcount;
2905 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2907 return resource_set_priority(&surface->resource, priority);
2910 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2912 return resource_get_priority(&surface->resource);
2915 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2917 TRACE("surface %p.\n", surface);
2919 if (!surface->resource.device->d3d_initialized)
2921 ERR("D3D not initialized.\n");
2922 return;
2925 surface_internal_preload(surface, SRGB_ANY);
2928 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2930 TRACE("surface %p.\n", surface);
2932 return surface->resource.parent;
2935 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2937 TRACE("surface %p.\n", surface);
2939 return &surface->resource;
2942 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2944 TRACE("surface %p, flags %#x.\n", surface, flags);
2946 switch (flags)
2948 case WINEDDGBS_CANBLT:
2949 case WINEDDGBS_ISBLTDONE:
2950 return WINED3D_OK;
2952 default:
2953 return WINED3DERR_INVALIDCALL;
2957 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2959 TRACE("surface %p, flags %#x.\n", surface, flags);
2961 /* XXX: DDERR_INVALIDSURFACETYPE */
2963 switch (flags)
2965 case WINEDDGFS_CANFLIP:
2966 case WINEDDGFS_ISFLIPDONE:
2967 return WINED3D_OK;
2969 default:
2970 return WINED3DERR_INVALIDCALL;
2974 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2976 TRACE("surface %p.\n", surface);
2978 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2979 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2982 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2984 TRACE("surface %p.\n", surface);
2986 surface->flags &= ~SFLAG_LOST;
2987 return WINED3D_OK;
2990 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2992 TRACE("surface %p, palette %p.\n", surface, palette);
2994 if (surface->palette == palette)
2996 TRACE("Nop palette change.\n");
2997 return WINED3D_OK;
3000 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
3001 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3003 surface->palette = palette;
3005 if (palette)
3007 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3008 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
3010 surface->surface_ops->surface_realize_palette(surface);
3013 return WINED3D_OK;
3016 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
3017 DWORD flags, const struct wined3d_color_key *color_key)
3019 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3021 if (flags & WINEDDCKEY_COLORSPACE)
3023 FIXME(" colorkey value not supported (%08x) !\n", flags);
3024 return WINED3DERR_INVALIDCALL;
3027 /* Dirtify the surface, but only if a key was changed. */
3028 if (color_key)
3030 switch (flags & ~WINEDDCKEY_COLORSPACE)
3032 case WINEDDCKEY_DESTBLT:
3033 surface->dst_blt_color_key = *color_key;
3034 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3035 break;
3037 case WINEDDCKEY_DESTOVERLAY:
3038 surface->dst_overlay_color_key = *color_key;
3039 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3040 break;
3042 case WINEDDCKEY_SRCOVERLAY:
3043 surface->src_overlay_color_key = *color_key;
3044 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3045 break;
3047 case WINEDDCKEY_SRCBLT:
3048 surface->src_blt_color_key = *color_key;
3049 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3050 break;
3053 else
3055 switch (flags & ~WINEDDCKEY_COLORSPACE)
3057 case WINEDDCKEY_DESTBLT:
3058 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3059 break;
3061 case WINEDDCKEY_DESTOVERLAY:
3062 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3063 break;
3065 case WINEDDCKEY_SRCOVERLAY:
3066 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3067 break;
3069 case WINEDDCKEY_SRCBLT:
3070 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3071 break;
3075 return WINED3D_OK;
3078 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3080 TRACE("surface %p.\n", surface);
3082 return surface->palette;
3085 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3087 const struct wined3d_format *format = surface->resource.format;
3088 DWORD pitch;
3090 TRACE("surface %p.\n", surface);
3092 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
3094 /* Since compressed formats are block based, pitch means the amount of
3095 * bytes to the next row of block rather than the next row of pixels. */
3096 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3097 pitch = row_block_count * format->block_byte_count;
3099 else
3101 unsigned char alignment = surface->resource.device->surface_alignment;
3102 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3103 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3106 TRACE("Returning %u.\n", pitch);
3108 return pitch;
3111 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3113 TRACE("surface %p, mem %p.\n", surface, mem);
3115 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3117 WARN("Surface is locked or the DC is in use.\n");
3118 return WINED3DERR_INVALIDCALL;
3121 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
3122 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3124 ERR("Not supported on render targets.\n");
3125 return WINED3DERR_INVALIDCALL;
3128 if (mem && mem != surface->resource.allocatedMemory)
3130 void *release = NULL;
3132 /* Do I have to copy the old surface content? */
3133 if (surface->flags & SFLAG_DIBSECTION)
3135 DeleteDC(surface->hDC);
3136 DeleteObject(surface->dib.DIBsection);
3137 surface->dib.bitmap_data = NULL;
3138 surface->resource.allocatedMemory = NULL;
3139 surface->hDC = NULL;
3140 surface->flags &= ~SFLAG_DIBSECTION;
3142 else if (!(surface->flags & SFLAG_USERPTR))
3144 release = surface->resource.heapMemory;
3145 surface->resource.heapMemory = NULL;
3147 surface->resource.allocatedMemory = mem;
3148 surface->flags |= SFLAG_USERPTR;
3150 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
3151 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3153 /* For client textures OpenGL has to be notified. */
3154 if (surface->flags & SFLAG_CLIENT)
3155 surface_release_client_storage(surface);
3157 /* Now free the old memory if any. */
3158 HeapFree(GetProcessHeap(), 0, release);
3160 else if (surface->flags & SFLAG_USERPTR)
3162 /* HeapMemory should be NULL already. */
3163 if (surface->resource.heapMemory)
3164 ERR("User pointer surface has heap memory allocated.\n");
3166 if (!mem)
3168 surface->resource.allocatedMemory = NULL;
3169 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
3171 if (surface->flags & SFLAG_CLIENT)
3172 surface_release_client_storage(surface);
3174 surface_prepare_system_memory(surface);
3177 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3180 return WINED3D_OK;
3183 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3185 LONG w, h;
3187 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3189 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3191 WARN("Not an overlay surface.\n");
3192 return WINEDDERR_NOTAOVERLAYSURFACE;
3195 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3196 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3197 surface->overlay_destrect.left = x;
3198 surface->overlay_destrect.top = y;
3199 surface->overlay_destrect.right = x + w;
3200 surface->overlay_destrect.bottom = y + h;
3202 surface_draw_overlay(surface);
3204 return WINED3D_OK;
3207 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3209 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3211 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3213 TRACE("Not an overlay surface.\n");
3214 return WINEDDERR_NOTAOVERLAYSURFACE;
3217 if (!surface->overlay_dest)
3219 TRACE("Overlay not visible.\n");
3220 *x = 0;
3221 *y = 0;
3222 return WINEDDERR_OVERLAYNOTVISIBLE;
3225 *x = surface->overlay_destrect.left;
3226 *y = surface->overlay_destrect.top;
3228 TRACE("Returning position %d, %d.\n", *x, *y);
3230 return WINED3D_OK;
3233 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3234 DWORD flags, struct wined3d_surface *ref)
3236 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3238 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3240 TRACE("Not an overlay surface.\n");
3241 return WINEDDERR_NOTAOVERLAYSURFACE;
3244 return WINED3D_OK;
3247 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3248 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3250 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3251 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3253 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3255 WARN("Not an overlay surface.\n");
3256 return WINEDDERR_NOTAOVERLAYSURFACE;
3258 else if (!dst_surface)
3260 WARN("Dest surface is NULL.\n");
3261 return WINED3DERR_INVALIDCALL;
3264 if (src_rect)
3266 surface->overlay_srcrect = *src_rect;
3268 else
3270 surface->overlay_srcrect.left = 0;
3271 surface->overlay_srcrect.top = 0;
3272 surface->overlay_srcrect.right = surface->resource.width;
3273 surface->overlay_srcrect.bottom = surface->resource.height;
3276 if (dst_rect)
3278 surface->overlay_destrect = *dst_rect;
3280 else
3282 surface->overlay_destrect.left = 0;
3283 surface->overlay_destrect.top = 0;
3284 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3285 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3288 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3290 surface->overlay_dest = NULL;
3291 list_remove(&surface->overlay_entry);
3294 if (flags & WINEDDOVER_SHOW)
3296 if (surface->overlay_dest != dst_surface)
3298 surface->overlay_dest = dst_surface;
3299 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3302 else if (flags & WINEDDOVER_HIDE)
3304 /* tests show that the rectangles are erased on hide */
3305 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3306 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3307 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3308 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3309 surface->overlay_dest = NULL;
3312 surface_draw_overlay(surface);
3314 return WINED3D_OK;
3317 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
3318 UINT width, UINT height, enum wined3d_format_id format_id,
3319 enum wined3d_multisample_type multisample_type, UINT multisample_quality)
3321 struct wined3d_device *device = surface->resource.device;
3322 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3323 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
3324 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height);
3326 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u.\n",
3327 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type);
3329 if (!resource_size)
3330 return WINED3DERR_INVALIDCALL;
3332 if (device->d3d_initialized)
3333 surface->resource.resource_ops->resource_unload(&surface->resource);
3335 if (surface->flags & SFLAG_DIBSECTION)
3337 DeleteDC(surface->hDC);
3338 DeleteObject(surface->dib.DIBsection);
3339 surface->dib.bitmap_data = NULL;
3340 surface->flags &= ~SFLAG_DIBSECTION;
3343 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_USERPTR);
3344 surface->resource.allocatedMemory = NULL;
3345 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
3346 surface->resource.heapMemory = NULL;
3348 surface->resource.width = width;
3349 surface->resource.height = height;
3350 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
3351 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
3353 surface->pow2Width = width;
3354 surface->pow2Height = height;
3356 else
3358 surface->pow2Width = surface->pow2Height = 1;
3359 while (surface->pow2Width < width)
3360 surface->pow2Width <<= 1;
3361 while (surface->pow2Height < height)
3362 surface->pow2Height <<= 1;
3365 if (surface->pow2Width != width || surface->pow2Height != height)
3366 surface->flags |= SFLAG_NONPOW2;
3367 else
3368 surface->flags &= ~SFLAG_NONPOW2;
3370 surface->resource.format = format;
3371 surface->resource.multisample_type = multisample_type;
3372 surface->resource.multisample_quality = multisample_quality;
3373 surface->resource.size = resource_size;
3375 if (!surface_init_sysmem(surface))
3376 return E_OUTOFMEMORY;
3378 return WINED3D_OK;
3381 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3383 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3385 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3387 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3389 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3390 return WINED3DERR_INVALIDCALL;
3393 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3394 surface->pow2Width, surface->pow2Height);
3395 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3396 surface->resource.format = format;
3398 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3399 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3400 format->glFormat, format->glInternal, format->glType);
3402 return WINED3D_OK;
3405 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3406 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3408 unsigned short *dst_s;
3409 const float *src_f;
3410 unsigned int x, y;
3412 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3414 for (y = 0; y < h; ++y)
3416 src_f = (const float *)(src + y * pitch_in);
3417 dst_s = (unsigned short *) (dst + y * pitch_out);
3418 for (x = 0; x < w; ++x)
3420 dst_s[x] = float_32_to_16(src_f + x);
3425 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3426 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3428 static const unsigned char convert_5to8[] =
3430 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3431 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3432 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3433 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3435 static const unsigned char convert_6to8[] =
3437 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3438 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3439 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3440 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3441 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3442 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3443 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3444 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3446 unsigned int x, y;
3448 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3450 for (y = 0; y < h; ++y)
3452 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3453 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3454 for (x = 0; x < w; ++x)
3456 WORD pixel = src_line[x];
3457 dst_line[x] = 0xff000000
3458 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3459 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3460 | convert_5to8[(pixel & 0x001f)];
3465 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3466 * in both cases we're just setting the X / Alpha channel to 0xff. */
3467 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3468 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3470 unsigned int x, y;
3472 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3474 for (y = 0; y < h; ++y)
3476 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3477 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3479 for (x = 0; x < w; ++x)
3481 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3486 static inline BYTE cliptobyte(int x)
3488 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3491 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3492 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3494 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3495 unsigned int x, y;
3497 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3499 for (y = 0; y < h; ++y)
3501 const BYTE *src_line = src + y * pitch_in;
3502 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3503 for (x = 0; x < w; ++x)
3505 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3506 * C = Y - 16; D = U - 128; E = V - 128;
3507 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3508 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3509 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3510 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3511 * U and V are shared between the pixels. */
3512 if (!(x & 1)) /* For every even pixel, read new U and V. */
3514 d = (int) src_line[1] - 128;
3515 e = (int) src_line[3] - 128;
3516 r2 = 409 * e + 128;
3517 g2 = - 100 * d - 208 * e + 128;
3518 b2 = 516 * d + 128;
3520 c2 = 298 * ((int) src_line[0] - 16);
3521 dst_line[x] = 0xff000000
3522 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3523 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3524 | cliptobyte((c2 + b2) >> 8); /* blue */
3525 /* Scale RGB values to 0..255 range,
3526 * then clip them if still not in range (may be negative),
3527 * then shift them within DWORD if necessary. */
3528 src_line += 2;
3533 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3534 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3536 unsigned int x, y;
3537 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3539 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3541 for (y = 0; y < h; ++y)
3543 const BYTE *src_line = src + y * pitch_in;
3544 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3545 for (x = 0; x < w; ++x)
3547 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3548 * C = Y - 16; D = U - 128; E = V - 128;
3549 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3550 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3551 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3552 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3553 * U and V are shared between the pixels. */
3554 if (!(x & 1)) /* For every even pixel, read new U and V. */
3556 d = (int) src_line[1] - 128;
3557 e = (int) src_line[3] - 128;
3558 r2 = 409 * e + 128;
3559 g2 = - 100 * d - 208 * e + 128;
3560 b2 = 516 * d + 128;
3562 c2 = 298 * ((int) src_line[0] - 16);
3563 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3564 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3565 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3566 /* Scale RGB values to 0..255 range,
3567 * then clip them if still not in range (may be negative),
3568 * then shift them within DWORD if necessary. */
3569 src_line += 2;
3574 struct d3dfmt_convertor_desc
3576 enum wined3d_format_id from, to;
3577 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3580 static const struct d3dfmt_convertor_desc convertors[] =
3582 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3583 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3584 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3585 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3586 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3587 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3590 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3591 enum wined3d_format_id to)
3593 unsigned int i;
3595 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3597 if (convertors[i].from == from && convertors[i].to == to)
3598 return &convertors[i];
3601 return NULL;
3604 /*****************************************************************************
3605 * surface_convert_format
3607 * Creates a duplicate of a surface in a different format. Is used by Blt to
3608 * blit between surfaces with different formats.
3610 * Parameters
3611 * source: Source surface
3612 * fmt: Requested destination format
3614 *****************************************************************************/
3615 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3617 struct wined3d_mapped_rect src_map, dst_map;
3618 const struct d3dfmt_convertor_desc *conv;
3619 struct wined3d_surface *ret = NULL;
3620 HRESULT hr;
3622 conv = find_convertor(source->resource.format->id, to_fmt);
3623 if (!conv)
3625 FIXME("Cannot find a conversion function from format %s to %s.\n",
3626 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3627 return NULL;
3630 wined3d_surface_create(source->resource.device, source->resource.width,
3631 source->resource.height, to_fmt, 0 /* level */, 0 /* usage */, WINED3DPOOL_SCRATCH,
3632 WINED3D_MULTISAMPLE_NONE /* TODO: Multisampled conversion */, 0 /* MultiSampleQuality */,
3633 source->surface_type, WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD,
3634 NULL /* parent */, &wined3d_null_parent_ops, &ret);
3635 if (!ret)
3637 ERR("Failed to create a destination surface for conversion.\n");
3638 return NULL;
3641 memset(&src_map, 0, sizeof(src_map));
3642 memset(&dst_map, 0, sizeof(dst_map));
3644 hr = wined3d_surface_map(source, &src_map, NULL, WINED3DLOCK_READONLY);
3645 if (FAILED(hr))
3647 ERR("Failed to lock the source surface.\n");
3648 wined3d_surface_decref(ret);
3649 return NULL;
3651 hr = wined3d_surface_map(ret, &dst_map, NULL, WINED3DLOCK_READONLY);
3652 if (FAILED(hr))
3654 ERR("Failed to lock the destination surface.\n");
3655 wined3d_surface_unmap(source);
3656 wined3d_surface_decref(ret);
3657 return NULL;
3660 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
3661 source->resource.width, source->resource.height);
3663 wined3d_surface_unmap(ret);
3664 wined3d_surface_unmap(source);
3666 return ret;
3669 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3670 unsigned int bpp, UINT pitch, DWORD color)
3672 BYTE *first;
3673 int x, y;
3675 /* Do first row */
3677 #define COLORFILL_ROW(type) \
3678 do { \
3679 type *d = (type *)buf; \
3680 for (x = 0; x < width; ++x) \
3681 d[x] = (type)color; \
3682 } while(0)
3684 switch (bpp)
3686 case 1:
3687 COLORFILL_ROW(BYTE);
3688 break;
3690 case 2:
3691 COLORFILL_ROW(WORD);
3692 break;
3694 case 3:
3696 BYTE *d = buf;
3697 for (x = 0; x < width; ++x, d += 3)
3699 d[0] = (color ) & 0xFF;
3700 d[1] = (color >> 8) & 0xFF;
3701 d[2] = (color >> 16) & 0xFF;
3703 break;
3705 case 4:
3706 COLORFILL_ROW(DWORD);
3707 break;
3709 default:
3710 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3711 return WINED3DERR_NOTAVAILABLE;
3714 #undef COLORFILL_ROW
3716 /* Now copy first row. */
3717 first = buf;
3718 for (y = 1; y < height; ++y)
3720 buf += pitch;
3721 memcpy(buf, first, width * bpp);
3724 return WINED3D_OK;
3727 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3729 TRACE("surface %p.\n", surface);
3731 if (!(surface->flags & SFLAG_LOCKED))
3733 WARN("Trying to unmap unmapped surface.\n");
3734 return WINEDDERR_NOTLOCKED;
3736 surface->flags &= ~SFLAG_LOCKED;
3738 surface->surface_ops->surface_unmap(surface);
3740 return WINED3D_OK;
3743 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3744 struct wined3d_mapped_rect *mapped_rect, const RECT *rect, DWORD flags)
3746 const struct wined3d_format *format = surface->resource.format;
3748 TRACE("surface %p, mapped_rect %p, rect %s, flags %#x.\n",
3749 surface, mapped_rect, wine_dbgstr_rect(rect), flags);
3751 if (surface->flags & SFLAG_LOCKED)
3753 WARN("Surface is already mapped.\n");
3754 return WINED3DERR_INVALIDCALL;
3756 if ((format->flags & WINED3DFMT_FLAG_BLOCKS)
3757 && rect && (rect->left || rect->top
3758 || rect->right != surface->resource.width
3759 || rect->bottom != surface->resource.height))
3761 UINT width_mask = format->block_width - 1;
3762 UINT height_mask = format->block_height - 1;
3764 if ((rect->left & width_mask) || (rect->right & width_mask)
3765 || (rect->top & height_mask) || (rect->bottom & height_mask))
3767 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3768 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3770 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3771 return WINED3DERR_INVALIDCALL;
3775 surface->flags |= SFLAG_LOCKED;
3777 if (!(surface->flags & SFLAG_LOCKABLE))
3778 WARN("Trying to lock unlockable surface.\n");
3780 /* Performance optimization: Count how often a surface is mapped, if it is
3781 * mapped regularly do not throw away the system memory copy. This avoids
3782 * the need to download the surface from OpenGL all the time. The surface
3783 * is still downloaded if the OpenGL texture is changed. */
3784 if (!(surface->flags & SFLAG_DYNLOCK))
3786 if (++surface->lockCount > MAXLOCKCOUNT)
3788 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3789 surface->flags |= SFLAG_DYNLOCK;
3793 surface->surface_ops->surface_map(surface, rect, flags);
3795 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3796 mapped_rect->row_pitch = surface->resource.width * format->byte_count;
3797 else
3798 mapped_rect->row_pitch = wined3d_surface_get_pitch(surface);
3800 if (!rect)
3802 mapped_rect->data = surface->resource.allocatedMemory;
3803 surface->lockedRect.left = 0;
3804 surface->lockedRect.top = 0;
3805 surface->lockedRect.right = surface->resource.width;
3806 surface->lockedRect.bottom = surface->resource.height;
3808 else
3810 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3812 /* Compressed textures are block based, so calculate the offset of
3813 * the block that contains the top-left pixel of the locked rectangle. */
3814 mapped_rect->data = surface->resource.allocatedMemory
3815 + ((rect->top / format->block_height) * mapped_rect->row_pitch)
3816 + ((rect->left / format->block_width) * format->block_byte_count);
3818 else
3820 mapped_rect->data = surface->resource.allocatedMemory
3821 + (mapped_rect->row_pitch * rect->top)
3822 + (rect->left * format->byte_count);
3824 surface->lockedRect.left = rect->left;
3825 surface->lockedRect.top = rect->top;
3826 surface->lockedRect.right = rect->right;
3827 surface->lockedRect.bottom = rect->bottom;
3830 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3831 TRACE("Returning memory %p, pitch %u.\n", mapped_rect->data, mapped_rect->row_pitch);
3833 return WINED3D_OK;
3836 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3838 struct wined3d_mapped_rect map;
3839 HRESULT hr;
3841 TRACE("surface %p, dc %p.\n", surface, dc);
3843 if (surface->flags & SFLAG_USERPTR)
3845 ERR("Not supported on surfaces with application-provided memory.\n");
3846 return WINEDDERR_NODC;
3849 /* Give more detailed info for ddraw. */
3850 if (surface->flags & SFLAG_DCINUSE)
3851 return WINEDDERR_DCALREADYCREATED;
3853 /* Can't GetDC if the surface is locked. */
3854 if (surface->flags & SFLAG_LOCKED)
3855 return WINED3DERR_INVALIDCALL;
3857 /* Create a DIB section if there isn't a dc yet. */
3858 if (!surface->hDC)
3860 if (surface->flags & SFLAG_CLIENT)
3862 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3863 surface_release_client_storage(surface);
3865 hr = surface_create_dib_section(surface);
3866 if (FAILED(hr))
3867 return WINED3DERR_INVALIDCALL;
3869 /* Use the DIB section from now on if we are not using a PBO. */
3870 if (!(surface->flags & SFLAG_PBO))
3871 surface->resource.allocatedMemory = surface->dib.bitmap_data;
3874 /* Map the surface. */
3875 hr = wined3d_surface_map(surface, &map, NULL, 0);
3876 if (FAILED(hr))
3878 ERR("Map failed, hr %#x.\n", hr);
3879 return hr;
3882 /* Sync the DIB with the PBO. This can't be done earlier because Map()
3883 * activates the allocatedMemory. */
3884 if (surface->flags & SFLAG_PBO)
3885 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
3887 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3888 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3890 /* GetDC on palettized formats is unsupported in D3D9, and the method
3891 * is missing in D3D8, so this should only be used for DX <=7
3892 * surfaces (with non-device palettes). */
3893 const PALETTEENTRY *pal = NULL;
3895 if (surface->palette)
3897 pal = surface->palette->palents;
3899 else
3901 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3902 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3904 if (dds_primary && dds_primary->palette)
3905 pal = dds_primary->palette->palents;
3908 if (pal)
3910 RGBQUAD col[256];
3911 unsigned int i;
3913 for (i = 0; i < 256; ++i)
3915 col[i].rgbRed = pal[i].peRed;
3916 col[i].rgbGreen = pal[i].peGreen;
3917 col[i].rgbBlue = pal[i].peBlue;
3918 col[i].rgbReserved = 0;
3920 SetDIBColorTable(surface->hDC, 0, 256, col);
3924 surface->flags |= SFLAG_DCINUSE;
3926 *dc = surface->hDC;
3927 TRACE("Returning dc %p.\n", *dc);
3929 return WINED3D_OK;
3932 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3934 TRACE("surface %p, dc %p.\n", surface, dc);
3936 if (!(surface->flags & SFLAG_DCINUSE))
3937 return WINEDDERR_NODC;
3939 if (surface->hDC != dc)
3941 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3942 dc, surface->hDC);
3943 return WINEDDERR_NODC;
3946 /* Copy the contents of the DIB over to the PBO. */
3947 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3948 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3950 /* We locked first, so unlock now. */
3951 wined3d_surface_unmap(surface);
3953 surface->flags &= ~SFLAG_DCINUSE;
3955 return WINED3D_OK;
3958 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3960 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3962 if (flags)
3964 static UINT once;
3965 if (!once++)
3966 FIXME("Ignoring flags %#x.\n", flags);
3967 else
3968 WARN("Ignoring flags %#x.\n", flags);
3971 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
3973 ERR("Not supported on swapchain surfaces.\n");
3974 return WINEDDERR_NOTFLIPPABLE;
3977 /* Flipping is only supported on render targets and overlays. */
3978 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
3980 WARN("Tried to flip a non-render target, non-overlay surface.\n");
3981 return WINEDDERR_NOTFLIPPABLE;
3984 flip_surface(surface, override);
3986 /* Update overlays if they're visible. */
3987 if ((surface->resource.usage & WINED3DUSAGE_OVERLAY) && surface->overlay_dest)
3988 return surface_draw_overlay(surface);
3990 return WINED3D_OK;
3993 /* Do not call while under the GL lock. */
3994 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3996 struct wined3d_device *device = surface->resource.device;
3998 TRACE("iface %p, srgb %#x.\n", surface, srgb);
4000 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4002 struct wined3d_texture *texture = surface->container.u.texture;
4004 TRACE("Passing to container (%p).\n", texture);
4005 texture->texture_ops->texture_preload(texture, srgb);
4007 else
4009 struct wined3d_context *context;
4011 TRACE("(%p) : About to load surface\n", surface);
4013 /* TODO: Use already acquired context when possible. */
4014 context = context_acquire(device, NULL);
4016 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
4018 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
4020 /* Tell opengl to try and keep this texture in video ram (well mostly) */
4021 GLclampf tmp;
4022 tmp = 0.9f;
4023 ENTER_GL();
4024 glPrioritizeTextures(1, &surface->texture_name, &tmp);
4025 LEAVE_GL();
4028 context_release(context);
4032 BOOL surface_init_sysmem(struct wined3d_surface *surface)
4034 if (!surface->resource.allocatedMemory)
4036 if (!surface->resource.heapMemory)
4038 if (!(surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
4039 surface->resource.size + RESOURCE_ALIGNMENT)))
4041 ERR("Failed to allocate memory.\n");
4042 return FALSE;
4045 else if (!(surface->flags & SFLAG_CLIENT))
4047 ERR("Surface %p has heapMemory %p and flags %#x.\n",
4048 surface, surface->resource.heapMemory, surface->flags);
4051 surface->resource.allocatedMemory =
4052 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
4054 else
4056 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
4059 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
4061 return TRUE;
4064 /* Read the framebuffer back into the surface */
4065 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
4067 struct wined3d_device *device = surface->resource.device;
4068 const struct wined3d_gl_info *gl_info;
4069 struct wined3d_context *context;
4070 BYTE *mem;
4071 GLint fmt;
4072 GLint type;
4073 BYTE *row, *top, *bottom;
4074 int i;
4075 BOOL bpp;
4076 RECT local_rect;
4077 BOOL srcIsUpsideDown;
4078 GLint rowLen = 0;
4079 GLint skipPix = 0;
4080 GLint skipRow = 0;
4082 context = context_acquire(device, surface);
4083 context_apply_blit_state(context, device);
4084 gl_info = context->gl_info;
4086 ENTER_GL();
4088 /* Select the correct read buffer, and give some debug output.
4089 * There is no need to keep track of the current read buffer or reset it, every part of the code
4090 * that reads sets the read buffer as desired.
4092 if (surface_is_offscreen(surface))
4094 /* Mapping the primary render target which is not on a swapchain.
4095 * Read from the back buffer. */
4096 TRACE("Mapping offscreen render target.\n");
4097 glReadBuffer(device->offscreenBuffer);
4098 srcIsUpsideDown = TRUE;
4100 else
4102 /* Onscreen surfaces are always part of a swapchain */
4103 GLenum buffer = surface_get_gl_buffer(surface);
4104 TRACE("Mapping %#x buffer.\n", buffer);
4105 glReadBuffer(buffer);
4106 checkGLcall("glReadBuffer");
4107 srcIsUpsideDown = FALSE;
4110 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
4111 if (!rect)
4113 local_rect.left = 0;
4114 local_rect.top = 0;
4115 local_rect.right = surface->resource.width;
4116 local_rect.bottom = surface->resource.height;
4118 else
4120 local_rect = *rect;
4122 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
4124 switch (surface->resource.format->id)
4126 case WINED3DFMT_P8_UINT:
4128 if (primary_render_target_is_p8(device))
4130 /* In case of P8 render targets the index is stored in the alpha component */
4131 fmt = GL_ALPHA;
4132 type = GL_UNSIGNED_BYTE;
4133 mem = dest;
4134 bpp = surface->resource.format->byte_count;
4136 else
4138 /* GL can't return palettized data, so read ARGB pixels into a
4139 * separate block of memory and convert them into palettized format
4140 * in software. Slow, but if the app means to use palettized render
4141 * targets and locks it...
4143 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4144 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4145 * for the color channels when palettizing the colors.
4147 fmt = GL_RGB;
4148 type = GL_UNSIGNED_BYTE;
4149 pitch *= 3;
4150 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4151 if (!mem)
4153 ERR("Out of memory\n");
4154 LEAVE_GL();
4155 return;
4157 bpp = surface->resource.format->byte_count * 3;
4160 break;
4162 default:
4163 mem = dest;
4164 fmt = surface->resource.format->glFormat;
4165 type = surface->resource.format->glType;
4166 bpp = surface->resource.format->byte_count;
4169 if (surface->flags & SFLAG_PBO)
4171 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4172 checkGLcall("glBindBufferARB");
4173 if (mem)
4175 ERR("mem not null for pbo -- unexpected\n");
4176 mem = NULL;
4180 /* Save old pixel store pack state */
4181 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4182 checkGLcall("glGetIntegerv");
4183 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4184 checkGLcall("glGetIntegerv");
4185 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4186 checkGLcall("glGetIntegerv");
4188 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4189 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4190 checkGLcall("glPixelStorei");
4191 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4192 checkGLcall("glPixelStorei");
4193 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4194 checkGLcall("glPixelStorei");
4196 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4197 local_rect.right - local_rect.left,
4198 local_rect.bottom - local_rect.top,
4199 fmt, type, mem);
4200 checkGLcall("glReadPixels");
4202 /* Reset previous pixel store pack state */
4203 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4204 checkGLcall("glPixelStorei");
4205 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4206 checkGLcall("glPixelStorei");
4207 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4208 checkGLcall("glPixelStorei");
4210 if (surface->flags & SFLAG_PBO)
4212 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4213 checkGLcall("glBindBufferARB");
4215 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4216 * to get a pointer to it and perform the flipping in software. This is a lot
4217 * faster than calling glReadPixels for each line. In case we want more speed
4218 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4219 if (!srcIsUpsideDown)
4221 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4222 checkGLcall("glBindBufferARB");
4224 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4225 checkGLcall("glMapBufferARB");
4229 /* TODO: Merge this with the palettization loop below for P8 targets */
4230 if(!srcIsUpsideDown) {
4231 UINT len, off;
4232 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4233 Flip the lines in software */
4234 len = (local_rect.right - local_rect.left) * bpp;
4235 off = local_rect.left * bpp;
4237 row = HeapAlloc(GetProcessHeap(), 0, len);
4238 if(!row) {
4239 ERR("Out of memory\n");
4240 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4241 HeapFree(GetProcessHeap(), 0, mem);
4242 LEAVE_GL();
4243 return;
4246 top = mem + pitch * local_rect.top;
4247 bottom = mem + pitch * (local_rect.bottom - 1);
4248 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4249 memcpy(row, top + off, len);
4250 memcpy(top + off, bottom + off, len);
4251 memcpy(bottom + off, row, len);
4252 top += pitch;
4253 bottom -= pitch;
4255 HeapFree(GetProcessHeap(), 0, row);
4257 /* Unmap the temp PBO buffer */
4258 if (surface->flags & SFLAG_PBO)
4260 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4261 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4265 LEAVE_GL();
4266 context_release(context);
4268 /* For P8 textures we need to perform an inverse palette lookup. This is
4269 * done by searching for a palette index which matches the RGB value.
4270 * Note this isn't guaranteed to work when there are multiple entries for
4271 * the same color but we have no choice. In case of P8 render targets,
4272 * the index is stored in the alpha component so no conversion is needed. */
4273 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4275 const PALETTEENTRY *pal = NULL;
4276 DWORD width = pitch / 3;
4277 int x, y, c;
4279 if (surface->palette)
4281 pal = surface->palette->palents;
4283 else
4285 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4286 HeapFree(GetProcessHeap(), 0, mem);
4287 return;
4290 for(y = local_rect.top; y < local_rect.bottom; y++) {
4291 for(x = local_rect.left; x < local_rect.right; x++) {
4292 /* start lines pixels */
4293 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4294 const BYTE *green = blue + 1;
4295 const BYTE *red = green + 1;
4297 for(c = 0; c < 256; c++) {
4298 if(*red == pal[c].peRed &&
4299 *green == pal[c].peGreen &&
4300 *blue == pal[c].peBlue)
4302 *((BYTE *) dest + y * width + x) = c;
4303 break;
4308 HeapFree(GetProcessHeap(), 0, mem);
4312 /* Read the framebuffer contents into a texture. Note that this function
4313 * doesn't do any kind of flipping. Using this on an onscreen surface will
4314 * result in a flipped D3D texture. */
4315 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
4317 struct wined3d_device *device = surface->resource.device;
4318 struct wined3d_context *context;
4320 context = context_acquire(device, surface);
4321 device_invalidate_state(device, STATE_FRAMEBUFFER);
4323 surface_prepare_texture(surface, context, srgb);
4324 surface_bind_and_dirtify(surface, context, srgb);
4326 TRACE("Reading back offscreen render target %p.\n", surface);
4328 ENTER_GL();
4330 if (surface_is_offscreen(surface))
4331 glReadBuffer(device->offscreenBuffer);
4332 else
4333 glReadBuffer(surface_get_gl_buffer(surface));
4334 checkGLcall("glReadBuffer");
4336 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4337 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4338 checkGLcall("glCopyTexSubImage2D");
4340 LEAVE_GL();
4342 context_release(context);
4345 /* Context activation is done by the caller. */
4346 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4347 struct wined3d_context *context, BOOL srgb)
4349 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4350 CONVERT_TYPES convert;
4351 struct wined3d_format format;
4353 if (surface->flags & alloc_flag) return;
4355 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4356 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4357 else surface->flags &= ~SFLAG_CONVERTED;
4359 surface_bind_and_dirtify(surface, context, srgb);
4360 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4361 surface->flags |= alloc_flag;
4364 /* Context activation is done by the caller. */
4365 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4367 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4369 struct wined3d_texture *texture = surface->container.u.texture;
4370 UINT sub_count = texture->level_count * texture->layer_count;
4371 UINT i;
4373 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4375 for (i = 0; i < sub_count; ++i)
4377 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4378 surface_prepare_texture_internal(s, context, srgb);
4381 return;
4384 surface_prepare_texture_internal(surface, context, srgb);
4387 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4389 if (multisample)
4391 if (surface->rb_multisample)
4392 return;
4394 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4395 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4396 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4397 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4398 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4400 else
4402 if (surface->rb_resolved)
4403 return;
4405 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4406 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4407 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4408 surface->pow2Width, surface->pow2Height);
4409 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4413 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4414 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4416 struct wined3d_device *device = surface->resource.device;
4417 UINT pitch = wined3d_surface_get_pitch(surface);
4418 const struct wined3d_gl_info *gl_info;
4419 struct wined3d_context *context;
4420 RECT local_rect;
4421 UINT w, h;
4423 surface_get_rect(surface, rect, &local_rect);
4425 mem += local_rect.top * pitch + local_rect.left * bpp;
4426 w = local_rect.right - local_rect.left;
4427 h = local_rect.bottom - local_rect.top;
4429 /* Activate the correct context for the render target */
4430 context = context_acquire(device, surface);
4431 context_apply_blit_state(context, device);
4432 gl_info = context->gl_info;
4434 ENTER_GL();
4436 if (!surface_is_offscreen(surface))
4438 GLenum buffer = surface_get_gl_buffer(surface);
4439 TRACE("Unlocking %#x buffer.\n", buffer);
4440 context_set_draw_buffer(context, buffer);
4442 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4443 glPixelZoom(1.0f, -1.0f);
4445 else
4447 /* Primary offscreen render target */
4448 TRACE("Offscreen render target.\n");
4449 context_set_draw_buffer(context, device->offscreenBuffer);
4451 glPixelZoom(1.0f, 1.0f);
4454 glRasterPos3i(local_rect.left, local_rect.top, 1);
4455 checkGLcall("glRasterPos3i");
4457 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4458 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4460 if (surface->flags & SFLAG_PBO)
4462 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4463 checkGLcall("glBindBufferARB");
4466 glDrawPixels(w, h, fmt, type, mem);
4467 checkGLcall("glDrawPixels");
4469 if (surface->flags & SFLAG_PBO)
4471 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4472 checkGLcall("glBindBufferARB");
4475 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4476 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4478 LEAVE_GL();
4480 if (wined3d_settings.strict_draw_ordering
4481 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4482 && surface->container.u.swapchain->front_buffer == surface))
4483 wglFlush();
4485 context_release(context);
4488 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4489 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4491 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4492 const struct wined3d_device *device = surface->resource.device;
4493 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4494 BOOL blit_supported = FALSE;
4496 /* Copy the default values from the surface. Below we might perform fixups */
4497 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4498 *format = *surface->resource.format;
4499 *convert = NO_CONVERSION;
4501 /* Ok, now look if we have to do any conversion */
4502 switch (surface->resource.format->id)
4504 case WINED3DFMT_P8_UINT:
4505 /* Below the call to blit_supported is disabled for Wine 1.2
4506 * because the function isn't operating correctly yet. At the
4507 * moment 8-bit blits are handled in software and if certain GL
4508 * extensions are around, surface conversion is performed at
4509 * upload time. The blit_supported call recognizes it as a
4510 * destination fixup. This type of upload 'fixup' and 8-bit to
4511 * 8-bit blits need to be handled by the blit_shader.
4512 * TODO: get rid of this #if 0. */
4513 #if 0
4514 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4515 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4516 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4517 #endif
4518 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4520 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4521 * texturing. Further also use conversion in case of color keying.
4522 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4523 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4524 * conflicts with this.
4526 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4527 || colorkey_active || !use_texturing)
4529 format->glFormat = GL_RGBA;
4530 format->glInternal = GL_RGBA;
4531 format->glType = GL_UNSIGNED_BYTE;
4532 format->conv_byte_count = 4;
4533 if (colorkey_active)
4534 *convert = CONVERT_PALETTED_CK;
4535 else
4536 *convert = CONVERT_PALETTED;
4538 break;
4540 case WINED3DFMT_B2G3R3_UNORM:
4541 /* **********************
4542 GL_UNSIGNED_BYTE_3_3_2
4543 ********************** */
4544 if (colorkey_active) {
4545 /* This texture format will never be used.. So do not care about color keying
4546 up until the point in time it will be needed :-) */
4547 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4549 break;
4551 case WINED3DFMT_B5G6R5_UNORM:
4552 if (colorkey_active)
4554 *convert = CONVERT_CK_565;
4555 format->glFormat = GL_RGBA;
4556 format->glInternal = GL_RGB5_A1;
4557 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4558 format->conv_byte_count = 2;
4560 break;
4562 case WINED3DFMT_B5G5R5X1_UNORM:
4563 if (colorkey_active)
4565 *convert = CONVERT_CK_5551;
4566 format->glFormat = GL_BGRA;
4567 format->glInternal = GL_RGB5_A1;
4568 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4569 format->conv_byte_count = 2;
4571 break;
4573 case WINED3DFMT_B8G8R8_UNORM:
4574 if (colorkey_active)
4576 *convert = CONVERT_CK_RGB24;
4577 format->glFormat = GL_RGBA;
4578 format->glInternal = GL_RGBA8;
4579 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4580 format->conv_byte_count = 4;
4582 break;
4584 case WINED3DFMT_B8G8R8X8_UNORM:
4585 if (colorkey_active)
4587 *convert = CONVERT_RGB32_888;
4588 format->glFormat = GL_RGBA;
4589 format->glInternal = GL_RGBA8;
4590 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4591 format->conv_byte_count = 4;
4593 break;
4595 default:
4596 break;
4599 if (*convert != NO_CONVERSION)
4601 format->rtInternal = format->glInternal;
4602 format->glGammaInternal = format->glInternal;
4605 return WINED3D_OK;
4608 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
4610 /* FIXME: Is this really how color keys are supposed to work? I think it
4611 * makes more sense to compare the individual channels. */
4612 return color >= color_key->color_space_low_value
4613 && color <= color_key->color_space_high_value;
4616 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4618 const struct wined3d_device *device = surface->resource.device;
4619 const struct wined3d_palette *pal = surface->palette;
4620 BOOL index_in_alpha = FALSE;
4621 unsigned int i;
4623 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4624 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4625 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4626 * duplicate entries. Store the color key in the unused alpha component to speed the
4627 * download up and to make conversion unneeded. */
4628 index_in_alpha = primary_render_target_is_p8(device);
4630 if (!pal)
4632 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4633 if (index_in_alpha)
4635 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4636 * there's no palette at this time. */
4637 for (i = 0; i < 256; i++) table[i][3] = i;
4640 else
4642 TRACE("Using surface palette %p\n", pal);
4643 /* Get the surface's palette */
4644 for (i = 0; i < 256; ++i)
4646 table[i][0] = pal->palents[i].peRed;
4647 table[i][1] = pal->palents[i].peGreen;
4648 table[i][2] = pal->palents[i].peBlue;
4650 /* When index_in_alpha is set the palette index is stored in the
4651 * alpha component. In case of a readback we can then read
4652 * GL_ALPHA. Color keying is handled in BltOverride using a
4653 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4654 * color key itself is passed to glAlphaFunc in other cases the
4655 * alpha component of pixels that should be masked away is set to 0. */
4656 if (index_in_alpha)
4657 table[i][3] = i;
4658 else if (colorkey && color_in_range(&surface->src_blt_color_key, i))
4659 table[i][3] = 0x00;
4660 else if (pal->flags & WINEDDPCAPS_ALPHA)
4661 table[i][3] = pal->palents[i].peFlags;
4662 else
4663 table[i][3] = 0xFF;
4668 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4669 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4671 const BYTE *source;
4672 BYTE *dest;
4673 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4675 switch (convert) {
4676 case NO_CONVERSION:
4678 memcpy(dst, src, pitch * height);
4679 break;
4681 case CONVERT_PALETTED:
4682 case CONVERT_PALETTED_CK:
4684 BYTE table[256][4];
4685 unsigned int x, y;
4687 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4689 for (y = 0; y < height; y++)
4691 source = src + pitch * y;
4692 dest = dst + outpitch * y;
4693 /* This is an 1 bpp format, using the width here is fine */
4694 for (x = 0; x < width; x++) {
4695 BYTE color = *source++;
4696 *dest++ = table[color][0];
4697 *dest++ = table[color][1];
4698 *dest++ = table[color][2];
4699 *dest++ = table[color][3];
4703 break;
4705 case CONVERT_CK_565:
4707 /* Converting the 565 format in 5551 packed to emulate color-keying.
4709 Note : in all these conversion, it would be best to average the averaging
4710 pixels to get the color of the pixel that will be color-keyed to
4711 prevent 'color bleeding'. This will be done later on if ever it is
4712 too visible.
4714 Note2: Nvidia documents say that their driver does not support alpha + color keying
4715 on the same surface and disables color keying in such a case
4717 unsigned int x, y;
4718 const WORD *Source;
4719 WORD *Dest;
4721 TRACE("Color keyed 565\n");
4723 for (y = 0; y < height; y++) {
4724 Source = (const WORD *)(src + y * pitch);
4725 Dest = (WORD *) (dst + y * outpitch);
4726 for (x = 0; x < width; x++ ) {
4727 WORD color = *Source++;
4728 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4729 if (!color_in_range(&surface->src_blt_color_key, color))
4730 *Dest |= 0x0001;
4731 Dest++;
4735 break;
4737 case CONVERT_CK_5551:
4739 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4740 unsigned int x, y;
4741 const WORD *Source;
4742 WORD *Dest;
4743 TRACE("Color keyed 5551\n");
4744 for (y = 0; y < height; y++) {
4745 Source = (const WORD *)(src + y * pitch);
4746 Dest = (WORD *) (dst + y * outpitch);
4747 for (x = 0; x < width; x++ ) {
4748 WORD color = *Source++;
4749 *Dest = color;
4750 if (!color_in_range(&surface->src_blt_color_key, color))
4751 *Dest |= (1 << 15);
4752 else
4753 *Dest &= ~(1 << 15);
4754 Dest++;
4758 break;
4760 case CONVERT_CK_RGB24:
4762 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4763 unsigned int x, y;
4764 for (y = 0; y < height; y++)
4766 source = src + pitch * y;
4767 dest = dst + outpitch * y;
4768 for (x = 0; x < width; x++) {
4769 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4770 DWORD dstcolor = color << 8;
4771 if (!color_in_range(&surface->src_blt_color_key, color))
4772 dstcolor |= 0xff;
4773 *(DWORD*)dest = dstcolor;
4774 source += 3;
4775 dest += 4;
4779 break;
4781 case CONVERT_RGB32_888:
4783 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4784 unsigned int x, y;
4785 for (y = 0; y < height; y++)
4787 source = src + pitch * y;
4788 dest = dst + outpitch * y;
4789 for (x = 0; x < width; x++) {
4790 DWORD color = 0xffffff & *(const DWORD*)source;
4791 DWORD dstcolor = color << 8;
4792 if (!color_in_range(&surface->src_blt_color_key, color))
4793 dstcolor |= 0xff;
4794 *(DWORD*)dest = dstcolor;
4795 source += 4;
4796 dest += 4;
4800 break;
4802 default:
4803 ERR("Unsupported conversion type %#x.\n", convert);
4805 return WINED3D_OK;
4808 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4810 /* Flip the surface contents */
4811 /* Flip the DC */
4813 HDC tmp;
4814 tmp = front->hDC;
4815 front->hDC = back->hDC;
4816 back->hDC = tmp;
4819 /* Flip the DIBsection */
4821 HBITMAP tmp = front->dib.DIBsection;
4822 front->dib.DIBsection = back->dib.DIBsection;
4823 back->dib.DIBsection = tmp;
4826 /* Flip the surface data */
4828 void* tmp;
4830 tmp = front->dib.bitmap_data;
4831 front->dib.bitmap_data = back->dib.bitmap_data;
4832 back->dib.bitmap_data = tmp;
4834 tmp = front->resource.allocatedMemory;
4835 front->resource.allocatedMemory = back->resource.allocatedMemory;
4836 back->resource.allocatedMemory = tmp;
4838 tmp = front->resource.heapMemory;
4839 front->resource.heapMemory = back->resource.heapMemory;
4840 back->resource.heapMemory = tmp;
4843 /* Flip the PBO */
4845 GLuint tmp_pbo = front->pbo;
4846 front->pbo = back->pbo;
4847 back->pbo = tmp_pbo;
4850 /* Flip the opengl texture */
4852 GLuint tmp;
4854 tmp = back->texture_name;
4855 back->texture_name = front->texture_name;
4856 front->texture_name = tmp;
4858 tmp = back->texture_name_srgb;
4859 back->texture_name_srgb = front->texture_name_srgb;
4860 front->texture_name_srgb = tmp;
4862 tmp = back->rb_multisample;
4863 back->rb_multisample = front->rb_multisample;
4864 front->rb_multisample = tmp;
4866 tmp = back->rb_resolved;
4867 back->rb_resolved = front->rb_resolved;
4868 front->rb_resolved = tmp;
4870 resource_unload(&back->resource);
4871 resource_unload(&front->resource);
4875 DWORD tmp_flags = back->flags;
4876 back->flags = front->flags;
4877 front->flags = tmp_flags;
4881 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4882 * pixel copy calls. */
4883 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4884 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4886 struct wined3d_device *device = dst_surface->resource.device;
4887 float xrel, yrel;
4888 UINT row;
4889 struct wined3d_context *context;
4890 BOOL upsidedown = FALSE;
4891 RECT dst_rect = *dst_rect_in;
4893 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4894 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4896 if(dst_rect.top > dst_rect.bottom) {
4897 UINT tmp = dst_rect.bottom;
4898 dst_rect.bottom = dst_rect.top;
4899 dst_rect.top = tmp;
4900 upsidedown = TRUE;
4903 context = context_acquire(device, src_surface);
4904 context_apply_blit_state(context, device);
4905 surface_internal_preload(dst_surface, SRGB_RGB);
4906 ENTER_GL();
4908 /* Bind the target texture */
4909 context_bind_texture(context, dst_surface->texture_target, dst_surface->texture_name);
4910 if (surface_is_offscreen(src_surface))
4912 TRACE("Reading from an offscreen target\n");
4913 upsidedown = !upsidedown;
4914 glReadBuffer(device->offscreenBuffer);
4916 else
4918 glReadBuffer(surface_get_gl_buffer(src_surface));
4920 checkGLcall("glReadBuffer");
4922 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4923 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4925 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4927 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4929 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4930 ERR("Texture filtering not supported in direct blit\n");
4933 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4934 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4936 ERR("Texture filtering not supported in direct blit\n");
4939 if (upsidedown
4940 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4941 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4943 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4945 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4946 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4947 src_rect->left, src_surface->resource.height - src_rect->bottom,
4948 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4950 else
4952 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4953 /* I have to process this row by row to swap the image,
4954 * otherwise it would be upside down, so stretching in y direction
4955 * doesn't cost extra time
4957 * However, stretching in x direction can be avoided if not necessary
4959 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4960 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4962 /* Well, that stuff works, but it's very slow.
4963 * find a better way instead
4965 UINT col;
4967 for (col = dst_rect.left; col < dst_rect.right; ++col)
4969 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4970 dst_rect.left + col /* x offset */, row /* y offset */,
4971 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4974 else
4976 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4977 dst_rect.left /* x offset */, row /* y offset */,
4978 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4982 checkGLcall("glCopyTexSubImage2D");
4984 LEAVE_GL();
4985 context_release(context);
4987 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4988 * path is never entered
4990 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4993 /* Uses the hardware to stretch and flip the image */
4994 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4995 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4997 struct wined3d_device *device = dst_surface->resource.device;
4998 struct wined3d_swapchain *src_swapchain = NULL;
4999 GLuint src, backup = 0;
5000 float left, right, top, bottom; /* Texture coordinates */
5001 UINT fbwidth = src_surface->resource.width;
5002 UINT fbheight = src_surface->resource.height;
5003 struct wined3d_context *context;
5004 GLenum drawBuffer = GL_BACK;
5005 GLenum texture_target;
5006 BOOL noBackBufferBackup;
5007 BOOL src_offscreen;
5008 BOOL upsidedown = FALSE;
5009 RECT dst_rect = *dst_rect_in;
5011 TRACE("Using hwstretch blit\n");
5012 /* Activate the Proper context for reading from the source surface, set it up for blitting */
5013 context = context_acquire(device, src_surface);
5014 context_apply_blit_state(context, device);
5015 surface_internal_preload(dst_surface, SRGB_RGB);
5017 src_offscreen = surface_is_offscreen(src_surface);
5018 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
5019 if (!noBackBufferBackup && !src_surface->texture_name)
5021 /* Get it a description */
5022 surface_internal_preload(src_surface, SRGB_RGB);
5024 ENTER_GL();
5026 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
5027 * This way we don't have to wait for the 2nd readback to finish to leave this function.
5029 if (context->aux_buffers >= 2)
5031 /* Got more than one aux buffer? Use the 2nd aux buffer */
5032 drawBuffer = GL_AUX1;
5034 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
5036 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
5037 drawBuffer = GL_AUX0;
5040 if(noBackBufferBackup) {
5041 glGenTextures(1, &backup);
5042 checkGLcall("glGenTextures");
5043 context_bind_texture(context, GL_TEXTURE_2D, backup);
5044 texture_target = GL_TEXTURE_2D;
5045 } else {
5046 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
5047 * we are reading from the back buffer, the backup can be used as source texture
5049 texture_target = src_surface->texture_target;
5050 context_bind_texture(context, texture_target, src_surface->texture_name);
5051 glEnable(texture_target);
5052 checkGLcall("glEnable(texture_target)");
5054 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
5055 src_surface->flags &= ~SFLAG_INTEXTURE;
5058 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
5059 * glCopyTexSubImage is a bit picky about the parameters we pass to it
5061 if(dst_rect.top > dst_rect.bottom) {
5062 UINT tmp = dst_rect.bottom;
5063 dst_rect.bottom = dst_rect.top;
5064 dst_rect.top = tmp;
5065 upsidedown = TRUE;
5068 if (src_offscreen)
5070 TRACE("Reading from an offscreen target\n");
5071 upsidedown = !upsidedown;
5072 glReadBuffer(device->offscreenBuffer);
5074 else
5076 glReadBuffer(surface_get_gl_buffer(src_surface));
5079 /* TODO: Only back up the part that will be overwritten */
5080 glCopyTexSubImage2D(texture_target, 0,
5081 0, 0 /* read offsets */,
5082 0, 0,
5083 fbwidth,
5084 fbheight);
5086 checkGLcall("glCopyTexSubImage2D");
5088 /* No issue with overriding these - the sampler is dirty due to blit usage */
5089 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
5090 wined3d_gl_mag_filter(magLookup, Filter));
5091 checkGLcall("glTexParameteri");
5092 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5093 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
5094 checkGLcall("glTexParameteri");
5096 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5097 src_swapchain = src_surface->container.u.swapchain;
5098 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5100 src = backup ? backup : src_surface->texture_name;
5102 else
5104 glReadBuffer(GL_FRONT);
5105 checkGLcall("glReadBuffer(GL_FRONT)");
5107 glGenTextures(1, &src);
5108 checkGLcall("glGenTextures(1, &src)");
5109 context_bind_texture(context, GL_TEXTURE_2D, src);
5111 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5112 * out for power of 2 sizes
5114 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5115 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5116 checkGLcall("glTexImage2D");
5117 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5118 0, 0 /* read offsets */,
5119 0, 0,
5120 fbwidth,
5121 fbheight);
5123 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5124 checkGLcall("glTexParameteri");
5125 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5126 checkGLcall("glTexParameteri");
5128 glReadBuffer(GL_BACK);
5129 checkGLcall("glReadBuffer(GL_BACK)");
5131 if(texture_target != GL_TEXTURE_2D) {
5132 glDisable(texture_target);
5133 glEnable(GL_TEXTURE_2D);
5134 texture_target = GL_TEXTURE_2D;
5137 checkGLcall("glEnd and previous");
5139 left = src_rect->left;
5140 right = src_rect->right;
5142 if (!upsidedown)
5144 top = src_surface->resource.height - src_rect->top;
5145 bottom = src_surface->resource.height - src_rect->bottom;
5147 else
5149 top = src_surface->resource.height - src_rect->bottom;
5150 bottom = src_surface->resource.height - src_rect->top;
5153 if (src_surface->flags & SFLAG_NORMCOORD)
5155 left /= src_surface->pow2Width;
5156 right /= src_surface->pow2Width;
5157 top /= src_surface->pow2Height;
5158 bottom /= src_surface->pow2Height;
5161 /* draw the source texture stretched and upside down. The correct surface is bound already */
5162 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5163 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5165 context_set_draw_buffer(context, drawBuffer);
5166 glReadBuffer(drawBuffer);
5168 glBegin(GL_QUADS);
5169 /* bottom left */
5170 glTexCoord2f(left, bottom);
5171 glVertex2i(0, 0);
5173 /* top left */
5174 glTexCoord2f(left, top);
5175 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5177 /* top right */
5178 glTexCoord2f(right, top);
5179 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5181 /* bottom right */
5182 glTexCoord2f(right, bottom);
5183 glVertex2i(dst_rect.right - dst_rect.left, 0);
5184 glEnd();
5185 checkGLcall("glEnd and previous");
5187 if (texture_target != dst_surface->texture_target)
5189 glDisable(texture_target);
5190 glEnable(dst_surface->texture_target);
5191 texture_target = dst_surface->texture_target;
5194 /* Now read the stretched and upside down image into the destination texture */
5195 context_bind_texture(context, texture_target, dst_surface->texture_name);
5196 glCopyTexSubImage2D(texture_target,
5198 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5199 0, 0, /* We blitted the image to the origin */
5200 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5201 checkGLcall("glCopyTexSubImage2D");
5203 if(drawBuffer == GL_BACK) {
5204 /* Write the back buffer backup back */
5205 if(backup) {
5206 if(texture_target != GL_TEXTURE_2D) {
5207 glDisable(texture_target);
5208 glEnable(GL_TEXTURE_2D);
5209 texture_target = GL_TEXTURE_2D;
5211 context_bind_texture(context, GL_TEXTURE_2D, backup);
5213 else
5215 if (texture_target != src_surface->texture_target)
5217 glDisable(texture_target);
5218 glEnable(src_surface->texture_target);
5219 texture_target = src_surface->texture_target;
5221 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5224 glBegin(GL_QUADS);
5225 /* top left */
5226 glTexCoord2f(0.0f, 0.0f);
5227 glVertex2i(0, fbheight);
5229 /* bottom left */
5230 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5231 glVertex2i(0, 0);
5233 /* bottom right */
5234 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5235 (float)fbheight / (float)src_surface->pow2Height);
5236 glVertex2i(fbwidth, 0);
5238 /* top right */
5239 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5240 glVertex2i(fbwidth, fbheight);
5241 glEnd();
5243 glDisable(texture_target);
5244 checkGLcall("glDisable(texture_target)");
5246 /* Cleanup */
5247 if (src != src_surface->texture_name && src != backup)
5249 glDeleteTextures(1, &src);
5250 checkGLcall("glDeleteTextures(1, &src)");
5252 if(backup) {
5253 glDeleteTextures(1, &backup);
5254 checkGLcall("glDeleteTextures(1, &backup)");
5257 LEAVE_GL();
5259 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5261 context_release(context);
5263 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5264 * path is never entered
5266 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5269 /* Front buffer coordinates are always full screen coordinates, but our GL
5270 * drawable is limited to the window's client area. The sysmem and texture
5271 * copies do have the full screen size. Note that GL has a bottom-left
5272 * origin, while D3D has a top-left origin. */
5273 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5275 UINT drawable_height;
5277 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5278 && surface == surface->container.u.swapchain->front_buffer)
5280 POINT offset = {0, 0};
5281 RECT windowsize;
5283 ScreenToClient(window, &offset);
5284 OffsetRect(rect, offset.x, offset.y);
5286 GetClientRect(window, &windowsize);
5287 drawable_height = windowsize.bottom - windowsize.top;
5289 else
5291 drawable_height = surface->resource.height;
5294 rect->top = drawable_height - rect->top;
5295 rect->bottom = drawable_height - rect->bottom;
5298 static void surface_blt_to_drawable(const struct wined3d_device *device,
5299 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5300 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5301 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5303 struct wined3d_context *context;
5304 RECT src_rect, dst_rect;
5306 src_rect = *src_rect_in;
5307 dst_rect = *dst_rect_in;
5309 /* Make sure the surface is up-to-date. This should probably use
5310 * surface_load_location() and worry about the destination surface too,
5311 * unless we're overwriting it completely. */
5312 surface_internal_preload(src_surface, SRGB_RGB);
5314 /* Activate the destination context, set it up for blitting */
5315 context = context_acquire(device, dst_surface);
5316 context_apply_blit_state(context, device);
5318 if (!surface_is_offscreen(dst_surface))
5319 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5321 device->blitter->set_shader(device->blit_priv, context, src_surface);
5323 ENTER_GL();
5325 if (color_key)
5327 glEnable(GL_ALPHA_TEST);
5328 checkGLcall("glEnable(GL_ALPHA_TEST)");
5330 /* When the primary render target uses P8, the alpha component
5331 * contains the palette index. Which means that the colorkey is one of
5332 * the palette entries. In other cases pixels that should be masked
5333 * away have alpha set to 0. */
5334 if (primary_render_target_is_p8(device))
5335 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->src_blt_color_key.color_space_low_value / 256.0f);
5336 else
5337 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5338 checkGLcall("glAlphaFunc");
5340 else
5342 glDisable(GL_ALPHA_TEST);
5343 checkGLcall("glDisable(GL_ALPHA_TEST)");
5346 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5348 if (color_key)
5350 glDisable(GL_ALPHA_TEST);
5351 checkGLcall("glDisable(GL_ALPHA_TEST)");
5354 LEAVE_GL();
5356 /* Leave the opengl state valid for blitting */
5357 device->blitter->unset_shader(context->gl_info);
5359 if (wined3d_settings.strict_draw_ordering
5360 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5361 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5362 wglFlush(); /* Flush to ensure ordering across contexts. */
5364 context_release(context);
5367 /* Do not call while under the GL lock. */
5368 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
5370 struct wined3d_device *device = s->resource.device;
5371 const struct blit_shader *blitter;
5373 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5374 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5375 if (!blitter)
5377 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5378 return WINED3DERR_INVALIDCALL;
5381 return blitter->color_fill(device, s, rect, color);
5384 /* Do not call while under the GL lock. */
5385 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5386 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5387 WINED3DTEXTUREFILTERTYPE Filter)
5389 struct wined3d_device *device = dst_surface->resource.device;
5390 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5391 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5393 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5394 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5395 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5397 /* Get the swapchain. One of the surfaces has to be a primary surface */
5398 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5400 WARN("Destination is in sysmem, rejecting gl blt\n");
5401 return WINED3DERR_INVALIDCALL;
5404 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5405 dstSwapchain = dst_surface->container.u.swapchain;
5407 if (src_surface)
5409 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5411 WARN("Src is in sysmem, rejecting gl blt\n");
5412 return WINED3DERR_INVALIDCALL;
5415 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5416 srcSwapchain = src_surface->container.u.swapchain;
5419 /* Early sort out of cases where no render target is used */
5420 if (!dstSwapchain && !srcSwapchain
5421 && src_surface != device->fb.render_targets[0]
5422 && dst_surface != device->fb.render_targets[0])
5424 TRACE("No surface is render target, not using hardware blit.\n");
5425 return WINED3DERR_INVALIDCALL;
5428 /* No destination color keying supported */
5429 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5431 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5432 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5433 return WINED3DERR_INVALIDCALL;
5436 if (dstSwapchain && dstSwapchain == srcSwapchain)
5438 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5439 return WINED3DERR_INVALIDCALL;
5442 if (dstSwapchain && srcSwapchain)
5444 FIXME("Implement hardware blit between two different swapchains\n");
5445 return WINED3DERR_INVALIDCALL;
5448 if (dstSwapchain)
5450 /* Handled with regular texture -> swapchain blit */
5451 if (src_surface == device->fb.render_targets[0])
5452 TRACE("Blit from active render target to a swapchain\n");
5454 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5456 FIXME("Implement blit from a swapchain to the active render target\n");
5457 return WINED3DERR_INVALIDCALL;
5460 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5462 /* Blit from render target to texture */
5463 BOOL stretchx;
5465 /* P8 read back is not implemented */
5466 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5467 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5469 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5470 return WINED3DERR_INVALIDCALL;
5473 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5475 TRACE("Color keying not supported by frame buffer to texture blit\n");
5476 return WINED3DERR_INVALIDCALL;
5477 /* Destination color key is checked above */
5480 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5481 stretchx = TRUE;
5482 else
5483 stretchx = FALSE;
5485 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5486 * flip the image nor scale it.
5488 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5489 * -> If the app wants a image width an unscaled width, copy it line per line
5490 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5491 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5492 * back buffer. This is slower than reading line per line, thus not used for flipping
5493 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5494 * pixel by pixel. */
5495 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5496 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5498 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5499 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, Filter);
5500 } else {
5501 TRACE("Using hardware stretching to flip / stretch the texture\n");
5502 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5505 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5507 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5508 dst_surface->resource.allocatedMemory = NULL;
5509 dst_surface->resource.heapMemory = NULL;
5511 else
5513 dst_surface->flags &= ~SFLAG_INSYSMEM;
5516 return WINED3D_OK;
5518 else if (src_surface)
5520 /* Blit from offscreen surface to render target */
5521 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
5522 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5524 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5526 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5527 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5528 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5530 FIXME("Unsupported blit operation falling back to software\n");
5531 return WINED3DERR_INVALIDCALL;
5534 /* Color keying: Check if we have to do a color keyed blt,
5535 * and if not check if a color key is activated.
5537 * Just modify the color keying parameters in the surface and restore them afterwards
5538 * The surface keeps track of the color key last used to load the opengl surface.
5539 * PreLoad will catch the change to the flags and color key and reload if necessary.
5541 if (flags & WINEDDBLT_KEYSRC)
5543 /* Use color key from surface */
5545 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5547 /* Use color key from DDBltFx */
5548 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5549 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
5551 else
5553 /* Do not use color key */
5554 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5557 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5558 src_surface, src_rect, dst_surface, dst_rect);
5560 /* Restore the color key parameters */
5561 src_surface->CKeyFlags = oldCKeyFlags;
5562 src_surface->src_blt_color_key = old_blt_key;
5564 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5566 return WINED3D_OK;
5569 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5570 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5571 return WINED3DERR_INVALIDCALL;
5574 /* GL locking is done by the caller */
5575 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5576 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5578 struct wined3d_device *device = surface->resource.device;
5579 const struct wined3d_gl_info *gl_info = context->gl_info;
5580 GLint compare_mode = GL_NONE;
5581 struct blt_info info;
5582 GLint old_binding = 0;
5583 RECT rect;
5585 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5587 glDisable(GL_CULL_FACE);
5588 glDisable(GL_BLEND);
5589 glDisable(GL_ALPHA_TEST);
5590 glDisable(GL_SCISSOR_TEST);
5591 glDisable(GL_STENCIL_TEST);
5592 glEnable(GL_DEPTH_TEST);
5593 glDepthFunc(GL_ALWAYS);
5594 glDepthMask(GL_TRUE);
5595 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5596 glViewport(x, y, w, h);
5598 SetRect(&rect, 0, h, w, 0);
5599 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5600 context_active_texture(context, context->gl_info, 0);
5601 glGetIntegerv(info.binding, &old_binding);
5602 glBindTexture(info.bind_target, texture);
5603 if (gl_info->supported[ARB_SHADOW])
5605 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5606 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5609 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5610 gl_info, info.tex_type, &surface->ds_current_size);
5612 glBegin(GL_TRIANGLE_STRIP);
5613 glTexCoord3fv(info.coords[0]);
5614 glVertex2f(-1.0f, -1.0f);
5615 glTexCoord3fv(info.coords[1]);
5616 glVertex2f(1.0f, -1.0f);
5617 glTexCoord3fv(info.coords[2]);
5618 glVertex2f(-1.0f, 1.0f);
5619 glTexCoord3fv(info.coords[3]);
5620 glVertex2f(1.0f, 1.0f);
5621 glEnd();
5623 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5624 glBindTexture(info.bind_target, old_binding);
5626 glPopAttrib();
5628 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5631 void surface_modify_ds_location(struct wined3d_surface *surface,
5632 DWORD location, UINT w, UINT h)
5634 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5636 if (location & ~(SFLAG_LOCATIONS | SFLAG_LOST))
5637 FIXME("Invalid location (%#x) specified.\n", location);
5639 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5640 || (!(surface->flags & SFLAG_INTEXTURE) && (location & SFLAG_INTEXTURE)))
5642 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5644 TRACE("Passing to container.\n");
5645 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5649 surface->ds_current_size.cx = w;
5650 surface->ds_current_size.cy = h;
5651 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_LOST);
5652 surface->flags |= location;
5655 /* Context activation is done by the caller. */
5656 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5658 struct wined3d_device *device = surface->resource.device;
5659 GLsizei w, h;
5661 TRACE("surface %p, new location %#x.\n", surface, location);
5663 /* TODO: Make this work for modes other than FBO */
5664 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5666 if (!(surface->flags & location))
5668 w = surface->ds_current_size.cx;
5669 h = surface->ds_current_size.cy;
5670 surface->ds_current_size.cx = 0;
5671 surface->ds_current_size.cy = 0;
5673 else
5675 w = surface->resource.width;
5676 h = surface->resource.height;
5679 if (surface->ds_current_size.cx == surface->resource.width
5680 && surface->ds_current_size.cy == surface->resource.height)
5682 TRACE("Location (%#x) is already up to date.\n", location);
5683 return;
5686 if (surface->current_renderbuffer)
5688 FIXME("Not supported with fixed up depth stencil.\n");
5689 return;
5692 if (surface->flags & SFLAG_LOST)
5694 TRACE("Surface was discarded, no need copy data.\n");
5695 switch (location)
5697 case SFLAG_INTEXTURE:
5698 surface_prepare_texture(surface, context, FALSE);
5699 break;
5700 case SFLAG_INRB_MULTISAMPLE:
5701 surface_prepare_rb(surface, context->gl_info, TRUE);
5702 break;
5703 case SFLAG_INDRAWABLE:
5704 /* Nothing to do */
5705 break;
5706 default:
5707 FIXME("Unhandled location %#x", location);
5709 surface->flags &= ~SFLAG_LOST;
5710 surface->flags |= location;
5711 surface->ds_current_size.cx = surface->resource.width;
5712 surface->ds_current_size.cy = surface->resource.height;
5713 return;
5716 if (!(surface->flags & SFLAG_LOCATIONS))
5718 FIXME("No up to date depth stencil location.\n");
5719 surface->flags |= location;
5720 surface->ds_current_size.cx = surface->resource.width;
5721 surface->ds_current_size.cy = surface->resource.height;
5722 return;
5725 if (location == SFLAG_INTEXTURE)
5727 GLint old_binding = 0;
5728 GLenum bind_target;
5730 /* The render target is allowed to be smaller than the depth/stencil
5731 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5732 * than the offscreen surface. Don't overwrite the offscreen surface
5733 * with undefined data. */
5734 w = min(w, context->swapchain->desc.backbuffer_width);
5735 h = min(h, context->swapchain->desc.backbuffer_height);
5737 TRACE("Copying onscreen depth buffer to depth texture.\n");
5739 ENTER_GL();
5741 if (!device->depth_blt_texture)
5743 glGenTextures(1, &device->depth_blt_texture);
5746 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5747 * directly on the FBO texture. That's because we need to flip. */
5748 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5749 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5750 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5752 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5753 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5755 else
5757 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5758 bind_target = GL_TEXTURE_2D;
5760 glBindTexture(bind_target, device->depth_blt_texture);
5761 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5762 * internal format, because the internal format might include stencil
5763 * data. In principle we should copy stencil data as well, but unless
5764 * the driver supports stencil export it's hard to do, and doesn't
5765 * seem to be needed in practice. If the hardware doesn't support
5766 * writing stencil data, the glCopyTexImage2D() call might trigger
5767 * software fallbacks. */
5768 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5769 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5770 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5771 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5772 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5773 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5774 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5775 glBindTexture(bind_target, old_binding);
5777 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5778 NULL, surface, SFLAG_INTEXTURE);
5779 context_set_draw_buffer(context, GL_NONE);
5780 glReadBuffer(GL_NONE);
5782 /* Do the actual blit */
5783 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5784 checkGLcall("depth_blt");
5786 context_invalidate_state(context, STATE_FRAMEBUFFER);
5788 LEAVE_GL();
5790 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5792 else if (location == SFLAG_INDRAWABLE)
5794 TRACE("Copying depth texture to onscreen depth buffer.\n");
5796 ENTER_GL();
5798 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5799 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5800 surface_depth_blt(surface, context, surface->texture_name,
5801 0, surface->pow2Height - h, w, h, surface->texture_target);
5802 checkGLcall("depth_blt");
5804 context_invalidate_state(context, STATE_FRAMEBUFFER);
5806 LEAVE_GL();
5808 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5810 else
5812 ERR("Invalid location (%#x) specified.\n", location);
5815 surface->flags |= location;
5816 surface->ds_current_size.cx = surface->resource.width;
5817 surface->ds_current_size.cy = surface->resource.height;
5820 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5822 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5823 struct wined3d_surface *overlay;
5825 TRACE("surface %p, location %s, persistent %#x.\n",
5826 surface, debug_surflocation(location), persistent);
5828 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5829 && !(surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5830 && (location & SFLAG_INDRAWABLE))
5831 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5833 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5834 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5835 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5837 if (persistent)
5839 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5840 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5842 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5844 TRACE("Passing to container.\n");
5845 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5848 surface->flags &= ~SFLAG_LOCATIONS;
5849 surface->flags |= location;
5851 /* Redraw emulated overlays, if any */
5852 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5854 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5856 surface_draw_overlay(overlay);
5860 else
5862 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5864 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5866 TRACE("Passing to container\n");
5867 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5870 surface->flags &= ~location;
5873 if (!(surface->flags & SFLAG_LOCATIONS))
5875 ERR("Surface %p does not have any up to date location.\n", surface);
5879 static DWORD resource_access_from_location(DWORD location)
5881 switch (location)
5883 case SFLAG_INSYSMEM:
5884 return WINED3D_RESOURCE_ACCESS_CPU;
5886 case SFLAG_INDRAWABLE:
5887 case SFLAG_INSRGBTEX:
5888 case SFLAG_INTEXTURE:
5889 case SFLAG_INRB_MULTISAMPLE:
5890 case SFLAG_INRB_RESOLVED:
5891 return WINED3D_RESOURCE_ACCESS_GPU;
5893 default:
5894 FIXME("Unhandled location %#x.\n", location);
5895 return 0;
5899 static void surface_load_sysmem(struct wined3d_surface *surface,
5900 const struct wined3d_gl_info *gl_info, const RECT *rect)
5902 surface_prepare_system_memory(surface);
5904 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5905 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5907 /* Download the surface to system memory. */
5908 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5910 struct wined3d_device *device = surface->resource.device;
5911 struct wined3d_context *context;
5913 /* TODO: Use already acquired context when possible. */
5914 context = context_acquire(device, NULL);
5916 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5917 surface_download_data(surface, gl_info);
5919 context_release(context);
5921 return;
5924 if (surface->flags & SFLAG_INDRAWABLE)
5926 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5927 wined3d_surface_get_pitch(surface));
5928 return;
5931 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5932 surface, surface->flags & SFLAG_LOCATIONS);
5935 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5936 const struct wined3d_gl_info *gl_info, const RECT *rect)
5938 struct wined3d_device *device = surface->resource.device;
5939 struct wined3d_format format;
5940 CONVERT_TYPES convert;
5941 UINT byte_count;
5942 BYTE *mem;
5944 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5946 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5947 return WINED3DERR_INVALIDCALL;
5950 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5951 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5953 if (surface->flags & SFLAG_INTEXTURE)
5955 RECT r;
5957 surface_get_rect(surface, rect, &r);
5958 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5960 return WINED3D_OK;
5963 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5965 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5966 * path through sysmem. */
5967 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5970 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5972 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5973 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5974 * called. */
5975 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5977 struct wined3d_context *context;
5979 TRACE("Removing the pbo attached to surface %p.\n", surface);
5981 /* TODO: Use already acquired context when possible. */
5982 context = context_acquire(device, NULL);
5984 surface_remove_pbo(surface, gl_info);
5986 context_release(context);
5989 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5991 UINT height = surface->resource.height;
5992 UINT width = surface->resource.width;
5993 UINT src_pitch, dst_pitch;
5995 byte_count = format.conv_byte_count;
5996 src_pitch = wined3d_surface_get_pitch(surface);
5998 /* Stick to the alignment for the converted surface too, makes it
5999 * easier to load the surface. */
6000 dst_pitch = width * byte_count;
6001 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6003 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6005 ERR("Out of memory (%u).\n", dst_pitch * height);
6006 return E_OUTOFMEMORY;
6009 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
6010 src_pitch, width, height, dst_pitch, convert, surface);
6012 surface->flags |= SFLAG_CONVERTED;
6014 else
6016 surface->flags &= ~SFLAG_CONVERTED;
6017 mem = surface->resource.allocatedMemory;
6018 byte_count = format.byte_count;
6021 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
6023 /* Don't delete PBO memory. */
6024 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6025 HeapFree(GetProcessHeap(), 0, mem);
6027 return WINED3D_OK;
6030 static HRESULT surface_load_texture(struct wined3d_surface *surface,
6031 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
6033 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
6034 struct wined3d_device *device = surface->resource.device;
6035 struct wined3d_context *context;
6036 UINT width, src_pitch, dst_pitch;
6037 struct wined3d_bo_address data;
6038 struct wined3d_format format;
6039 POINT dst_point = {0, 0};
6040 CONVERT_TYPES convert;
6041 BYTE *mem;
6043 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
6044 && surface_is_offscreen(surface)
6045 && (surface->flags & SFLAG_INDRAWABLE))
6047 surface_load_fb_texture(surface, srgb);
6049 return WINED3D_OK;
6052 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6053 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
6054 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6055 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6056 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6058 if (srgb)
6059 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
6060 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6061 else
6062 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
6063 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6065 return WINED3D_OK;
6068 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
6069 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
6070 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6071 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6072 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6074 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
6075 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
6076 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6078 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, src_location,
6079 &rect, surface, dst_location, &rect);
6081 return WINED3D_OK;
6084 /* Upload from system memory */
6086 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6087 TRUE /* We will use textures */, &format, &convert);
6089 if (srgb)
6091 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6093 /* Performance warning... */
6094 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6095 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6098 else
6100 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6102 /* Performance warning... */
6103 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6104 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6108 if (!(surface->flags & SFLAG_INSYSMEM))
6110 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6111 /* Lets hope we get it from somewhere... */
6112 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6115 /* TODO: Use already acquired context when possible. */
6116 context = context_acquire(device, NULL);
6118 surface_prepare_texture(surface, context, srgb);
6119 surface_bind_and_dirtify(surface, context, srgb);
6121 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6123 surface->flags |= SFLAG_GLCKEY;
6124 surface->gl_color_key = surface->src_blt_color_key;
6126 else surface->flags &= ~SFLAG_GLCKEY;
6128 width = surface->resource.width;
6129 src_pitch = wined3d_surface_get_pitch(surface);
6131 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6132 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6133 * called. */
6134 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6136 TRACE("Removing the pbo attached to surface %p.\n", surface);
6137 surface_remove_pbo(surface, gl_info);
6140 if (format.convert)
6142 /* This code is entered for texture formats which need a fixup. */
6143 UINT height = surface->resource.height;
6145 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6146 dst_pitch = width * format.conv_byte_count;
6147 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6149 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6151 ERR("Out of memory (%u).\n", dst_pitch * height);
6152 context_release(context);
6153 return E_OUTOFMEMORY;
6155 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6156 format.byte_count = format.conv_byte_count;
6157 src_pitch = dst_pitch;
6159 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6161 /* This code is only entered for color keying fixups */
6162 UINT height = surface->resource.height;
6164 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6165 dst_pitch = width * format.conv_byte_count;
6166 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6168 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6170 ERR("Out of memory (%u).\n", dst_pitch * height);
6171 context_release(context);
6172 return E_OUTOFMEMORY;
6174 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6175 width, height, dst_pitch, convert, surface);
6176 format.byte_count = format.conv_byte_count;
6177 src_pitch = dst_pitch;
6179 else
6181 mem = surface->resource.allocatedMemory;
6184 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6185 data.addr = mem;
6186 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
6188 context_release(context);
6190 /* Don't delete PBO memory. */
6191 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6192 HeapFree(GetProcessHeap(), 0, mem);
6194 return WINED3D_OK;
6197 static void surface_multisample_resolve(struct wined3d_surface *surface)
6199 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6201 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6202 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6204 surface_blt_fbo(surface->resource.device, WINED3DTEXF_POINT,
6205 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6208 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6210 struct wined3d_device *device = surface->resource.device;
6211 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6212 HRESULT hr;
6214 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6216 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6218 if (location == SFLAG_INTEXTURE)
6220 struct wined3d_context *context = context_acquire(device, NULL);
6221 surface_load_ds_location(surface, context, location);
6222 context_release(context);
6223 return WINED3D_OK;
6225 else
6227 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6228 return WINED3DERR_INVALIDCALL;
6232 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6233 location = SFLAG_INTEXTURE;
6235 if (surface->flags & location)
6237 TRACE("Location already up to date.\n");
6239 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
6240 && surface_need_pbo(surface, gl_info))
6241 surface_load_pbo(surface, gl_info);
6243 return WINED3D_OK;
6246 if (WARN_ON(d3d_surface))
6248 DWORD required_access = resource_access_from_location(location);
6249 if ((surface->resource.access_flags & required_access) != required_access)
6250 WARN("Operation requires %#x access, but surface only has %#x.\n",
6251 required_access, surface->resource.access_flags);
6254 if (!(surface->flags & SFLAG_LOCATIONS))
6256 ERR("Surface %p does not have any up to date location.\n", surface);
6257 surface->flags |= SFLAG_LOST;
6258 return WINED3DERR_DEVICELOST;
6261 switch (location)
6263 case SFLAG_INSYSMEM:
6264 surface_load_sysmem(surface, gl_info, rect);
6265 break;
6267 case SFLAG_INDRAWABLE:
6268 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6269 return hr;
6270 break;
6272 case SFLAG_INRB_RESOLVED:
6273 surface_multisample_resolve(surface);
6274 break;
6276 case SFLAG_INTEXTURE:
6277 case SFLAG_INSRGBTEX:
6278 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6279 return hr;
6280 break;
6282 default:
6283 ERR("Don't know how to handle location %#x.\n", location);
6284 break;
6287 if (!rect)
6289 surface->flags |= location;
6291 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6292 surface_evict_sysmem(surface);
6295 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6296 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6298 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6301 return WINED3D_OK;
6304 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6306 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6308 /* Not on a swapchain - must be offscreen */
6309 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6311 /* The front buffer is always onscreen */
6312 if (surface == swapchain->front_buffer) return FALSE;
6314 /* If the swapchain is rendered to an FBO, the backbuffer is
6315 * offscreen, otherwise onscreen */
6316 return swapchain->render_to_fbo;
6319 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6320 /* Context activation is done by the caller. */
6321 static void ffp_blit_free(struct wined3d_device *device) { }
6323 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6324 /* Context activation is done by the caller. */
6325 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6327 BYTE table[256][4];
6328 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6330 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6332 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6333 ENTER_GL();
6334 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6335 LEAVE_GL();
6338 /* Context activation is done by the caller. */
6339 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6341 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6343 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6344 * else the surface is converted in software at upload time in LoadLocation.
6346 if (!(surface->flags & SFLAG_CONVERTED) && fixup == COMPLEX_FIXUP_P8
6347 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6348 ffp_blit_p8_upload_palette(surface, context->gl_info);
6350 ENTER_GL();
6351 glEnable(surface->texture_target);
6352 checkGLcall("glEnable(surface->texture_target)");
6353 LEAVE_GL();
6354 return WINED3D_OK;
6357 /* Context activation is done by the caller. */
6358 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6360 ENTER_GL();
6361 glDisable(GL_TEXTURE_2D);
6362 checkGLcall("glDisable(GL_TEXTURE_2D)");
6363 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6365 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6366 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6368 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6370 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6371 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6373 LEAVE_GL();
6376 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6377 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6378 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6380 enum complex_fixup src_fixup;
6382 switch (blit_op)
6384 case WINED3D_BLIT_OP_COLOR_BLIT:
6385 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
6386 return FALSE;
6388 src_fixup = get_complex_fixup(src_format->color_fixup);
6389 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6391 TRACE("Checking support for fixup:\n");
6392 dump_color_fixup_desc(src_format->color_fixup);
6395 if (!is_identity_fixup(dst_format->color_fixup))
6397 TRACE("Destination fixups are not supported\n");
6398 return FALSE;
6401 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6403 TRACE("P8 fixup supported\n");
6404 return TRUE;
6407 /* We only support identity conversions. */
6408 if (is_identity_fixup(src_format->color_fixup))
6410 TRACE("[OK]\n");
6411 return TRUE;
6414 TRACE("[FAILED]\n");
6415 return FALSE;
6417 case WINED3D_BLIT_OP_COLOR_FILL:
6418 if (dst_pool == WINED3DPOOL_SYSTEMMEM)
6419 return FALSE;
6421 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6423 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6424 return FALSE;
6426 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6428 TRACE("Color fill not supported\n");
6429 return FALSE;
6432 /* FIXME: We should reject color fills on formats with fixups,
6433 * but this would break P8 color fills for example. */
6435 return TRUE;
6437 case WINED3D_BLIT_OP_DEPTH_FILL:
6438 return TRUE;
6440 default:
6441 TRACE("Unsupported blit_op=%d\n", blit_op);
6442 return FALSE;
6446 /* Do not call while under the GL lock. */
6447 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6448 const RECT *dst_rect, const struct wined3d_color *color)
6450 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6451 struct wined3d_fb_state fb = {&dst_surface, NULL};
6453 return device_clear_render_targets(device, 1, &fb,
6454 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6457 /* Do not call while under the GL lock. */
6458 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6459 struct wined3d_surface *surface, const RECT *rect, float depth)
6461 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6462 struct wined3d_fb_state fb = {NULL, surface};
6464 return device_clear_render_targets(device, 0, &fb,
6465 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6468 const struct blit_shader ffp_blit = {
6469 ffp_blit_alloc,
6470 ffp_blit_free,
6471 ffp_blit_set,
6472 ffp_blit_unset,
6473 ffp_blit_supported,
6474 ffp_blit_color_fill,
6475 ffp_blit_depth_fill,
6478 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6480 return WINED3D_OK;
6483 /* Context activation is done by the caller. */
6484 static void cpu_blit_free(struct wined3d_device *device)
6488 /* Context activation is done by the caller. */
6489 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6491 return WINED3D_OK;
6494 /* Context activation is done by the caller. */
6495 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6499 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6500 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6501 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6503 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6505 return TRUE;
6508 return FALSE;
6511 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
6512 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
6513 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
6515 UINT row_block_count;
6516 const BYTE *src_row;
6517 BYTE *dst_row;
6518 UINT x, y;
6520 src_row = src_data;
6521 dst_row = dst_data;
6523 row_block_count = (update_w + format->block_width - 1) / format->block_width;
6525 if (!flags)
6527 for (y = 0; y < update_h; y += format->block_height)
6529 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
6530 src_row += src_pitch;
6531 dst_row += dst_pitch;
6534 return WINED3D_OK;
6537 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
6539 src_row += (((update_h / format->block_height) - 1) * src_pitch);
6541 switch (format->id)
6543 case WINED3DFMT_DXT1:
6544 for (y = 0; y < update_h; y += format->block_height)
6546 struct block
6548 WORD color[2];
6549 BYTE control_row[4];
6552 const struct block *s = (const struct block *)src_row;
6553 struct block *d = (struct block *)dst_row;
6555 for (x = 0; x < row_block_count; ++x)
6557 d[x].color[0] = s[x].color[0];
6558 d[x].color[1] = s[x].color[1];
6559 d[x].control_row[0] = s[x].control_row[3];
6560 d[x].control_row[1] = s[x].control_row[2];
6561 d[x].control_row[2] = s[x].control_row[1];
6562 d[x].control_row[3] = s[x].control_row[0];
6564 src_row -= src_pitch;
6565 dst_row += dst_pitch;
6567 return WINED3D_OK;
6569 case WINED3DFMT_DXT3:
6570 for (y = 0; y < update_h; y += format->block_height)
6572 struct block
6574 WORD alpha_row[4];
6575 WORD color[2];
6576 BYTE control_row[4];
6579 const struct block *s = (const struct block *)src_row;
6580 struct block *d = (struct block *)dst_row;
6582 for (x = 0; x < row_block_count; ++x)
6584 d[x].alpha_row[0] = s[x].alpha_row[3];
6585 d[x].alpha_row[1] = s[x].alpha_row[2];
6586 d[x].alpha_row[2] = s[x].alpha_row[1];
6587 d[x].alpha_row[3] = s[x].alpha_row[0];
6588 d[x].color[0] = s[x].color[0];
6589 d[x].color[1] = s[x].color[1];
6590 d[x].control_row[0] = s[x].control_row[3];
6591 d[x].control_row[1] = s[x].control_row[2];
6592 d[x].control_row[2] = s[x].control_row[1];
6593 d[x].control_row[3] = s[x].control_row[0];
6595 src_row -= src_pitch;
6596 dst_row += dst_pitch;
6598 return WINED3D_OK;
6600 default:
6601 FIXME("Compressed flip not implemented for format %s.\n",
6602 debug_d3dformat(format->id));
6603 return E_NOTIMPL;
6607 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
6608 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
6610 return E_NOTIMPL;
6613 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6614 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6615 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6617 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6618 const struct wined3d_format *src_format, *dst_format;
6619 struct wined3d_surface *orig_src = src_surface;
6620 struct wined3d_mapped_rect dst_map, src_map;
6621 HRESULT hr = WINED3D_OK;
6622 const BYTE *sbuf;
6623 RECT xdst,xsrc;
6624 BYTE *dbuf;
6625 int x, y;
6627 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6628 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6629 flags, fx, debug_d3dtexturefiltertype(filter));
6631 xsrc = *src_rect;
6633 if (!src_surface)
6635 RECT full_rect;
6637 full_rect.left = 0;
6638 full_rect.top = 0;
6639 full_rect.right = dst_surface->resource.width;
6640 full_rect.bottom = dst_surface->resource.height;
6641 IntersectRect(&xdst, &full_rect, dst_rect);
6643 else
6645 BOOL clip_horiz, clip_vert;
6647 xdst = *dst_rect;
6648 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6649 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6651 if (clip_vert || clip_horiz)
6653 /* Now check if this is a special case or not... */
6654 if ((flags & WINEDDBLT_DDFX)
6655 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6656 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6658 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6659 return WINED3D_OK;
6662 if (clip_horiz)
6664 if (xdst.left < 0)
6666 xsrc.left -= xdst.left;
6667 xdst.left = 0;
6669 if (xdst.right > dst_surface->resource.width)
6671 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6672 xdst.right = (int)dst_surface->resource.width;
6676 if (clip_vert)
6678 if (xdst.top < 0)
6680 xsrc.top -= xdst.top;
6681 xdst.top = 0;
6683 if (xdst.bottom > dst_surface->resource.height)
6685 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6686 xdst.bottom = (int)dst_surface->resource.height;
6690 /* And check if after clipping something is still to be done... */
6691 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6692 || (xdst.left >= (int)dst_surface->resource.width)
6693 || (xdst.top >= (int)dst_surface->resource.height)
6694 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6695 || (xsrc.left >= (int)src_surface->resource.width)
6696 || (xsrc.top >= (int)src_surface->resource.height))
6698 TRACE("Nothing to be done after clipping.\n");
6699 return WINED3D_OK;
6704 if (src_surface == dst_surface)
6706 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6707 src_map = dst_map;
6708 src_format = dst_surface->resource.format;
6709 dst_format = src_format;
6711 else
6713 dst_format = dst_surface->resource.format;
6714 if (src_surface)
6716 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6718 src_surface = surface_convert_format(src_surface, dst_format->id);
6719 if (!src_surface)
6721 /* The conv function writes a FIXME */
6722 WARN("Cannot convert source surface format to dest format.\n");
6723 goto release;
6726 wined3d_surface_map(src_surface, &src_map, NULL, WINED3DLOCK_READONLY);
6727 src_format = src_surface->resource.format;
6729 else
6731 src_format = dst_format;
6733 if (dst_rect)
6734 wined3d_surface_map(dst_surface, &dst_map, &xdst, 0);
6735 else
6736 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6739 bpp = dst_surface->resource.format->byte_count;
6740 srcheight = xsrc.bottom - xsrc.top;
6741 srcwidth = xsrc.right - xsrc.left;
6742 dstheight = xdst.bottom - xdst.top;
6743 dstwidth = xdst.right - xdst.left;
6744 width = (xdst.right - xdst.left) * bpp;
6746 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
6748 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6750 if (src_surface == dst_surface)
6752 FIXME("Only plain blits supported on compressed surfaces.\n");
6753 hr = E_NOTIMPL;
6754 goto release;
6757 if (srcheight != dstheight || srcwidth != dstwidth)
6759 WARN("Stretching not supported on compressed surfaces.\n");
6760 hr = WINED3DERR_INVALIDCALL;
6761 goto release;
6764 if (srcwidth & (src_format->block_width - 1) || srcheight & (src_format->block_height - 1))
6766 WARN("Rectangle not block-aligned.\n");
6767 hr = WINED3DERR_INVALIDCALL;
6768 goto release;
6771 hr = surface_cpu_blt_compressed(src_map.data, dst_map.data,
6772 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
6773 src_format, flags, fx);
6774 goto release;
6777 if (dst_rect && src_surface != dst_surface)
6778 dbuf = dst_map.data;
6779 else
6780 dbuf = (BYTE *)dst_map.data + (xdst.top * dst_map.row_pitch) + (xdst.left * bpp);
6782 /* First, all the 'source-less' blits */
6783 if (flags & WINEDDBLT_COLORFILL)
6785 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
6786 flags &= ~WINEDDBLT_COLORFILL;
6789 if (flags & WINEDDBLT_DEPTHFILL)
6791 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6793 if (flags & WINEDDBLT_ROP)
6795 /* Catch some degenerate cases here. */
6796 switch (fx->dwROP)
6798 case BLACKNESS:
6799 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
6800 break;
6801 case 0xAA0029: /* No-op */
6802 break;
6803 case WHITENESS:
6804 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
6805 break;
6806 case SRCCOPY: /* Well, we do that below? */
6807 break;
6808 default:
6809 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6810 goto error;
6812 flags &= ~WINEDDBLT_ROP;
6814 if (flags & WINEDDBLT_DDROPS)
6816 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6818 /* Now the 'with source' blits. */
6819 if (src_surface)
6821 const BYTE *sbase;
6822 int sx, xinc, sy, yinc;
6824 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6825 goto release;
6827 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6828 && (srcwidth != dstwidth || srcheight != dstheight))
6830 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6831 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6834 sbase = (BYTE *)src_map.data + (xsrc.top * src_map.row_pitch) + xsrc.left * bpp;
6835 xinc = (srcwidth << 16) / dstwidth;
6836 yinc = (srcheight << 16) / dstheight;
6838 if (!flags)
6840 /* No effects, we can cheat here. */
6841 if (dstwidth == srcwidth)
6843 if (dstheight == srcheight)
6845 /* No stretching in either direction. This needs to be as
6846 * fast as possible. */
6847 sbuf = sbase;
6849 /* Check for overlapping surfaces. */
6850 if (src_surface != dst_surface || xdst.top < xsrc.top
6851 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6853 /* No overlap, or dst above src, so copy from top downwards. */
6854 for (y = 0; y < dstheight; ++y)
6856 memcpy(dbuf, sbuf, width);
6857 sbuf += src_map.row_pitch;
6858 dbuf += dst_map.row_pitch;
6861 else if (xdst.top > xsrc.top)
6863 /* Copy from bottom upwards. */
6864 sbuf += src_map.row_pitch * dstheight;
6865 dbuf += dst_map.row_pitch * dstheight;
6866 for (y = 0; y < dstheight; ++y)
6868 sbuf -= src_map.row_pitch;
6869 dbuf -= dst_map.row_pitch;
6870 memcpy(dbuf, sbuf, width);
6873 else
6875 /* Src and dst overlapping on the same line, use memmove. */
6876 for (y = 0; y < dstheight; ++y)
6878 memmove(dbuf, sbuf, width);
6879 sbuf += src_map.row_pitch;
6880 dbuf += dst_map.row_pitch;
6884 else
6886 /* Stretching in y direction only. */
6887 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6889 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6890 memcpy(dbuf, sbuf, width);
6891 dbuf += dst_map.row_pitch;
6895 else
6897 /* Stretching in X direction. */
6898 int last_sy = -1;
6899 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6901 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6903 if ((sy >> 16) == (last_sy >> 16))
6905 /* This source row is the same as last source row -
6906 * Copy the already stretched row. */
6907 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
6909 else
6911 #define STRETCH_ROW(type) \
6912 do { \
6913 const type *s = (const type *)sbuf; \
6914 type *d = (type *)dbuf; \
6915 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6916 d[x] = s[sx >> 16]; \
6917 } while(0)
6919 switch(bpp)
6921 case 1:
6922 STRETCH_ROW(BYTE);
6923 break;
6924 case 2:
6925 STRETCH_ROW(WORD);
6926 break;
6927 case 4:
6928 STRETCH_ROW(DWORD);
6929 break;
6930 case 3:
6932 const BYTE *s;
6933 BYTE *d = dbuf;
6934 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6936 DWORD pixel;
6938 s = sbuf + 3 * (sx >> 16);
6939 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6940 d[0] = (pixel ) & 0xff;
6941 d[1] = (pixel >> 8) & 0xff;
6942 d[2] = (pixel >> 16) & 0xff;
6943 d += 3;
6945 break;
6947 default:
6948 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6949 hr = WINED3DERR_NOTAVAILABLE;
6950 goto error;
6952 #undef STRETCH_ROW
6954 dbuf += dst_map.row_pitch;
6955 last_sy = sy;
6959 else
6961 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
6962 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6963 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6964 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6966 /* The color keying flags are checked for correctness in ddraw */
6967 if (flags & WINEDDBLT_KEYSRC)
6969 keylow = src_surface->src_blt_color_key.color_space_low_value;
6970 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
6972 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6974 keylow = fx->ddckSrcColorkey.color_space_low_value;
6975 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
6978 if (flags & WINEDDBLT_KEYDEST)
6980 /* Destination color keys are taken from the source surface! */
6981 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
6982 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
6984 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6986 destkeylow = fx->ddckDestColorkey.color_space_low_value;
6987 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
6990 if (bpp == 1)
6992 keymask = 0xff;
6994 else
6996 keymask = src_format->red_mask
6997 | src_format->green_mask
6998 | src_format->blue_mask;
7000 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
7003 if (flags & WINEDDBLT_DDFX)
7005 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
7006 LONG tmpxy;
7007 dTopLeft = dbuf;
7008 dTopRight = dbuf + ((dstwidth - 1) * bpp);
7009 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
7010 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
7012 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
7014 /* I don't think we need to do anything about this flag */
7015 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
7017 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
7019 tmp = dTopRight;
7020 dTopRight = dTopLeft;
7021 dTopLeft = tmp;
7022 tmp = dBottomRight;
7023 dBottomRight = dBottomLeft;
7024 dBottomLeft = tmp;
7025 dstxinc = dstxinc * -1;
7027 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
7029 tmp = dTopLeft;
7030 dTopLeft = dBottomLeft;
7031 dBottomLeft = tmp;
7032 tmp = dTopRight;
7033 dTopRight = dBottomRight;
7034 dBottomRight = tmp;
7035 dstyinc = dstyinc * -1;
7037 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
7039 /* I don't think we need to do anything about this flag */
7040 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
7042 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
7044 tmp = dBottomRight;
7045 dBottomRight = dTopLeft;
7046 dTopLeft = tmp;
7047 tmp = dBottomLeft;
7048 dBottomLeft = dTopRight;
7049 dTopRight = tmp;
7050 dstxinc = dstxinc * -1;
7051 dstyinc = dstyinc * -1;
7053 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
7055 tmp = dTopLeft;
7056 dTopLeft = dBottomLeft;
7057 dBottomLeft = dBottomRight;
7058 dBottomRight = dTopRight;
7059 dTopRight = tmp;
7060 tmpxy = dstxinc;
7061 dstxinc = dstyinc;
7062 dstyinc = tmpxy;
7063 dstxinc = dstxinc * -1;
7065 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
7067 tmp = dTopLeft;
7068 dTopLeft = dTopRight;
7069 dTopRight = dBottomRight;
7070 dBottomRight = dBottomLeft;
7071 dBottomLeft = tmp;
7072 tmpxy = dstxinc;
7073 dstxinc = dstyinc;
7074 dstyinc = tmpxy;
7075 dstyinc = dstyinc * -1;
7077 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
7079 /* I don't think we need to do anything about this flag */
7080 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
7082 dbuf = dTopLeft;
7083 flags &= ~(WINEDDBLT_DDFX);
7086 #define COPY_COLORKEY_FX(type) \
7087 do { \
7088 const type *s; \
7089 type *d = (type *)dbuf, *dx, tmp; \
7090 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
7092 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
7093 dx = d; \
7094 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
7096 tmp = s[sx >> 16]; \
7097 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
7098 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
7100 dx[0] = tmp; \
7102 dx = (type *)(((BYTE *)dx) + dstxinc); \
7104 d = (type *)(((BYTE *)d) + dstyinc); \
7106 } while(0)
7108 switch (bpp)
7110 case 1:
7111 COPY_COLORKEY_FX(BYTE);
7112 break;
7113 case 2:
7114 COPY_COLORKEY_FX(WORD);
7115 break;
7116 case 4:
7117 COPY_COLORKEY_FX(DWORD);
7118 break;
7119 case 3:
7121 const BYTE *s;
7122 BYTE *d = dbuf, *dx;
7123 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
7125 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
7126 dx = d;
7127 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
7129 DWORD pixel, dpixel = 0;
7130 s = sbuf + 3 * (sx>>16);
7131 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
7132 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
7133 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
7134 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
7136 dx[0] = (pixel ) & 0xff;
7137 dx[1] = (pixel >> 8) & 0xff;
7138 dx[2] = (pixel >> 16) & 0xff;
7140 dx += dstxinc;
7142 d += dstyinc;
7144 break;
7146 default:
7147 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7148 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7149 hr = WINED3DERR_NOTAVAILABLE;
7150 goto error;
7151 #undef COPY_COLORKEY_FX
7156 error:
7157 if (flags && FIXME_ON(d3d_surface))
7159 FIXME("\tUnsupported flags: %#x.\n", flags);
7162 release:
7163 wined3d_surface_unmap(dst_surface);
7164 if (src_surface && src_surface != dst_surface)
7165 wined3d_surface_unmap(src_surface);
7166 /* Release the converted surface, if any. */
7167 if (src_surface && src_surface != orig_src)
7168 wined3d_surface_decref(src_surface);
7170 return hr;
7173 /* Do not call while under the GL lock. */
7174 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7175 const RECT *dst_rect, const struct wined3d_color *color)
7177 static const RECT src_rect;
7178 WINEDDBLTFX BltFx;
7180 memset(&BltFx, 0, sizeof(BltFx));
7181 BltFx.dwSize = sizeof(BltFx);
7182 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7183 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7184 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7187 /* Do not call while under the GL lock. */
7188 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7189 struct wined3d_surface *surface, const RECT *rect, float depth)
7191 FIXME("Depth filling not implemented by cpu_blit.\n");
7192 return WINED3DERR_INVALIDCALL;
7195 const struct blit_shader cpu_blit = {
7196 cpu_blit_alloc,
7197 cpu_blit_free,
7198 cpu_blit_set,
7199 cpu_blit_unset,
7200 cpu_blit_supported,
7201 cpu_blit_color_fill,
7202 cpu_blit_depth_fill,
7205 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7206 UINT width, UINT height, UINT level, enum wined3d_multisample_type multisample_type,
7207 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7208 WINED3DPOOL pool, DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops)
7210 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7211 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7212 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
7213 unsigned int resource_size;
7214 HRESULT hr;
7216 if (multisample_quality > 0)
7218 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7219 multisample_quality = 0;
7222 /* Quick lockable sanity check.
7223 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7224 * this function is too deep to need to care about things like this.
7225 * Levels need to be checked too, since they all affect what can be done. */
7226 switch (pool)
7228 case WINED3DPOOL_SCRATCH:
7229 if (!lockable)
7231 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7232 "which are mutually exclusive, setting lockable to TRUE.\n");
7233 lockable = TRUE;
7235 break;
7237 case WINED3DPOOL_SYSTEMMEM:
7238 if (!lockable)
7239 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7240 break;
7242 case WINED3DPOOL_MANAGED:
7243 if (usage & WINED3DUSAGE_DYNAMIC)
7244 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7245 break;
7247 case WINED3DPOOL_DEFAULT:
7248 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7249 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7250 break;
7252 default:
7253 FIXME("Unknown pool %#x.\n", pool);
7254 break;
7257 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7258 FIXME("Trying to create a render target that isn't in the default pool.\n");
7260 /* FIXME: Check that the format is supported by the device. */
7262 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7263 if (!resource_size)
7264 return WINED3DERR_INVALIDCALL;
7266 surface->surface_type = surface_type;
7268 switch (surface_type)
7270 case SURFACE_OPENGL:
7271 surface->surface_ops = &surface_ops;
7272 break;
7274 case SURFACE_GDI:
7275 surface->surface_ops = &gdi_surface_ops;
7276 break;
7278 default:
7279 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7280 return WINED3DERR_INVALIDCALL;
7283 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7284 multisample_type, multisample_quality, usage, pool, width, height, 1,
7285 resource_size, parent, parent_ops, &surface_resource_ops);
7286 if (FAILED(hr))
7288 WARN("Failed to initialize resource, returning %#x.\n", hr);
7289 return hr;
7292 /* "Standalone" surface. */
7293 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7295 surface->texture_level = level;
7296 list_init(&surface->overlays);
7298 /* Flags */
7299 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7300 if (flags & WINED3D_SURFACE_DISCARD)
7301 surface->flags |= SFLAG_DISCARD;
7302 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
7303 surface->flags |= SFLAG_PIN_SYSMEM;
7304 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7305 surface->flags |= SFLAG_LOCKABLE;
7306 /* I'm not sure if this qualifies as a hack or as an optimization. It
7307 * seems reasonable to assume that lockable render targets will get
7308 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7309 * creation. However, the other reason we want to do this is that several
7310 * ddraw applications access surface memory while the surface isn't
7311 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7312 * future locks prevents these from crashing. */
7313 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7314 surface->flags |= SFLAG_DYNLOCK;
7316 /* Mark the texture as dirty so that it gets loaded first time around. */
7317 surface_add_dirty_rect(surface, NULL);
7318 list_init(&surface->renderbuffers);
7320 TRACE("surface %p, memory %p, size %u\n",
7321 surface, surface->resource.allocatedMemory, surface->resource.size);
7323 /* Call the private setup routine */
7324 hr = surface->surface_ops->surface_private_setup(surface);
7325 if (FAILED(hr))
7327 ERR("Private setup failed, returning %#x\n", hr);
7328 surface_cleanup(surface);
7329 return hr;
7332 /* Similar to lockable rendertargets above, creating the DIB section
7333 * during surface initialization prevents the sysmem pointer from changing
7334 * after a wined3d_surface_getdc() call. */
7335 if ((usage & WINED3DUSAGE_OWNDC) && !surface->hDC
7336 && SUCCEEDED(surface_create_dib_section(surface)))
7338 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
7339 surface->resource.heapMemory = NULL;
7340 surface->resource.allocatedMemory = surface->dib.bitmap_data;
7343 return hr;
7346 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7347 enum wined3d_format_id format_id, UINT level, DWORD usage, WINED3DPOOL pool,
7348 enum wined3d_multisample_type multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7349 DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7351 struct wined3d_surface *object;
7352 HRESULT hr;
7354 TRACE("device %p, width %u, height %u, format %s, level %u\n",
7355 device, width, height, debug_d3dformat(format_id), level);
7356 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7357 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7358 TRACE("surface_type %#x, flags %#x, parent %p, parent_ops %p.\n", surface_type, flags, parent, parent_ops);
7360 if (surface_type == SURFACE_OPENGL && !device->adapter)
7362 ERR("OpenGL surfaces are not available without OpenGL.\n");
7363 return WINED3DERR_NOTAVAILABLE;
7366 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7367 if (!object)
7369 ERR("Failed to allocate surface memory.\n");
7370 return WINED3DERR_OUTOFVIDEOMEMORY;
7373 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level,
7374 multisample_type, multisample_quality, device, usage, format_id, pool, flags, parent, parent_ops);
7375 if (FAILED(hr))
7377 WARN("Failed to initialize surface, returning %#x.\n", hr);
7378 HeapFree(GetProcessHeap(), 0, object);
7379 return hr;
7382 TRACE("Created surface %p.\n", object);
7383 *surface = object;
7385 return hr;