wined3d: Don't load the draw_binding location in surface_unmap() unless the surface...
[wine.git] / dlls / wined3d / surface.c
blob21bd0885d963df1e1b902fd632cdcd5347597f18
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2011 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d);
36 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
37 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
38 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter);
39 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
40 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *fx,
41 enum wined3d_texture_filter_type filter);
43 static void surface_cleanup(struct wined3d_surface *surface)
45 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->texture_name || (surface->flags & SFLAG_PBO)
50 || surface->rb_multisample || surface->rb_resolved
51 || !list_empty(&surface->renderbuffers))
53 struct wined3d_renderbuffer_entry *entry, *entry2;
54 const struct wined3d_gl_info *gl_info;
55 struct wined3d_context *context;
57 context = context_acquire(surface->resource.device, NULL);
58 gl_info = context->gl_info;
60 ENTER_GL();
62 if (surface->texture_name)
64 TRACE("Deleting texture %u.\n", surface->texture_name);
65 glDeleteTextures(1, &surface->texture_name);
68 if (surface->flags & SFLAG_PBO)
70 TRACE("Deleting PBO %u.\n", surface->pbo);
71 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
74 if (surface->rb_multisample)
76 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
77 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
80 if (surface->rb_resolved)
82 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
83 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
86 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
88 TRACE("Deleting renderbuffer %u.\n", entry->id);
89 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
90 HeapFree(GetProcessHeap(), 0, entry);
93 LEAVE_GL();
95 context_release(context);
98 if (surface->flags & SFLAG_DIBSECTION)
100 DeleteDC(surface->hDC);
101 DeleteObject(surface->dib.DIBsection);
102 surface->dib.bitmap_data = NULL;
103 surface->resource.allocatedMemory = NULL;
106 if (surface->flags & SFLAG_USERPTR)
107 wined3d_surface_set_mem(surface, NULL);
108 if (surface->overlay_dest)
109 list_remove(&surface->overlay_entry);
111 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
113 list_remove(&overlay->overlay_entry);
114 overlay->overlay_dest = NULL;
117 resource_cleanup(&surface->resource);
120 void surface_update_draw_binding(struct wined3d_surface *surface)
122 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
123 surface->draw_binding = SFLAG_INDRAWABLE;
124 else if (surface->resource.multisample_type)
125 surface->draw_binding = SFLAG_INRB_MULTISAMPLE;
126 else
127 surface->draw_binding = SFLAG_INTEXTURE;
130 void surface_set_container(struct wined3d_surface *surface, enum wined3d_container_type type, void *container)
132 TRACE("surface %p, container %p.\n", surface, container);
134 if (!container && type != WINED3D_CONTAINER_NONE)
135 ERR("Setting NULL container of type %#x.\n", type);
137 if (type == WINED3D_CONTAINER_SWAPCHAIN)
139 surface->get_drawable_size = get_drawable_size_swapchain;
141 else
143 switch (wined3d_settings.offscreen_rendering_mode)
145 case ORM_FBO:
146 surface->get_drawable_size = get_drawable_size_fbo;
147 break;
149 case ORM_BACKBUFFER:
150 surface->get_drawable_size = get_drawable_size_backbuffer;
151 break;
153 default:
154 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
155 return;
159 surface->container.type = type;
160 surface->container.u.base = container;
161 surface_update_draw_binding(surface);
164 struct blt_info
166 GLenum binding;
167 GLenum bind_target;
168 enum tex_types tex_type;
169 GLfloat coords[4][3];
172 struct float_rect
174 float l;
175 float t;
176 float r;
177 float b;
180 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
182 f->l = ((r->left * 2.0f) / w) - 1.0f;
183 f->t = ((r->top * 2.0f) / h) - 1.0f;
184 f->r = ((r->right * 2.0f) / w) - 1.0f;
185 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
188 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
190 GLfloat (*coords)[3] = info->coords;
191 struct float_rect f;
193 switch (target)
195 default:
196 FIXME("Unsupported texture target %#x\n", target);
197 /* Fall back to GL_TEXTURE_2D */
198 case GL_TEXTURE_2D:
199 info->binding = GL_TEXTURE_BINDING_2D;
200 info->bind_target = GL_TEXTURE_2D;
201 info->tex_type = tex_2d;
202 coords[0][0] = (float)rect->left / w;
203 coords[0][1] = (float)rect->top / h;
204 coords[0][2] = 0.0f;
206 coords[1][0] = (float)rect->right / w;
207 coords[1][1] = (float)rect->top / h;
208 coords[1][2] = 0.0f;
210 coords[2][0] = (float)rect->left / w;
211 coords[2][1] = (float)rect->bottom / h;
212 coords[2][2] = 0.0f;
214 coords[3][0] = (float)rect->right / w;
215 coords[3][1] = (float)rect->bottom / h;
216 coords[3][2] = 0.0f;
217 break;
219 case GL_TEXTURE_RECTANGLE_ARB:
220 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
221 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
222 info->tex_type = tex_rect;
223 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
224 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
225 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
226 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
227 break;
229 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
230 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
231 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
232 info->tex_type = tex_cube;
233 cube_coords_float(rect, w, h, &f);
235 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
236 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
237 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
238 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
239 break;
241 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
242 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
243 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
244 info->tex_type = tex_cube;
245 cube_coords_float(rect, w, h, &f);
247 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
248 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
249 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
250 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
251 break;
253 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
254 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
255 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
256 info->tex_type = tex_cube;
257 cube_coords_float(rect, w, h, &f);
259 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
260 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
261 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
262 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
263 break;
265 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
266 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
267 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
268 info->tex_type = tex_cube;
269 cube_coords_float(rect, w, h, &f);
271 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
272 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
273 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
274 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
275 break;
277 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
278 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
279 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
280 info->tex_type = tex_cube;
281 cube_coords_float(rect, w, h, &f);
283 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
284 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
285 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
286 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
287 break;
289 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
290 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
291 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
292 info->tex_type = tex_cube;
293 cube_coords_float(rect, w, h, &f);
295 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
296 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
297 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
298 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
299 break;
303 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
305 if (rect_in)
306 *rect_out = *rect_in;
307 else
309 rect_out->left = 0;
310 rect_out->top = 0;
311 rect_out->right = surface->resource.width;
312 rect_out->bottom = surface->resource.height;
316 /* GL locking and context activation is done by the caller */
317 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
318 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
320 struct blt_info info;
322 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
324 glEnable(info.bind_target);
325 checkGLcall("glEnable(bind_target)");
327 context_bind_texture(context, info.bind_target, src_surface->texture_name);
329 /* Filtering for StretchRect */
330 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
331 wined3d_gl_mag_filter(magLookup, filter));
332 checkGLcall("glTexParameteri");
333 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
334 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
335 checkGLcall("glTexParameteri");
336 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
337 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
338 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
339 glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
340 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
341 checkGLcall("glTexEnvi");
343 /* Draw a quad */
344 glBegin(GL_TRIANGLE_STRIP);
345 glTexCoord3fv(info.coords[0]);
346 glVertex2i(dst_rect->left, dst_rect->top);
348 glTexCoord3fv(info.coords[1]);
349 glVertex2i(dst_rect->right, dst_rect->top);
351 glTexCoord3fv(info.coords[2]);
352 glVertex2i(dst_rect->left, dst_rect->bottom);
354 glTexCoord3fv(info.coords[3]);
355 glVertex2i(dst_rect->right, dst_rect->bottom);
356 glEnd();
358 /* Unbind the texture */
359 context_bind_texture(context, info.bind_target, 0);
361 /* We changed the filtering settings on the texture. Inform the
362 * container about this to get the filters reset properly next draw. */
363 if (src_surface->container.type == WINED3D_CONTAINER_TEXTURE)
365 struct wined3d_texture *texture = src_surface->container.u.texture;
366 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_POINT;
367 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
368 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_NONE;
369 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
373 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
375 const struct wined3d_format *format = surface->resource.format;
376 SYSTEM_INFO sysInfo;
377 BITMAPINFO *b_info;
378 int extraline = 0;
379 DWORD *masks;
380 UINT usage;
381 HDC dc;
383 TRACE("surface %p.\n", surface);
385 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
387 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
388 return WINED3DERR_INVALIDCALL;
391 switch (format->byte_count)
393 case 2:
394 case 4:
395 /* Allocate extra space to store the RGB bit masks. */
396 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
397 break;
399 case 3:
400 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
401 break;
403 default:
404 /* Allocate extra space for a palette. */
405 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
406 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
407 break;
410 if (!b_info)
411 return E_OUTOFMEMORY;
413 /* Some applications access the surface in via DWORDs, and do not take
414 * the necessary care at the end of the surface. So we need at least
415 * 4 extra bytes at the end of the surface. Check against the page size,
416 * if the last page used for the surface has at least 4 spare bytes we're
417 * safe, otherwise add an extra line to the DIB section. */
418 GetSystemInfo(&sysInfo);
419 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
421 extraline = 1;
422 TRACE("Adding an extra line to the DIB section.\n");
425 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
426 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
427 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
428 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
429 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
430 * wined3d_surface_get_pitch(surface);
431 b_info->bmiHeader.biPlanes = 1;
432 b_info->bmiHeader.biBitCount = format->byte_count * 8;
434 b_info->bmiHeader.biXPelsPerMeter = 0;
435 b_info->bmiHeader.biYPelsPerMeter = 0;
436 b_info->bmiHeader.biClrUsed = 0;
437 b_info->bmiHeader.biClrImportant = 0;
439 /* Get the bit masks */
440 masks = (DWORD *)b_info->bmiColors;
441 switch (surface->resource.format->id)
443 case WINED3DFMT_B8G8R8_UNORM:
444 usage = DIB_RGB_COLORS;
445 b_info->bmiHeader.biCompression = BI_RGB;
446 break;
448 case WINED3DFMT_B5G5R5X1_UNORM:
449 case WINED3DFMT_B5G5R5A1_UNORM:
450 case WINED3DFMT_B4G4R4A4_UNORM:
451 case WINED3DFMT_B4G4R4X4_UNORM:
452 case WINED3DFMT_B2G3R3_UNORM:
453 case WINED3DFMT_B2G3R3A8_UNORM:
454 case WINED3DFMT_R10G10B10A2_UNORM:
455 case WINED3DFMT_R8G8B8A8_UNORM:
456 case WINED3DFMT_R8G8B8X8_UNORM:
457 case WINED3DFMT_B10G10R10A2_UNORM:
458 case WINED3DFMT_B5G6R5_UNORM:
459 case WINED3DFMT_R16G16B16A16_UNORM:
460 usage = 0;
461 b_info->bmiHeader.biCompression = BI_BITFIELDS;
462 masks[0] = format->red_mask;
463 masks[1] = format->green_mask;
464 masks[2] = format->blue_mask;
465 break;
467 default:
468 /* Don't know palette */
469 b_info->bmiHeader.biCompression = BI_RGB;
470 usage = 0;
471 break;
474 if (!(dc = GetDC(0)))
476 HeapFree(GetProcessHeap(), 0, b_info);
477 return HRESULT_FROM_WIN32(GetLastError());
480 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
481 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
482 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
483 surface->dib.DIBsection = CreateDIBSection(dc, b_info, usage, &surface->dib.bitmap_data, 0, 0);
484 ReleaseDC(0, dc);
486 if (!surface->dib.DIBsection)
488 ERR("Failed to create DIB section.\n");
489 HeapFree(GetProcessHeap(), 0, b_info);
490 return HRESULT_FROM_WIN32(GetLastError());
493 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
494 /* Copy the existing surface to the dib section. */
495 if (surface->resource.allocatedMemory)
497 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
498 surface->resource.height * wined3d_surface_get_pitch(surface));
500 else
502 /* This is to make maps read the GL texture although memory is allocated. */
503 surface->flags &= ~SFLAG_INSYSMEM;
505 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
507 HeapFree(GetProcessHeap(), 0, b_info);
509 /* Now allocate a DC. */
510 surface->hDC = CreateCompatibleDC(0);
511 SelectObject(surface->hDC, surface->dib.DIBsection);
512 TRACE("Using wined3d palette %p.\n", surface->palette);
513 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
515 surface->flags |= SFLAG_DIBSECTION;
517 return WINED3D_OK;
520 static BOOL surface_need_pbo(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
522 if (surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
523 return FALSE;
524 if (!(surface->flags & SFLAG_DYNLOCK))
525 return FALSE;
526 if (surface->flags & (SFLAG_CONVERTED | SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM))
527 return FALSE;
528 if (!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT])
529 return FALSE;
531 return TRUE;
534 static void surface_load_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
536 struct wined3d_context *context;
537 GLenum error;
539 context = context_acquire(surface->resource.device, NULL);
540 ENTER_GL();
542 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
543 error = glGetError();
544 if (!surface->pbo || error != GL_NO_ERROR)
545 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
547 TRACE("Binding PBO %u.\n", surface->pbo);
549 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
550 checkGLcall("glBindBufferARB");
552 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
553 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
554 checkGLcall("glBufferDataARB");
556 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
557 checkGLcall("glBindBufferARB");
559 /* We don't need the system memory anymore and we can't even use it for PBOs. */
560 if (!(surface->flags & SFLAG_CLIENT))
562 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
563 surface->resource.heapMemory = NULL;
565 surface->resource.allocatedMemory = NULL;
566 surface->flags |= SFLAG_PBO;
567 LEAVE_GL();
568 context_release(context);
571 static void surface_prepare_system_memory(struct wined3d_surface *surface)
573 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
575 TRACE("surface %p.\n", surface);
577 if (!(surface->flags & SFLAG_PBO) && surface_need_pbo(surface, gl_info))
578 surface_load_pbo(surface, gl_info);
579 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
581 /* Whatever surface we have, make sure that there is memory allocated
582 * for the downloaded copy, or a PBO to map. */
583 if (!surface->resource.heapMemory)
584 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
586 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
587 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
589 if (surface->flags & SFLAG_INSYSMEM)
590 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
594 static void surface_evict_sysmem(struct wined3d_surface *surface)
596 if (surface->flags & SFLAG_DONOTFREE)
597 return;
599 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
600 surface->resource.allocatedMemory = NULL;
601 surface->resource.heapMemory = NULL;
602 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
605 /* Context activation is done by the caller. */
606 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
607 struct wined3d_context *context, BOOL srgb)
609 struct wined3d_device *device = surface->resource.device;
610 DWORD active_sampler;
612 /* We don't need a specific texture unit, but after binding the texture
613 * the current unit is dirty. Read the unit back instead of switching to
614 * 0, this avoids messing around with the state manager's GL states. The
615 * current texture unit should always be a valid one.
617 * To be more specific, this is tricky because we can implicitly be
618 * called from sampler() in state.c. This means we can't touch anything
619 * other than whatever happens to be the currently active texture, or we
620 * would risk marking already applied sampler states dirty again. */
621 active_sampler = device->rev_tex_unit_map[context->active_texture];
623 if (active_sampler != WINED3D_UNMAPPED_STAGE)
624 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
625 surface_bind(surface, context, srgb);
628 static void surface_force_reload(struct wined3d_surface *surface)
630 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
633 static void surface_release_client_storage(struct wined3d_surface *surface)
635 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
637 ENTER_GL();
638 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
639 if (surface->texture_name)
641 surface_bind_and_dirtify(surface, context, FALSE);
642 glTexImage2D(surface->texture_target, surface->texture_level,
643 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
645 if (surface->texture_name_srgb)
647 surface_bind_and_dirtify(surface, context, TRUE);
648 glTexImage2D(surface->texture_target, surface->texture_level,
649 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
651 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
652 LEAVE_GL();
654 context_release(context);
656 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
657 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
658 surface_force_reload(surface);
661 static HRESULT surface_private_setup(struct wined3d_surface *surface)
663 /* TODO: Check against the maximum texture sizes supported by the video card. */
664 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
665 unsigned int pow2Width, pow2Height;
667 TRACE("surface %p.\n", surface);
669 surface->texture_name = 0;
670 surface->texture_target = GL_TEXTURE_2D;
672 /* Non-power2 support */
673 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
675 pow2Width = surface->resource.width;
676 pow2Height = surface->resource.height;
678 else
680 /* Find the nearest pow2 match */
681 pow2Width = pow2Height = 1;
682 while (pow2Width < surface->resource.width)
683 pow2Width <<= 1;
684 while (pow2Height < surface->resource.height)
685 pow2Height <<= 1;
687 surface->pow2Width = pow2Width;
688 surface->pow2Height = pow2Height;
690 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
692 /* TODO: Add support for non power two compressed textures. */
693 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
695 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
696 surface, surface->resource.width, surface->resource.height);
697 return WINED3DERR_NOTAVAILABLE;
701 if (pow2Width != surface->resource.width
702 || pow2Height != surface->resource.height)
704 surface->flags |= SFLAG_NONPOW2;
707 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
708 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
710 /* One of three options:
711 * 1: Do the same as we do with NPOT and scale the texture, (any
712 * texture ops would require the texture to be scaled which is
713 * potentially slow)
714 * 2: Set the texture to the maximum size (bad idea).
715 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
716 * 4: Create the surface, but allow it to be used only for DirectDraw
717 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
718 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
719 * the render target. */
720 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
722 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
723 return WINED3DERR_NOTAVAILABLE;
726 /* We should never use this surface in combination with OpenGL! */
727 TRACE("Creating an oversized surface: %ux%u.\n",
728 surface->pow2Width, surface->pow2Height);
730 else
732 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
733 * and EXT_PALETTED_TEXTURE is used in combination with texture
734 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
735 * EXT_PALETTED_TEXTURE doesn't work in combination with
736 * ARB_TEXTURE_RECTANGLE. */
737 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
738 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
739 && gl_info->supported[EXT_PALETTED_TEXTURE]
740 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
742 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
743 surface->pow2Width = surface->resource.width;
744 surface->pow2Height = surface->resource.height;
745 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
749 switch (wined3d_settings.offscreen_rendering_mode)
751 case ORM_FBO:
752 surface->get_drawable_size = get_drawable_size_fbo;
753 break;
755 case ORM_BACKBUFFER:
756 surface->get_drawable_size = get_drawable_size_backbuffer;
757 break;
759 default:
760 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
761 return WINED3DERR_INVALIDCALL;
764 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
765 surface->flags |= SFLAG_LOST;
767 return WINED3D_OK;
770 static void surface_realize_palette(struct wined3d_surface *surface)
772 struct wined3d_palette *palette = surface->palette;
774 TRACE("surface %p.\n", surface);
776 if (!palette) return;
778 if (surface->resource.format->id == WINED3DFMT_P8_UINT
779 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
781 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
783 /* Make sure the texture is up to date. This call doesn't do
784 * anything if the texture is already up to date. */
785 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
787 /* We want to force a palette refresh, so mark the drawable as not being up to date */
788 if (!surface_is_offscreen(surface))
789 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
791 else
793 if (!(surface->flags & SFLAG_INSYSMEM))
795 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
796 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
798 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
802 if (surface->flags & SFLAG_DIBSECTION)
804 RGBQUAD col[256];
805 unsigned int i;
807 TRACE("Updating the DC's palette.\n");
809 for (i = 0; i < 256; ++i)
811 col[i].rgbRed = palette->palents[i].peRed;
812 col[i].rgbGreen = palette->palents[i].peGreen;
813 col[i].rgbBlue = palette->palents[i].peBlue;
814 col[i].rgbReserved = 0;
816 SetDIBColorTable(surface->hDC, 0, 256, col);
819 /* Propagate the changes to the drawable when we have a palette. */
820 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
821 surface_load_location(surface, surface->draw_binding, NULL);
824 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
826 HRESULT hr;
828 /* If there's no destination surface there is nothing to do. */
829 if (!surface->overlay_dest)
830 return WINED3D_OK;
832 /* Blt calls ModifyLocation on the dest surface, which in turn calls
833 * DrawOverlay to update the overlay. Prevent an endless recursion. */
834 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
835 return WINED3D_OK;
837 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
838 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
839 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3D_TEXF_LINEAR);
840 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
842 return hr;
845 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
847 struct wined3d_device *device = surface->resource.device;
848 const RECT *pass_rect = rect;
850 TRACE("surface %p, rect %s, flags %#x.\n",
851 surface, wine_dbgstr_rect(rect), flags);
853 if (flags & WINED3DLOCK_DISCARD)
855 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
856 surface_prepare_system_memory(surface);
857 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
859 else
861 /* surface_load_location() does not check if the rectangle specifies
862 * the full surface. Most callers don't need that, so do it here. */
863 if (rect && !rect->top && !rect->left
864 && rect->right == surface->resource.width
865 && rect->bottom == surface->resource.height)
866 pass_rect = NULL;
867 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
870 if (surface->flags & SFLAG_PBO)
872 const struct wined3d_gl_info *gl_info;
873 struct wined3d_context *context;
875 context = context_acquire(device, NULL);
876 gl_info = context->gl_info;
878 ENTER_GL();
879 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
880 checkGLcall("glBindBufferARB");
882 /* This shouldn't happen but could occur if some other function
883 * didn't handle the PBO properly. */
884 if (surface->resource.allocatedMemory)
885 ERR("The surface already has PBO memory allocated.\n");
887 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
888 checkGLcall("glMapBufferARB");
890 /* Make sure the PBO isn't set anymore in order not to break non-PBO
891 * calls. */
892 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
893 checkGLcall("glBindBufferARB");
895 LEAVE_GL();
896 context_release(context);
899 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
901 if (!rect)
902 surface_add_dirty_rect(surface, NULL);
903 else
905 struct wined3d_box b;
907 b.left = rect->left;
908 b.top = rect->top;
909 b.right = rect->right;
910 b.bottom = rect->bottom;
911 b.front = 0;
912 b.back = 1;
913 surface_add_dirty_rect(surface, &b);
918 static void surface_unmap(struct wined3d_surface *surface)
920 struct wined3d_device *device = surface->resource.device;
921 BOOL fullsurface;
923 TRACE("surface %p.\n", surface);
925 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
927 if (surface->flags & SFLAG_PBO)
929 const struct wined3d_gl_info *gl_info;
930 struct wined3d_context *context;
932 TRACE("Freeing PBO memory.\n");
934 context = context_acquire(device, NULL);
935 gl_info = context->gl_info;
937 ENTER_GL();
938 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
939 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
940 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
941 checkGLcall("glUnmapBufferARB");
942 LEAVE_GL();
943 context_release(context);
945 surface->resource.allocatedMemory = NULL;
948 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
950 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
952 TRACE("Not dirtified, nothing to do.\n");
953 goto done;
956 /* FIXME: The ORM_BACKBUFFER case probably isn't needed, but who knows
957 * what obscure bugs in backbuffer ORM removing it will uncover. Also,
958 * this should only be needed for the frontbuffer, but that requires
959 * present calls to call surface_load_location() on the backbuffer.
960 * Fix both of those after 1.4. */
961 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
962 || (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER
963 && device->fb.render_targets && surface == device->fb.render_targets[0]))
965 if (!surface->dirtyRect.left && !surface->dirtyRect.top
966 && surface->dirtyRect.right == surface->resource.width
967 && surface->dirtyRect.bottom == surface->resource.height)
969 fullsurface = TRUE;
971 else
973 /* TODO: Proper partial rectangle tracking. */
974 fullsurface = FALSE;
975 surface->flags |= SFLAG_INSYSMEM;
978 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
980 /* Partial rectangle tracking is not commonly implemented, it is only
981 * done for render targets. INSYSMEM was set before to tell
982 * surface_load_location() where to read the rectangle from.
983 * Indrawable is set because all modifications from the partial
984 * sysmem copy are written back to the drawable, thus the surface is
985 * merged again in the drawable. The sysmem copy is not fully up to
986 * date because only a subrectangle was read in Map(). */
987 if (!fullsurface)
989 surface_modify_location(surface, surface->draw_binding, TRUE);
990 surface_evict_sysmem(surface);
993 surface->dirtyRect.left = surface->resource.width;
994 surface->dirtyRect.top = surface->resource.height;
995 surface->dirtyRect.right = 0;
996 surface->dirtyRect.bottom = 0;
998 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
1000 FIXME("Depth / stencil buffer locking is not implemented.\n");
1003 done:
1004 /* Overlays have to be redrawn manually after changes with the GL implementation */
1005 if (surface->overlay_dest)
1006 surface_draw_overlay(surface);
1009 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1011 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1012 return FALSE;
1013 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1014 return FALSE;
1015 return TRUE;
1018 static void wined3d_surface_depth_blt_fbo(const struct wined3d_device *device, struct wined3d_surface *src_surface,
1019 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1021 const struct wined3d_gl_info *gl_info;
1022 struct wined3d_context *context;
1023 DWORD src_mask, dst_mask;
1024 GLbitfield gl_mask;
1026 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1027 device, src_surface, wine_dbgstr_rect(src_rect),
1028 dst_surface, wine_dbgstr_rect(dst_rect));
1030 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1031 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1033 if (src_mask != dst_mask)
1035 ERR("Incompatible formats %s and %s.\n",
1036 debug_d3dformat(src_surface->resource.format->id),
1037 debug_d3dformat(dst_surface->resource.format->id));
1038 return;
1041 if (!src_mask)
1043 ERR("Not a depth / stencil format: %s.\n",
1044 debug_d3dformat(src_surface->resource.format->id));
1045 return;
1048 gl_mask = 0;
1049 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1050 gl_mask |= GL_DEPTH_BUFFER_BIT;
1051 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1052 gl_mask |= GL_STENCIL_BUFFER_BIT;
1054 /* Make sure the locations are up-to-date. Loading the destination
1055 * surface isn't required if the entire surface is overwritten. */
1056 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1057 if (!surface_is_full_rect(dst_surface, dst_rect))
1058 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1060 context = context_acquire(device, NULL);
1061 if (!context->valid)
1063 context_release(context);
1064 WARN("Invalid context, skipping blit.\n");
1065 return;
1068 gl_info = context->gl_info;
1070 ENTER_GL();
1072 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1073 glReadBuffer(GL_NONE);
1074 checkGLcall("glReadBuffer()");
1075 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1077 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1078 context_set_draw_buffer(context, GL_NONE);
1079 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1080 context_invalidate_state(context, STATE_FRAMEBUFFER);
1082 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1084 glDepthMask(GL_TRUE);
1085 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
1087 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1089 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1091 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1092 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
1094 glStencilMask(~0U);
1095 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
1098 glDisable(GL_SCISSOR_TEST);
1099 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1101 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1102 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1103 checkGLcall("glBlitFramebuffer()");
1105 LEAVE_GL();
1107 if (wined3d_settings.strict_draw_ordering)
1108 wglFlush(); /* Flush to ensure ordering across contexts. */
1110 context_release(context);
1113 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1114 * Depth / stencil is not supported. */
1115 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
1116 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1117 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1119 const struct wined3d_gl_info *gl_info;
1120 struct wined3d_context *context;
1121 RECT src_rect, dst_rect;
1122 GLenum gl_filter;
1123 GLenum buffer;
1125 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1126 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1127 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1128 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1129 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1131 src_rect = *src_rect_in;
1132 dst_rect = *dst_rect_in;
1134 switch (filter)
1136 case WINED3D_TEXF_LINEAR:
1137 gl_filter = GL_LINEAR;
1138 break;
1140 default:
1141 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1142 case WINED3D_TEXF_NONE:
1143 case WINED3D_TEXF_POINT:
1144 gl_filter = GL_NEAREST;
1145 break;
1148 /* Resolve the source surface first if needed. */
1149 if (src_location == SFLAG_INRB_MULTISAMPLE
1150 && (src_surface->resource.format->id != dst_surface->resource.format->id
1151 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1152 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1153 src_location = SFLAG_INRB_RESOLVED;
1155 /* Make sure the locations are up-to-date. Loading the destination
1156 * surface isn't required if the entire surface is overwritten. (And is
1157 * in fact harmful if we're being called by surface_load_location() with
1158 * the purpose of loading the destination surface.) */
1159 surface_load_location(src_surface, src_location, NULL);
1160 if (!surface_is_full_rect(dst_surface, &dst_rect))
1161 surface_load_location(dst_surface, dst_location, NULL);
1163 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1164 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1165 else context = context_acquire(device, NULL);
1167 if (!context->valid)
1169 context_release(context);
1170 WARN("Invalid context, skipping blit.\n");
1171 return;
1174 gl_info = context->gl_info;
1176 if (src_location == SFLAG_INDRAWABLE)
1178 TRACE("Source surface %p is onscreen.\n", src_surface);
1179 buffer = surface_get_gl_buffer(src_surface);
1180 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1182 else
1184 TRACE("Source surface %p is offscreen.\n", src_surface);
1185 buffer = GL_COLOR_ATTACHMENT0;
1188 ENTER_GL();
1189 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1190 glReadBuffer(buffer);
1191 checkGLcall("glReadBuffer()");
1192 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1193 LEAVE_GL();
1195 if (dst_location == SFLAG_INDRAWABLE)
1197 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1198 buffer = surface_get_gl_buffer(dst_surface);
1199 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1201 else
1203 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1204 buffer = GL_COLOR_ATTACHMENT0;
1207 ENTER_GL();
1208 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1209 context_set_draw_buffer(context, buffer);
1210 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1211 context_invalidate_state(context, STATE_FRAMEBUFFER);
1213 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1214 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
1215 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
1216 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
1217 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
1219 glDisable(GL_SCISSOR_TEST);
1220 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1222 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1223 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1224 checkGLcall("glBlitFramebuffer()");
1226 LEAVE_GL();
1228 if (wined3d_settings.strict_draw_ordering
1229 || (dst_location == SFLAG_INDRAWABLE
1230 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1231 wglFlush();
1233 context_release(context);
1236 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1237 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
1238 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
1240 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1241 return FALSE;
1243 /* Source and/or destination need to be on the GL side */
1244 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
1245 return FALSE;
1247 switch (blit_op)
1249 case WINED3D_BLIT_OP_COLOR_BLIT:
1250 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1251 return FALSE;
1252 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1253 return FALSE;
1254 break;
1256 case WINED3D_BLIT_OP_DEPTH_BLIT:
1257 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1258 return FALSE;
1259 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1260 return FALSE;
1261 break;
1263 default:
1264 return FALSE;
1267 if (!(src_format->id == dst_format->id
1268 || (is_identity_fixup(src_format->color_fixup)
1269 && is_identity_fixup(dst_format->color_fixup))))
1270 return FALSE;
1272 return TRUE;
1275 /* This function checks if the primary render target uses the 8bit paletted format. */
1276 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1278 if (device->fb.render_targets && device->fb.render_targets[0])
1280 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1281 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1282 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1283 return TRUE;
1285 return FALSE;
1288 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1289 DWORD color, struct wined3d_color *float_color)
1291 const struct wined3d_format *format = surface->resource.format;
1292 const struct wined3d_device *device = surface->resource.device;
1294 switch (format->id)
1296 case WINED3DFMT_P8_UINT:
1297 if (surface->palette)
1299 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1300 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1301 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1303 else
1305 float_color->r = 0.0f;
1306 float_color->g = 0.0f;
1307 float_color->b = 0.0f;
1309 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1310 break;
1312 case WINED3DFMT_B5G6R5_UNORM:
1313 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1314 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1315 float_color->b = (color & 0x1f) / 31.0f;
1316 float_color->a = 1.0f;
1317 break;
1319 case WINED3DFMT_B8G8R8_UNORM:
1320 case WINED3DFMT_B8G8R8X8_UNORM:
1321 float_color->r = D3DCOLOR_R(color);
1322 float_color->g = D3DCOLOR_G(color);
1323 float_color->b = D3DCOLOR_B(color);
1324 float_color->a = 1.0f;
1325 break;
1327 case WINED3DFMT_B8G8R8A8_UNORM:
1328 float_color->r = D3DCOLOR_R(color);
1329 float_color->g = D3DCOLOR_G(color);
1330 float_color->b = D3DCOLOR_B(color);
1331 float_color->a = D3DCOLOR_A(color);
1332 break;
1334 default:
1335 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1336 return FALSE;
1339 return TRUE;
1342 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1344 const struct wined3d_format *format = surface->resource.format;
1346 switch (format->id)
1348 case WINED3DFMT_S1_UINT_D15_UNORM:
1349 *float_depth = depth / (float)0x00007fff;
1350 break;
1352 case WINED3DFMT_D16_UNORM:
1353 *float_depth = depth / (float)0x0000ffff;
1354 break;
1356 case WINED3DFMT_D24_UNORM_S8_UINT:
1357 case WINED3DFMT_X8D24_UNORM:
1358 *float_depth = depth / (float)0x00ffffff;
1359 break;
1361 case WINED3DFMT_D32_UNORM:
1362 *float_depth = depth / (float)0xffffffff;
1363 break;
1365 default:
1366 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1367 return FALSE;
1370 return TRUE;
1373 /* Do not call while under the GL lock. */
1374 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1376 const struct wined3d_resource *resource = &surface->resource;
1377 struct wined3d_device *device = resource->device;
1378 const struct blit_shader *blitter;
1380 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1381 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1382 if (!blitter)
1384 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1385 return WINED3DERR_INVALIDCALL;
1388 return blitter->depth_fill(device, surface, rect, depth);
1391 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1392 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1394 struct wined3d_device *device = src_surface->resource.device;
1396 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1397 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1398 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1399 return WINED3DERR_INVALIDCALL;
1401 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1403 surface_modify_ds_location(dst_surface, SFLAG_INTEXTURE,
1404 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1406 return WINED3D_OK;
1409 /* Do not call while under the GL lock. */
1410 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1411 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1412 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
1414 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1415 struct wined3d_device *device = dst_surface->resource.device;
1416 DWORD src_ds_flags, dst_ds_flags;
1417 RECT src_rect, dst_rect;
1418 BOOL scale, convert;
1420 static const DWORD simple_blit = WINEDDBLT_ASYNC
1421 | WINEDDBLT_COLORFILL
1422 | WINEDDBLT_WAIT
1423 | WINEDDBLT_DEPTHFILL
1424 | WINEDDBLT_DONOTWAIT;
1426 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1427 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1428 flags, fx, debug_d3dtexturefiltertype(filter));
1429 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1431 if (fx)
1433 TRACE("dwSize %#x.\n", fx->dwSize);
1434 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
1435 TRACE("dwROP %#x.\n", fx->dwROP);
1436 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
1437 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
1438 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
1439 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
1440 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
1441 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
1442 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
1443 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
1444 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
1445 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
1446 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
1447 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
1448 TRACE("dwReserved %#x.\n", fx->dwReserved);
1449 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
1450 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
1451 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
1452 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
1453 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
1454 TRACE("ddckDestColorkey {%#x, %#x}.\n",
1455 fx->ddckDestColorkey.color_space_low_value,
1456 fx->ddckDestColorkey.color_space_high_value);
1457 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
1458 fx->ddckSrcColorkey.color_space_low_value,
1459 fx->ddckSrcColorkey.color_space_high_value);
1462 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1464 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1465 return WINEDDERR_SURFACEBUSY;
1468 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1470 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1471 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1472 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1473 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1474 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1476 WARN("The application gave us a bad destination rectangle.\n");
1477 return WINEDDERR_INVALIDRECT;
1480 if (src_surface)
1482 surface_get_rect(src_surface, src_rect_in, &src_rect);
1484 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1485 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1486 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1487 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1488 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1490 WARN("Application gave us bad source rectangle for Blt.\n");
1491 return WINEDDERR_INVALIDRECT;
1494 else
1496 memset(&src_rect, 0, sizeof(src_rect));
1499 if (!fx || !(fx->dwDDFX))
1500 flags &= ~WINEDDBLT_DDFX;
1502 if (flags & WINEDDBLT_WAIT)
1503 flags &= ~WINEDDBLT_WAIT;
1505 if (flags & WINEDDBLT_ASYNC)
1507 static unsigned int once;
1509 if (!once++)
1510 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1511 flags &= ~WINEDDBLT_ASYNC;
1514 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1515 if (flags & WINEDDBLT_DONOTWAIT)
1517 static unsigned int once;
1519 if (!once++)
1520 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1521 flags &= ~WINEDDBLT_DONOTWAIT;
1524 if (!device->d3d_initialized)
1526 WARN("D3D not initialized, using fallback.\n");
1527 goto cpu;
1530 /* We want to avoid invalidating the sysmem location for converted
1531 * surfaces, since otherwise we'd have to convert the data back when
1532 * locking them. */
1533 if (dst_surface->flags & SFLAG_CONVERTED)
1535 WARN("Converted surface, using CPU blit.\n");
1536 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1539 if (flags & ~simple_blit)
1541 WARN("Using fallback for complex blit (%#x).\n", flags);
1542 goto fallback;
1545 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1546 src_swapchain = src_surface->container.u.swapchain;
1547 else
1548 src_swapchain = NULL;
1550 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1551 dst_swapchain = dst_surface->container.u.swapchain;
1552 else
1553 dst_swapchain = NULL;
1555 /* This isn't strictly needed. FBO blits for example could deal with
1556 * cross-swapchain blits by first downloading the source to a texture
1557 * before switching to the destination context. We just have this here to
1558 * not have to deal with the issue, since cross-swapchain blits should be
1559 * rare. */
1560 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1562 FIXME("Using fallback for cross-swapchain blit.\n");
1563 goto fallback;
1566 scale = src_surface
1567 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
1568 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
1569 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
1571 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1572 if (src_surface)
1573 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1574 else
1575 src_ds_flags = 0;
1577 if (src_ds_flags || dst_ds_flags)
1579 if (flags & WINEDDBLT_DEPTHFILL)
1581 float depth;
1583 TRACE("Depth fill.\n");
1585 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1586 return WINED3DERR_INVALIDCALL;
1588 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1589 return WINED3D_OK;
1591 else
1593 /* Accessing depth / stencil surfaces is supposed to fail while in
1594 * a scene, except for fills, which seem to work. */
1595 if (device->inScene)
1597 WARN("Rejecting depth / stencil access while in scene.\n");
1598 return WINED3DERR_INVALIDCALL;
1601 if (src_ds_flags != dst_ds_flags)
1603 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1604 return WINED3DERR_INVALIDCALL;
1607 if (src_rect.top || src_rect.left
1608 || src_rect.bottom != src_surface->resource.height
1609 || src_rect.right != src_surface->resource.width)
1611 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1612 wine_dbgstr_rect(&src_rect));
1613 return WINED3DERR_INVALIDCALL;
1616 if (dst_rect.top || dst_rect.left
1617 || dst_rect.bottom != dst_surface->resource.height
1618 || dst_rect.right != dst_surface->resource.width)
1620 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1621 wine_dbgstr_rect(&src_rect));
1622 return WINED3DERR_INVALIDCALL;
1625 if (scale)
1627 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1628 return WINED3DERR_INVALIDCALL;
1631 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1632 return WINED3D_OK;
1635 else
1637 /* In principle this would apply to depth blits as well, but we don't
1638 * implement those in the CPU blitter at the moment. */
1639 if ((dst_surface->flags & SFLAG_INSYSMEM)
1640 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
1642 if (scale)
1643 TRACE("Not doing sysmem blit because of scaling.\n");
1644 else if (convert)
1645 TRACE("Not doing sysmem blit because of format conversion.\n");
1646 else
1647 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1650 if (flags & WINEDDBLT_COLORFILL)
1652 struct wined3d_color color;
1654 TRACE("Color fill.\n");
1656 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1657 goto fallback;
1659 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1660 return WINED3D_OK;
1662 else
1664 TRACE("Color blit.\n");
1666 /* Upload */
1667 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
1669 if (scale)
1670 TRACE("Not doing upload because of scaling.\n");
1671 else if (convert)
1672 TRACE("Not doing upload because of format conversion.\n");
1673 else
1675 POINT dst_point = {dst_rect.left, dst_rect.top};
1677 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
1679 if (!surface_is_offscreen(dst_surface))
1680 surface_load_location(dst_surface, dst_surface->draw_binding, NULL);
1681 return WINED3D_OK;
1686 /* Use present for back -> front blits. The idea behind this is
1687 * that present is potentially faster than a blit, in particular
1688 * when FBO blits aren't available. Some ddraw applications like
1689 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
1690 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
1691 * applications can't blit directly to the frontbuffer. */
1692 if (dst_swapchain && dst_swapchain->back_buffers
1693 && dst_surface == dst_swapchain->front_buffer
1694 && src_surface == dst_swapchain->back_buffers[0])
1696 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
1698 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1700 /* Set the swap effect to COPY, we don't want the backbuffer
1701 * to become undefined. */
1702 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
1703 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
1704 dst_swapchain->desc.swap_effect = swap_effect;
1706 return WINED3D_OK;
1709 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1710 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1711 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1713 TRACE("Using FBO blit.\n");
1715 surface_blt_fbo(device, filter,
1716 src_surface, src_surface->draw_binding, &src_rect,
1717 dst_surface, dst_surface->draw_binding, &dst_rect);
1718 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1719 return WINED3D_OK;
1722 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1723 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1724 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1726 TRACE("Using arbfp blit.\n");
1728 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1729 return WINED3D_OK;
1734 fallback:
1736 /* Special cases for render targets. */
1737 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1738 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1740 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1741 src_surface, &src_rect, flags, fx, filter)))
1742 return WINED3D_OK;
1745 cpu:
1747 /* For the rest call the X11 surface implementation. For render targets
1748 * this should be implemented OpenGL accelerated in BltOverride, other
1749 * blits are rather rare. */
1750 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1753 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1754 struct wined3d_surface *render_target)
1756 TRACE("surface %p, render_target %p.\n", surface, render_target);
1758 /* TODO: Check surface sizes, pools, etc. */
1760 if (render_target->resource.multisample_type)
1761 return WINED3DERR_INVALIDCALL;
1763 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1766 /* Context activation is done by the caller. */
1767 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1769 if (surface->flags & SFLAG_DIBSECTION)
1771 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1773 else
1775 if (!surface->resource.heapMemory)
1776 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1777 else if (!(surface->flags & SFLAG_CLIENT))
1778 ERR("Surface %p has heapMemory %p and flags %#x.\n",
1779 surface, surface->resource.heapMemory, surface->flags);
1781 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1782 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1785 ENTER_GL();
1786 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1787 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1788 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1789 surface->resource.size, surface->resource.allocatedMemory));
1790 checkGLcall("glGetBufferSubDataARB");
1791 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1792 checkGLcall("glDeleteBuffersARB");
1793 LEAVE_GL();
1795 surface->pbo = 0;
1796 surface->flags &= ~SFLAG_PBO;
1799 /* Do not call while under the GL lock. */
1800 static void surface_unload(struct wined3d_resource *resource)
1802 struct wined3d_surface *surface = surface_from_resource(resource);
1803 struct wined3d_renderbuffer_entry *entry, *entry2;
1804 struct wined3d_device *device = resource->device;
1805 const struct wined3d_gl_info *gl_info;
1806 struct wined3d_context *context;
1808 TRACE("surface %p.\n", surface);
1810 if (resource->pool == WINED3D_POOL_DEFAULT)
1812 /* Default pool resources are supposed to be destroyed before Reset is called.
1813 * Implicit resources stay however. So this means we have an implicit render target
1814 * or depth stencil. The content may be destroyed, but we still have to tear down
1815 * opengl resources, so we cannot leave early.
1817 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1818 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1819 * or the depth stencil into an FBO the texture or render buffer will be removed
1820 * and all flags get lost
1822 if (!(surface->flags & SFLAG_PBO))
1823 surface_init_sysmem(surface);
1824 /* We also get here when the ddraw swapchain is destroyed, for example
1825 * for a mode switch. In this case this surface won't necessarily be
1826 * an implicit surface. We have to mark it lost so that the
1827 * application can restore it after the mode switch. */
1828 surface->flags |= SFLAG_LOST;
1830 else
1832 /* Load the surface into system memory */
1833 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1834 surface_modify_location(surface, surface->draw_binding, FALSE);
1836 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1837 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1838 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1840 context = context_acquire(device, NULL);
1841 gl_info = context->gl_info;
1843 /* Destroy PBOs, but load them into real sysmem before */
1844 if (surface->flags & SFLAG_PBO)
1845 surface_remove_pbo(surface, gl_info);
1847 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1848 * all application-created targets the application has to release the surface
1849 * before calling _Reset
1851 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1853 ENTER_GL();
1854 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1855 LEAVE_GL();
1856 list_remove(&entry->entry);
1857 HeapFree(GetProcessHeap(), 0, entry);
1859 list_init(&surface->renderbuffers);
1860 surface->current_renderbuffer = NULL;
1862 ENTER_GL();
1864 /* If we're in a texture, the texture name belongs to the texture.
1865 * Otherwise, destroy it. */
1866 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1868 glDeleteTextures(1, &surface->texture_name);
1869 surface->texture_name = 0;
1870 glDeleteTextures(1, &surface->texture_name_srgb);
1871 surface->texture_name_srgb = 0;
1873 if (surface->rb_multisample)
1875 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1876 surface->rb_multisample = 0;
1878 if (surface->rb_resolved)
1880 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1881 surface->rb_resolved = 0;
1884 LEAVE_GL();
1886 context_release(context);
1888 resource_unload(resource);
1891 static const struct wined3d_resource_ops surface_resource_ops =
1893 surface_unload,
1896 static const struct wined3d_surface_ops surface_ops =
1898 surface_private_setup,
1899 surface_realize_palette,
1900 surface_map,
1901 surface_unmap,
1904 /*****************************************************************************
1905 * Initializes the GDI surface, aka creates the DIB section we render to
1906 * The DIB section creation is done by calling GetDC, which will create the
1907 * section and releasing the dc to allow the app to use it. The dib section
1908 * will stay until the surface is released
1910 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1911 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1912 * avoid confusion in the shared surface code.
1914 * Returns:
1915 * WINED3D_OK on success
1916 * The return values of called methods on failure
1918 *****************************************************************************/
1919 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1921 HRESULT hr;
1923 TRACE("surface %p.\n", surface);
1925 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1927 ERR("Overlays not yet supported by GDI surfaces.\n");
1928 return WINED3DERR_INVALIDCALL;
1931 /* Sysmem textures have memory already allocated - release it,
1932 * this avoids an unnecessary memcpy. */
1933 hr = surface_create_dib_section(surface);
1934 if (SUCCEEDED(hr))
1936 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1937 surface->resource.heapMemory = NULL;
1938 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1941 /* We don't mind the nonpow2 stuff in GDI. */
1942 surface->pow2Width = surface->resource.width;
1943 surface->pow2Height = surface->resource.height;
1945 return WINED3D_OK;
1948 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1950 struct wined3d_palette *palette = surface->palette;
1952 TRACE("surface %p.\n", surface);
1954 if (!palette) return;
1956 if (surface->flags & SFLAG_DIBSECTION)
1958 RGBQUAD col[256];
1959 unsigned int i;
1961 TRACE("Updating the DC's palette.\n");
1963 for (i = 0; i < 256; ++i)
1965 col[i].rgbRed = palette->palents[i].peRed;
1966 col[i].rgbGreen = palette->palents[i].peGreen;
1967 col[i].rgbBlue = palette->palents[i].peBlue;
1968 col[i].rgbReserved = 0;
1970 SetDIBColorTable(surface->hDC, 0, 256, col);
1973 /* Update the image because of the palette change. Some games like e.g.
1974 * Red Alert call SetEntries a lot to implement fading. */
1975 /* Tell the swapchain to update the screen. */
1976 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1978 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1979 if (surface == swapchain->front_buffer)
1981 x11_copy_to_screen(swapchain, NULL);
1986 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
1988 TRACE("surface %p, rect %s, flags %#x.\n",
1989 surface, wine_dbgstr_rect(rect), flags);
1991 if (!(surface->flags & SFLAG_DIBSECTION))
1993 HRESULT hr;
1995 /* This happens on gdi surfaces if the application set a user pointer
1996 * and resets it. Recreate the DIB section. */
1997 if (FAILED(hr = surface_create_dib_section(surface)))
1999 ERR("Failed to create dib section, hr %#x.\n", hr);
2000 return;
2002 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
2003 surface->resource.heapMemory = NULL;
2004 surface->resource.allocatedMemory = surface->dib.bitmap_data;
2008 static void gdi_surface_unmap(struct wined3d_surface *surface)
2010 TRACE("surface %p.\n", surface);
2012 /* Tell the swapchain to update the screen. */
2013 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2015 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2016 if (surface == swapchain->front_buffer)
2018 x11_copy_to_screen(swapchain, &surface->lockedRect);
2022 memset(&surface->lockedRect, 0, sizeof(RECT));
2025 static const struct wined3d_surface_ops gdi_surface_ops =
2027 gdi_surface_private_setup,
2028 gdi_surface_realize_palette,
2029 gdi_surface_map,
2030 gdi_surface_unmap,
2033 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2035 GLuint *name;
2036 DWORD flag;
2038 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2040 if(srgb)
2042 name = &surface->texture_name_srgb;
2043 flag = SFLAG_INSRGBTEX;
2045 else
2047 name = &surface->texture_name;
2048 flag = SFLAG_INTEXTURE;
2051 if (!*name && new_name)
2053 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2054 * surface has no texture name yet. See if we can get rid of this. */
2055 if (surface->flags & flag)
2057 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2058 surface_modify_location(surface, flag, FALSE);
2062 *name = new_name;
2063 surface_force_reload(surface);
2066 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2068 TRACE("surface %p, target %#x.\n", surface, target);
2070 if (surface->texture_target != target)
2072 if (target == GL_TEXTURE_RECTANGLE_ARB)
2074 surface->flags &= ~SFLAG_NORMCOORD;
2076 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2078 surface->flags |= SFLAG_NORMCOORD;
2081 surface->texture_target = target;
2082 surface_force_reload(surface);
2085 /* Context activation is done by the caller. */
2086 void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
2088 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
2090 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2092 struct wined3d_texture *texture = surface->container.u.texture;
2094 TRACE("Passing to container (%p).\n", texture);
2095 texture->texture_ops->texture_bind(texture, context, srgb);
2097 else
2099 if (surface->texture_level)
2101 ERR("Standalone surface %p is non-zero texture level %u.\n",
2102 surface, surface->texture_level);
2105 if (srgb)
2106 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2108 ENTER_GL();
2110 if (!surface->texture_name)
2112 glGenTextures(1, &surface->texture_name);
2113 checkGLcall("glGenTextures");
2115 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2117 context_bind_texture(context, surface->texture_target, surface->texture_name);
2118 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2119 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2120 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2121 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2122 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2123 checkGLcall("glTexParameteri");
2125 else
2127 context_bind_texture(context, surface->texture_target, surface->texture_name);
2130 LEAVE_GL();
2134 /* This call just downloads data, the caller is responsible for binding the
2135 * correct texture. */
2136 /* Context activation is done by the caller. */
2137 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2139 const struct wined3d_format *format = surface->resource.format;
2141 /* Only support read back of converted P8 surfaces. */
2142 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2144 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
2145 return;
2148 ENTER_GL();
2150 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2152 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2153 surface, surface->texture_level, format->glFormat, format->glType,
2154 surface->resource.allocatedMemory);
2156 if (surface->flags & SFLAG_PBO)
2158 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2159 checkGLcall("glBindBufferARB");
2160 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2161 checkGLcall("glGetCompressedTexImageARB");
2162 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2163 checkGLcall("glBindBufferARB");
2165 else
2167 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2168 surface->texture_level, surface->resource.allocatedMemory));
2169 checkGLcall("glGetCompressedTexImageARB");
2172 LEAVE_GL();
2174 else
2176 void *mem;
2177 GLenum gl_format = format->glFormat;
2178 GLenum gl_type = format->glType;
2179 int src_pitch = 0;
2180 int dst_pitch = 0;
2182 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2183 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2185 gl_format = GL_ALPHA;
2186 gl_type = GL_UNSIGNED_BYTE;
2189 if (surface->flags & SFLAG_NONPOW2)
2191 unsigned char alignment = surface->resource.device->surface_alignment;
2192 src_pitch = format->byte_count * surface->pow2Width;
2193 dst_pitch = wined3d_surface_get_pitch(surface);
2194 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2195 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2197 else
2199 mem = surface->resource.allocatedMemory;
2202 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2203 surface, surface->texture_level, gl_format, gl_type, mem);
2205 if (surface->flags & SFLAG_PBO)
2207 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2208 checkGLcall("glBindBufferARB");
2210 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2211 checkGLcall("glGetTexImage");
2213 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2214 checkGLcall("glBindBufferARB");
2216 else
2218 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2219 checkGLcall("glGetTexImage");
2221 LEAVE_GL();
2223 if (surface->flags & SFLAG_NONPOW2)
2225 const BYTE *src_data;
2226 BYTE *dst_data;
2227 UINT y;
2229 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2230 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2231 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2233 * We're doing this...
2235 * instead of boxing the texture :
2236 * |<-texture width ->| -->pow2width| /\
2237 * |111111111111111111| | |
2238 * |222 Texture 222222| boxed empty | texture height
2239 * |3333 Data 33333333| | |
2240 * |444444444444444444| | \/
2241 * ----------------------------------- |
2242 * | boxed empty | boxed empty | pow2height
2243 * | | | \/
2244 * -----------------------------------
2247 * we're repacking the data to the expected texture width
2249 * |<-texture width ->| -->pow2width| /\
2250 * |111111111111111111222222222222222| |
2251 * |222333333333333333333444444444444| texture height
2252 * |444444 | |
2253 * | | \/
2254 * | | |
2255 * | empty | pow2height
2256 * | | \/
2257 * -----------------------------------
2259 * == is the same as
2261 * |<-texture width ->| /\
2262 * |111111111111111111|
2263 * |222222222222222222|texture height
2264 * |333333333333333333|
2265 * |444444444444444444| \/
2266 * --------------------
2268 * this also means that any references to allocatedMemory should work with the data as if were a
2269 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2271 * internally the texture is still stored in a boxed format so any references to textureName will
2272 * get a boxed texture with width pow2width and not a texture of width resource.width.
2274 * Performance should not be an issue, because applications normally do not lock the surfaces when
2275 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2276 * and doesn't have to be re-read. */
2277 src_data = mem;
2278 dst_data = surface->resource.allocatedMemory;
2279 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2280 for (y = 1; y < surface->resource.height; ++y)
2282 /* skip the first row */
2283 src_data += src_pitch;
2284 dst_data += dst_pitch;
2285 memcpy(dst_data, src_data, dst_pitch);
2288 HeapFree(GetProcessHeap(), 0, mem);
2292 /* Surface has now been downloaded */
2293 surface->flags |= SFLAG_INSYSMEM;
2296 /* This call just uploads data, the caller is responsible for binding the
2297 * correct texture. */
2298 /* Context activation is done by the caller. */
2299 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2300 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
2301 BOOL srgb, const struct wined3d_bo_address *data)
2303 UINT update_w = src_rect->right - src_rect->left;
2304 UINT update_h = src_rect->bottom - src_rect->top;
2306 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
2307 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
2308 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2310 if (surface->flags & SFLAG_LOCKED)
2312 WARN("Uploading a surface that is currently mapped, setting SFLAG_PIN_SYSMEM.\n");
2313 surface->flags |= SFLAG_PIN_SYSMEM;
2316 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2317 update_h *= format->heightscale;
2319 ENTER_GL();
2321 if (data->buffer_object)
2323 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2324 checkGLcall("glBindBufferARB");
2327 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2329 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2330 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2331 const BYTE *addr = data->addr;
2332 GLenum internal;
2334 addr += (src_rect->top / format->block_height) * src_pitch;
2335 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2337 if (srgb)
2338 internal = format->glGammaInternal;
2339 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2340 internal = format->rtInternal;
2341 else
2342 internal = format->glInternal;
2344 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2345 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2346 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2348 if (row_length == src_pitch)
2350 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2351 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2353 else
2355 UINT row, y;
2357 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2358 * can't use the unpack row length like below. */
2359 for (row = 0, y = dst_point->y; row < row_count; ++row)
2361 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2362 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2363 y += format->block_height;
2364 addr += src_pitch;
2367 checkGLcall("glCompressedTexSubImage2DARB");
2369 else
2371 const BYTE *addr = data->addr;
2373 addr += src_rect->top * src_pitch;
2374 addr += src_rect->left * format->byte_count;
2376 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2377 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2378 update_w, update_h, format->glFormat, format->glType, addr);
2380 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
2381 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2382 update_w, update_h, format->glFormat, format->glType, addr);
2383 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2384 checkGLcall("glTexSubImage2D");
2387 if (data->buffer_object)
2389 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2390 checkGLcall("glBindBufferARB");
2393 LEAVE_GL();
2395 if (wined3d_settings.strict_draw_ordering)
2396 wglFlush();
2398 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2400 struct wined3d_device *device = surface->resource.device;
2401 unsigned int i;
2403 for (i = 0; i < device->context_count; ++i)
2405 context_surface_update(device->contexts[i], surface);
2410 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
2411 struct wined3d_surface *src_surface, const RECT *src_rect)
2413 const struct wined3d_format *src_format;
2414 const struct wined3d_format *dst_format;
2415 const struct wined3d_gl_info *gl_info;
2416 struct wined3d_context *context;
2417 struct wined3d_bo_address data;
2418 struct wined3d_format format;
2419 UINT update_w, update_h;
2420 CONVERT_TYPES convert;
2421 UINT dst_w, dst_h;
2422 UINT src_w, src_h;
2423 UINT src_pitch;
2424 POINT p;
2425 RECT r;
2427 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
2428 dst_surface, wine_dbgstr_point(dst_point),
2429 src_surface, wine_dbgstr_rect(src_rect));
2431 src_format = src_surface->resource.format;
2432 dst_format = dst_surface->resource.format;
2434 if (src_format->id != dst_format->id)
2436 WARN("Source and destination surfaces should have the same format.\n");
2437 return WINED3DERR_INVALIDCALL;
2440 if (!dst_point)
2442 p.x = 0;
2443 p.y = 0;
2444 dst_point = &p;
2446 else if (dst_point->x < 0 || dst_point->y < 0)
2448 WARN("Invalid destination point.\n");
2449 return WINED3DERR_INVALIDCALL;
2452 if (!src_rect)
2454 r.left = 0;
2455 r.top = 0;
2456 r.right = src_surface->resource.width;
2457 r.bottom = src_surface->resource.height;
2458 src_rect = &r;
2460 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
2461 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
2463 WARN("Invalid source rectangle.\n");
2464 return WINED3DERR_INVALIDCALL;
2467 src_w = src_surface->resource.width;
2468 src_h = src_surface->resource.height;
2470 dst_w = dst_surface->resource.width;
2471 dst_h = dst_surface->resource.height;
2473 update_w = src_rect->right - src_rect->left;
2474 update_h = src_rect->bottom - src_rect->top;
2476 if (update_w > dst_w || dst_point->x > dst_w - update_w
2477 || update_h > dst_h || dst_point->y > dst_h - update_h)
2479 WARN("Destination out of bounds.\n");
2480 return WINED3DERR_INVALIDCALL;
2483 /* NPOT block sizes would be silly. */
2484 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS)
2485 && ((update_w & (src_format->block_width - 1) || update_h & (src_format->block_height - 1))
2486 && (src_w != update_w || dst_w != update_w || src_h != update_h || dst_h != update_h)))
2488 WARN("Update rect not block-aligned.\n");
2489 return WINED3DERR_INVALIDCALL;
2492 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
2493 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
2494 if (convert != NO_CONVERSION || format.convert)
2496 RECT dst_rect = {dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h};
2497 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
2500 context = context_acquire(dst_surface->resource.device, NULL);
2501 gl_info = context->gl_info;
2503 /* Only load the surface for partial updates. For newly allocated texture
2504 * the texture wouldn't be the current location, and we'd upload zeroes
2505 * just to overwrite them again. */
2506 if (update_w == dst_w && update_h == dst_h)
2507 surface_prepare_texture(dst_surface, context, FALSE);
2508 else
2509 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
2510 surface_bind(dst_surface, context, FALSE);
2512 data.buffer_object = src_surface->pbo;
2513 data.addr = src_surface->resource.allocatedMemory;
2514 src_pitch = wined3d_surface_get_pitch(src_surface);
2516 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
2518 invalidate_active_texture(dst_surface->resource.device, context);
2520 context_release(context);
2522 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
2523 return WINED3D_OK;
2526 /* This call just allocates the texture, the caller is responsible for binding
2527 * the correct texture. */
2528 /* Context activation is done by the caller. */
2529 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2530 const struct wined3d_format *format, BOOL srgb)
2532 BOOL enable_client_storage = FALSE;
2533 GLsizei width = surface->pow2Width;
2534 GLsizei height = surface->pow2Height;
2535 const BYTE *mem = NULL;
2536 GLenum internal;
2538 if (srgb)
2540 internal = format->glGammaInternal;
2542 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2544 internal = format->rtInternal;
2546 else
2548 internal = format->glInternal;
2551 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2553 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",
2554 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2555 internal, width, height, format->glFormat, format->glType);
2557 ENTER_GL();
2559 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2561 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2562 || !surface->resource.allocatedMemory)
2564 /* In some cases we want to disable client storage.
2565 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2566 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2567 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2568 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2570 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2571 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2572 surface->flags &= ~SFLAG_CLIENT;
2573 enable_client_storage = TRUE;
2575 else
2577 surface->flags |= SFLAG_CLIENT;
2579 /* Point OpenGL to our allocated texture memory. Do not use
2580 * resource.allocatedMemory here because it might point into a
2581 * PBO. Instead use heapMemory, but get the alignment right. */
2582 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2583 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2587 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2589 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2590 internal, width, height, 0, surface->resource.size, mem));
2591 checkGLcall("glCompressedTexImage2DARB");
2593 else
2595 glTexImage2D(surface->texture_target, surface->texture_level,
2596 internal, width, height, 0, format->glFormat, format->glType, mem);
2597 checkGLcall("glTexImage2D");
2600 if(enable_client_storage) {
2601 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2602 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2604 LEAVE_GL();
2607 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2608 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2609 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2610 /* GL locking is done by the caller */
2611 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2613 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2614 struct wined3d_renderbuffer_entry *entry;
2615 GLuint renderbuffer = 0;
2616 unsigned int src_width, src_height;
2617 unsigned int width, height;
2619 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2621 width = rt->pow2Width;
2622 height = rt->pow2Height;
2624 else
2626 width = surface->pow2Width;
2627 height = surface->pow2Height;
2630 src_width = surface->pow2Width;
2631 src_height = surface->pow2Height;
2633 /* A depth stencil smaller than the render target is not valid */
2634 if (width > src_width || height > src_height) return;
2636 /* Remove any renderbuffer set if the sizes match */
2637 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2638 || (width == src_width && height == src_height))
2640 surface->current_renderbuffer = NULL;
2641 return;
2644 /* Look if we've already got a renderbuffer of the correct dimensions */
2645 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2647 if (entry->width == width && entry->height == height)
2649 renderbuffer = entry->id;
2650 surface->current_renderbuffer = entry;
2651 break;
2655 if (!renderbuffer)
2657 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2658 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2659 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2660 surface->resource.format->glInternal, width, height);
2662 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2663 entry->width = width;
2664 entry->height = height;
2665 entry->id = renderbuffer;
2666 list_add_head(&surface->renderbuffers, &entry->entry);
2668 surface->current_renderbuffer = entry;
2671 checkGLcall("set_compatible_renderbuffer");
2674 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2676 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2678 TRACE("surface %p.\n", surface);
2680 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2682 ERR("Surface %p is not on a swapchain.\n", surface);
2683 return GL_NONE;
2686 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2688 if (swapchain->render_to_fbo)
2690 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2691 return GL_COLOR_ATTACHMENT0;
2693 TRACE("Returning GL_BACK\n");
2694 return GL_BACK;
2696 else if (surface == swapchain->front_buffer)
2698 TRACE("Returning GL_FRONT\n");
2699 return GL_FRONT;
2702 FIXME("Higher back buffer, returning GL_BACK\n");
2703 return GL_BACK;
2706 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2707 void surface_add_dirty_rect(struct wined3d_surface *surface, const struct wined3d_box *dirty_rect)
2709 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2711 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2712 /* No partial locking for textures yet. */
2713 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2715 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2716 if (dirty_rect)
2718 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->left);
2719 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->top);
2720 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->right);
2721 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->bottom);
2723 else
2725 surface->dirtyRect.left = 0;
2726 surface->dirtyRect.top = 0;
2727 surface->dirtyRect.right = surface->resource.width;
2728 surface->dirtyRect.bottom = surface->resource.height;
2731 /* if the container is a texture then mark it dirty. */
2732 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2734 TRACE("Passing to container.\n");
2735 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2739 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2741 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2742 BOOL ck_changed;
2744 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2746 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
2748 ERR("Not supported on scratch surfaces.\n");
2749 return WINED3DERR_INVALIDCALL;
2752 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2754 /* Reload if either the texture and sysmem have different ideas about the
2755 * color key, or the actual key values changed. */
2756 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2757 && (surface->gl_color_key.color_space_low_value != surface->src_blt_color_key.color_space_low_value
2758 || surface->gl_color_key.color_space_high_value != surface->src_blt_color_key.color_space_high_value)))
2760 TRACE("Reloading because of color keying\n");
2761 /* To perform the color key conversion we need a sysmem copy of
2762 * the surface. Make sure we have it. */
2764 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2765 /* Make sure the texture is reloaded because of the color key change,
2766 * this kills performance though :( */
2767 /* TODO: This is not necessarily needed with hw palettized texture support. */
2768 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2769 /* Switching color keying on / off may change the internal format. */
2770 if (ck_changed)
2771 surface_force_reload(surface);
2773 else if (!(surface->flags & flag))
2775 TRACE("Reloading because surface is dirty.\n");
2777 else
2779 TRACE("surface is already in texture\n");
2780 return WINED3D_OK;
2783 /* No partial locking for textures yet. */
2784 surface_load_location(surface, flag, NULL);
2785 surface_evict_sysmem(surface);
2787 return WINED3D_OK;
2790 /* See also float_16_to_32() in wined3d_private.h */
2791 static inline unsigned short float_32_to_16(const float *in)
2793 int exp = 0;
2794 float tmp = fabsf(*in);
2795 unsigned int mantissa;
2796 unsigned short ret;
2798 /* Deal with special numbers */
2799 if (*in == 0.0f)
2800 return 0x0000;
2801 if (isnan(*in))
2802 return 0x7c01;
2803 if (isinf(*in))
2804 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2806 if (tmp < powf(2, 10))
2810 tmp = tmp * 2.0f;
2811 exp--;
2812 } while (tmp < powf(2, 10));
2814 else if (tmp >= powf(2, 11))
2818 tmp /= 2.0f;
2819 exp++;
2820 } while (tmp >= powf(2, 11));
2823 mantissa = (unsigned int)tmp;
2824 if (tmp - mantissa >= 0.5f)
2825 ++mantissa; /* Round to nearest, away from zero. */
2827 exp += 10; /* Normalize the mantissa. */
2828 exp += 15; /* Exponent is encoded with excess 15. */
2830 if (exp > 30) /* too big */
2832 ret = 0x7c00; /* INF */
2834 else if (exp <= 0)
2836 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2837 while (exp <= 0)
2839 mantissa = mantissa >> 1;
2840 ++exp;
2842 ret = mantissa & 0x3ff;
2844 else
2846 ret = (exp << 10) | (mantissa & 0x3ff);
2849 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2850 return ret;
2853 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2855 ULONG refcount;
2857 TRACE("Surface %p, container %p of type %#x.\n",
2858 surface, surface->container.u.base, surface->container.type);
2860 switch (surface->container.type)
2862 case WINED3D_CONTAINER_TEXTURE:
2863 return wined3d_texture_incref(surface->container.u.texture);
2865 case WINED3D_CONTAINER_SWAPCHAIN:
2866 return wined3d_swapchain_incref(surface->container.u.swapchain);
2868 default:
2869 ERR("Unhandled container type %#x.\n", surface->container.type);
2870 case WINED3D_CONTAINER_NONE:
2871 break;
2874 refcount = InterlockedIncrement(&surface->resource.ref);
2875 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2877 return refcount;
2880 /* Do not call while under the GL lock. */
2881 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2883 ULONG refcount;
2885 TRACE("Surface %p, container %p of type %#x.\n",
2886 surface, surface->container.u.base, surface->container.type);
2888 switch (surface->container.type)
2890 case WINED3D_CONTAINER_TEXTURE:
2891 return wined3d_texture_decref(surface->container.u.texture);
2893 case WINED3D_CONTAINER_SWAPCHAIN:
2894 return wined3d_swapchain_decref(surface->container.u.swapchain);
2896 default:
2897 ERR("Unhandled container type %#x.\n", surface->container.type);
2898 case WINED3D_CONTAINER_NONE:
2899 break;
2902 refcount = InterlockedDecrement(&surface->resource.ref);
2903 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2905 if (!refcount)
2907 surface_cleanup(surface);
2908 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2910 TRACE("Destroyed surface %p.\n", surface);
2911 HeapFree(GetProcessHeap(), 0, surface);
2914 return refcount;
2917 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2919 return resource_set_priority(&surface->resource, priority);
2922 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2924 return resource_get_priority(&surface->resource);
2927 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2929 TRACE("surface %p.\n", surface);
2931 if (!surface->resource.device->d3d_initialized)
2933 ERR("D3D not initialized.\n");
2934 return;
2937 surface_internal_preload(surface, SRGB_ANY);
2940 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2942 TRACE("surface %p.\n", surface);
2944 return surface->resource.parent;
2947 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2949 TRACE("surface %p.\n", surface);
2951 return &surface->resource;
2954 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2956 TRACE("surface %p, flags %#x.\n", surface, flags);
2958 switch (flags)
2960 case WINEDDGBS_CANBLT:
2961 case WINEDDGBS_ISBLTDONE:
2962 return WINED3D_OK;
2964 default:
2965 return WINED3DERR_INVALIDCALL;
2969 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2971 TRACE("surface %p, flags %#x.\n", surface, flags);
2973 /* XXX: DDERR_INVALIDSURFACETYPE */
2975 switch (flags)
2977 case WINEDDGFS_CANFLIP:
2978 case WINEDDGFS_ISFLIPDONE:
2979 return WINED3D_OK;
2981 default:
2982 return WINED3DERR_INVALIDCALL;
2986 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2988 TRACE("surface %p.\n", surface);
2990 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2991 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2994 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2996 TRACE("surface %p.\n", surface);
2998 surface->flags &= ~SFLAG_LOST;
2999 return WINED3D_OK;
3002 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
3004 TRACE("surface %p, palette %p.\n", surface, palette);
3006 if (surface->palette == palette)
3008 TRACE("Nop palette change.\n");
3009 return WINED3D_OK;
3012 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
3013 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3015 surface->palette = palette;
3017 if (palette)
3019 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3020 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
3022 surface->surface_ops->surface_realize_palette(surface);
3025 return WINED3D_OK;
3028 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
3029 DWORD flags, const struct wined3d_color_key *color_key)
3031 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3033 if (flags & WINEDDCKEY_COLORSPACE)
3035 FIXME(" colorkey value not supported (%08x) !\n", flags);
3036 return WINED3DERR_INVALIDCALL;
3039 /* Dirtify the surface, but only if a key was changed. */
3040 if (color_key)
3042 switch (flags & ~WINEDDCKEY_COLORSPACE)
3044 case WINEDDCKEY_DESTBLT:
3045 surface->dst_blt_color_key = *color_key;
3046 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3047 break;
3049 case WINEDDCKEY_DESTOVERLAY:
3050 surface->dst_overlay_color_key = *color_key;
3051 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3052 break;
3054 case WINEDDCKEY_SRCOVERLAY:
3055 surface->src_overlay_color_key = *color_key;
3056 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3057 break;
3059 case WINEDDCKEY_SRCBLT:
3060 surface->src_blt_color_key = *color_key;
3061 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3062 break;
3065 else
3067 switch (flags & ~WINEDDCKEY_COLORSPACE)
3069 case WINEDDCKEY_DESTBLT:
3070 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3071 break;
3073 case WINEDDCKEY_DESTOVERLAY:
3074 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3075 break;
3077 case WINEDDCKEY_SRCOVERLAY:
3078 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3079 break;
3081 case WINEDDCKEY_SRCBLT:
3082 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3083 break;
3087 return WINED3D_OK;
3090 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3092 TRACE("surface %p.\n", surface);
3094 return surface->palette;
3097 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3099 const struct wined3d_format *format = surface->resource.format;
3100 DWORD pitch;
3102 TRACE("surface %p.\n", surface);
3104 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
3106 /* Since compressed formats are block based, pitch means the amount of
3107 * bytes to the next row of block rather than the next row of pixels. */
3108 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3109 pitch = row_block_count * format->block_byte_count;
3111 else
3113 unsigned char alignment = surface->resource.device->surface_alignment;
3114 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3115 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3118 TRACE("Returning %u.\n", pitch);
3120 return pitch;
3123 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3125 TRACE("surface %p, mem %p.\n", surface, mem);
3127 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3129 WARN("Surface is locked or the DC is in use.\n");
3130 return WINED3DERR_INVALIDCALL;
3133 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
3134 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3136 ERR("Not supported on render targets.\n");
3137 return WINED3DERR_INVALIDCALL;
3140 if (mem && mem != surface->resource.allocatedMemory)
3142 void *release = NULL;
3144 /* Do I have to copy the old surface content? */
3145 if (surface->flags & SFLAG_DIBSECTION)
3147 DeleteDC(surface->hDC);
3148 DeleteObject(surface->dib.DIBsection);
3149 surface->dib.bitmap_data = NULL;
3150 surface->resource.allocatedMemory = NULL;
3151 surface->hDC = NULL;
3152 surface->flags &= ~SFLAG_DIBSECTION;
3154 else if (!(surface->flags & SFLAG_USERPTR))
3156 release = surface->resource.heapMemory;
3157 surface->resource.heapMemory = NULL;
3159 surface->resource.allocatedMemory = mem;
3160 surface->flags |= SFLAG_USERPTR;
3162 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
3163 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3165 /* For client textures OpenGL has to be notified. */
3166 if (surface->flags & SFLAG_CLIENT)
3167 surface_release_client_storage(surface);
3169 /* Now free the old memory if any. */
3170 HeapFree(GetProcessHeap(), 0, release);
3172 else if (surface->flags & SFLAG_USERPTR)
3174 /* HeapMemory should be NULL already. */
3175 if (surface->resource.heapMemory)
3176 ERR("User pointer surface has heap memory allocated.\n");
3178 if (!mem)
3180 surface->resource.allocatedMemory = NULL;
3181 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
3183 if (surface->flags & SFLAG_CLIENT)
3184 surface_release_client_storage(surface);
3186 surface_prepare_system_memory(surface);
3189 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3192 return WINED3D_OK;
3195 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3197 LONG w, h;
3199 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3201 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3203 WARN("Not an overlay surface.\n");
3204 return WINEDDERR_NOTAOVERLAYSURFACE;
3207 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3208 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3209 surface->overlay_destrect.left = x;
3210 surface->overlay_destrect.top = y;
3211 surface->overlay_destrect.right = x + w;
3212 surface->overlay_destrect.bottom = y + h;
3214 surface_draw_overlay(surface);
3216 return WINED3D_OK;
3219 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3221 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3223 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3225 TRACE("Not an overlay surface.\n");
3226 return WINEDDERR_NOTAOVERLAYSURFACE;
3229 if (!surface->overlay_dest)
3231 TRACE("Overlay not visible.\n");
3232 *x = 0;
3233 *y = 0;
3234 return WINEDDERR_OVERLAYNOTVISIBLE;
3237 *x = surface->overlay_destrect.left;
3238 *y = surface->overlay_destrect.top;
3240 TRACE("Returning position %d, %d.\n", *x, *y);
3242 return WINED3D_OK;
3245 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3246 DWORD flags, struct wined3d_surface *ref)
3248 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3250 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3252 TRACE("Not an overlay surface.\n");
3253 return WINEDDERR_NOTAOVERLAYSURFACE;
3256 return WINED3D_OK;
3259 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3260 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3262 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3263 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3265 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3267 WARN("Not an overlay surface.\n");
3268 return WINEDDERR_NOTAOVERLAYSURFACE;
3270 else if (!dst_surface)
3272 WARN("Dest surface is NULL.\n");
3273 return WINED3DERR_INVALIDCALL;
3276 if (src_rect)
3278 surface->overlay_srcrect = *src_rect;
3280 else
3282 surface->overlay_srcrect.left = 0;
3283 surface->overlay_srcrect.top = 0;
3284 surface->overlay_srcrect.right = surface->resource.width;
3285 surface->overlay_srcrect.bottom = surface->resource.height;
3288 if (dst_rect)
3290 surface->overlay_destrect = *dst_rect;
3292 else
3294 surface->overlay_destrect.left = 0;
3295 surface->overlay_destrect.top = 0;
3296 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3297 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3300 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3302 surface->overlay_dest = NULL;
3303 list_remove(&surface->overlay_entry);
3306 if (flags & WINEDDOVER_SHOW)
3308 if (surface->overlay_dest != dst_surface)
3310 surface->overlay_dest = dst_surface;
3311 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3314 else if (flags & WINEDDOVER_HIDE)
3316 /* tests show that the rectangles are erased on hide */
3317 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3318 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3319 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3320 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3321 surface->overlay_dest = NULL;
3324 surface_draw_overlay(surface);
3326 return WINED3D_OK;
3329 HRESULT CDECL wined3d_surface_update_desc(struct wined3d_surface *surface,
3330 UINT width, UINT height, enum wined3d_format_id format_id,
3331 enum wined3d_multisample_type multisample_type, UINT multisample_quality)
3333 struct wined3d_device *device = surface->resource.device;
3334 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3335 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
3336 UINT resource_size = wined3d_format_calculate_size(format, device->surface_alignment, width, height);
3338 TRACE("surface %p, width %u, height %u, format %s, multisample_type %#x, multisample_quality %u.\n",
3339 surface, width, height, debug_d3dformat(format_id), multisample_type, multisample_type);
3341 if (!resource_size)
3342 return WINED3DERR_INVALIDCALL;
3344 if (device->d3d_initialized)
3345 surface->resource.resource_ops->resource_unload(&surface->resource);
3347 if (surface->flags & SFLAG_DIBSECTION)
3349 DeleteDC(surface->hDC);
3350 DeleteObject(surface->dib.DIBsection);
3351 surface->dib.bitmap_data = NULL;
3352 surface->flags &= ~SFLAG_DIBSECTION;
3355 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_USERPTR);
3356 surface->resource.allocatedMemory = NULL;
3357 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
3358 surface->resource.heapMemory = NULL;
3360 surface->resource.width = width;
3361 surface->resource.height = height;
3362 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
3363 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
3365 surface->pow2Width = width;
3366 surface->pow2Height = height;
3368 else
3370 surface->pow2Width = surface->pow2Height = 1;
3371 while (surface->pow2Width < width)
3372 surface->pow2Width <<= 1;
3373 while (surface->pow2Height < height)
3374 surface->pow2Height <<= 1;
3377 if (surface->pow2Width != width || surface->pow2Height != height)
3378 surface->flags |= SFLAG_NONPOW2;
3379 else
3380 surface->flags &= ~SFLAG_NONPOW2;
3382 surface->resource.format = format;
3383 surface->resource.multisample_type = multisample_type;
3384 surface->resource.multisample_quality = multisample_quality;
3385 surface->resource.size = resource_size;
3387 if (!surface_init_sysmem(surface))
3388 return E_OUTOFMEMORY;
3390 return WINED3D_OK;
3393 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3394 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3396 unsigned short *dst_s;
3397 const float *src_f;
3398 unsigned int x, y;
3400 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3402 for (y = 0; y < h; ++y)
3404 src_f = (const float *)(src + y * pitch_in);
3405 dst_s = (unsigned short *) (dst + y * pitch_out);
3406 for (x = 0; x < w; ++x)
3408 dst_s[x] = float_32_to_16(src_f + x);
3413 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3414 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3416 static const unsigned char convert_5to8[] =
3418 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3419 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3420 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3421 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3423 static const unsigned char convert_6to8[] =
3425 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3426 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3427 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3428 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3429 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3430 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3431 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3432 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3434 unsigned int x, y;
3436 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3438 for (y = 0; y < h; ++y)
3440 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3441 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3442 for (x = 0; x < w; ++x)
3444 WORD pixel = src_line[x];
3445 dst_line[x] = 0xff000000
3446 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3447 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3448 | convert_5to8[(pixel & 0x001f)];
3453 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3454 * in both cases we're just setting the X / Alpha channel to 0xff. */
3455 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3456 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3458 unsigned int x, y;
3460 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3462 for (y = 0; y < h; ++y)
3464 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3465 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3467 for (x = 0; x < w; ++x)
3469 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3474 static inline BYTE cliptobyte(int x)
3476 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3479 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3480 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3482 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3483 unsigned int x, y;
3485 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3487 for (y = 0; y < h; ++y)
3489 const BYTE *src_line = src + y * pitch_in;
3490 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3491 for (x = 0; x < w; ++x)
3493 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3494 * C = Y - 16; D = U - 128; E = V - 128;
3495 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3496 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3497 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3498 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3499 * U and V are shared between the pixels. */
3500 if (!(x & 1)) /* For every even pixel, read new U and V. */
3502 d = (int) src_line[1] - 128;
3503 e = (int) src_line[3] - 128;
3504 r2 = 409 * e + 128;
3505 g2 = - 100 * d - 208 * e + 128;
3506 b2 = 516 * d + 128;
3508 c2 = 298 * ((int) src_line[0] - 16);
3509 dst_line[x] = 0xff000000
3510 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3511 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3512 | cliptobyte((c2 + b2) >> 8); /* blue */
3513 /* Scale RGB values to 0..255 range,
3514 * then clip them if still not in range (may be negative),
3515 * then shift them within DWORD if necessary. */
3516 src_line += 2;
3521 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3522 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3524 unsigned int x, y;
3525 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3527 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3529 for (y = 0; y < h; ++y)
3531 const BYTE *src_line = src + y * pitch_in;
3532 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3533 for (x = 0; x < w; ++x)
3535 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3536 * C = Y - 16; D = U - 128; E = V - 128;
3537 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3538 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3539 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3540 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3541 * U and V are shared between the pixels. */
3542 if (!(x & 1)) /* For every even pixel, read new U and V. */
3544 d = (int) src_line[1] - 128;
3545 e = (int) src_line[3] - 128;
3546 r2 = 409 * e + 128;
3547 g2 = - 100 * d - 208 * e + 128;
3548 b2 = 516 * d + 128;
3550 c2 = 298 * ((int) src_line[0] - 16);
3551 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3552 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3553 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3554 /* Scale RGB values to 0..255 range,
3555 * then clip them if still not in range (may be negative),
3556 * then shift them within DWORD if necessary. */
3557 src_line += 2;
3562 struct d3dfmt_convertor_desc
3564 enum wined3d_format_id from, to;
3565 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3568 static const struct d3dfmt_convertor_desc convertors[] =
3570 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3571 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3572 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3573 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3574 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3575 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3578 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3579 enum wined3d_format_id to)
3581 unsigned int i;
3583 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3585 if (convertors[i].from == from && convertors[i].to == to)
3586 return &convertors[i];
3589 return NULL;
3592 /*****************************************************************************
3593 * surface_convert_format
3595 * Creates a duplicate of a surface in a different format. Is used by Blt to
3596 * blit between surfaces with different formats.
3598 * Parameters
3599 * source: Source surface
3600 * fmt: Requested destination format
3602 *****************************************************************************/
3603 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3605 struct wined3d_mapped_rect src_map, dst_map;
3606 const struct d3dfmt_convertor_desc *conv;
3607 struct wined3d_surface *ret = NULL;
3608 HRESULT hr;
3610 conv = find_convertor(source->resource.format->id, to_fmt);
3611 if (!conv)
3613 FIXME("Cannot find a conversion function from format %s to %s.\n",
3614 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3615 return NULL;
3618 wined3d_surface_create(source->resource.device, source->resource.width,
3619 source->resource.height, to_fmt, 0 /* level */, 0 /* usage */, WINED3D_POOL_SCRATCH,
3620 WINED3D_MULTISAMPLE_NONE /* TODO: Multisampled conversion */, 0 /* MultiSampleQuality */,
3621 source->surface_type, WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD,
3622 NULL /* parent */, &wined3d_null_parent_ops, &ret);
3623 if (!ret)
3625 ERR("Failed to create a destination surface for conversion.\n");
3626 return NULL;
3629 memset(&src_map, 0, sizeof(src_map));
3630 memset(&dst_map, 0, sizeof(dst_map));
3632 hr = wined3d_surface_map(source, &src_map, NULL, WINED3DLOCK_READONLY);
3633 if (FAILED(hr))
3635 ERR("Failed to lock the source surface.\n");
3636 wined3d_surface_decref(ret);
3637 return NULL;
3639 hr = wined3d_surface_map(ret, &dst_map, NULL, WINED3DLOCK_READONLY);
3640 if (FAILED(hr))
3642 ERR("Failed to lock the destination surface.\n");
3643 wined3d_surface_unmap(source);
3644 wined3d_surface_decref(ret);
3645 return NULL;
3648 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
3649 source->resource.width, source->resource.height);
3651 wined3d_surface_unmap(ret);
3652 wined3d_surface_unmap(source);
3654 return ret;
3657 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3658 unsigned int bpp, UINT pitch, DWORD color)
3660 BYTE *first;
3661 int x, y;
3663 /* Do first row */
3665 #define COLORFILL_ROW(type) \
3666 do { \
3667 type *d = (type *)buf; \
3668 for (x = 0; x < width; ++x) \
3669 d[x] = (type)color; \
3670 } while(0)
3672 switch (bpp)
3674 case 1:
3675 COLORFILL_ROW(BYTE);
3676 break;
3678 case 2:
3679 COLORFILL_ROW(WORD);
3680 break;
3682 case 3:
3684 BYTE *d = buf;
3685 for (x = 0; x < width; ++x, d += 3)
3687 d[0] = (color ) & 0xFF;
3688 d[1] = (color >> 8) & 0xFF;
3689 d[2] = (color >> 16) & 0xFF;
3691 break;
3693 case 4:
3694 COLORFILL_ROW(DWORD);
3695 break;
3697 default:
3698 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3699 return WINED3DERR_NOTAVAILABLE;
3702 #undef COLORFILL_ROW
3704 /* Now copy first row. */
3705 first = buf;
3706 for (y = 1; y < height; ++y)
3708 buf += pitch;
3709 memcpy(buf, first, width * bpp);
3712 return WINED3D_OK;
3715 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3717 TRACE("surface %p.\n", surface);
3719 if (!(surface->flags & SFLAG_LOCKED))
3721 WARN("Trying to unmap unmapped surface.\n");
3722 return WINEDDERR_NOTLOCKED;
3724 surface->flags &= ~SFLAG_LOCKED;
3726 surface->surface_ops->surface_unmap(surface);
3728 return WINED3D_OK;
3731 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3732 struct wined3d_mapped_rect *mapped_rect, const RECT *rect, DWORD flags)
3734 const struct wined3d_format *format = surface->resource.format;
3736 TRACE("surface %p, mapped_rect %p, rect %s, flags %#x.\n",
3737 surface, mapped_rect, wine_dbgstr_rect(rect), flags);
3739 if (surface->flags & SFLAG_LOCKED)
3741 WARN("Surface is already mapped.\n");
3742 return WINED3DERR_INVALIDCALL;
3744 if ((format->flags & WINED3DFMT_FLAG_BLOCKS)
3745 && rect && (rect->left || rect->top
3746 || rect->right != surface->resource.width
3747 || rect->bottom != surface->resource.height))
3749 UINT width_mask = format->block_width - 1;
3750 UINT height_mask = format->block_height - 1;
3752 if ((rect->left & width_mask) || (rect->right & width_mask)
3753 || (rect->top & height_mask) || (rect->bottom & height_mask))
3755 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3756 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3758 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
3759 return WINED3DERR_INVALIDCALL;
3763 surface->flags |= SFLAG_LOCKED;
3765 if (!(surface->flags & SFLAG_LOCKABLE))
3766 WARN("Trying to lock unlockable surface.\n");
3768 /* Performance optimization: Count how often a surface is mapped, if it is
3769 * mapped regularly do not throw away the system memory copy. This avoids
3770 * the need to download the surface from OpenGL all the time. The surface
3771 * is still downloaded if the OpenGL texture is changed. */
3772 if (!(surface->flags & SFLAG_DYNLOCK))
3774 if (++surface->lockCount > MAXLOCKCOUNT)
3776 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3777 surface->flags |= SFLAG_DYNLOCK;
3781 surface->surface_ops->surface_map(surface, rect, flags);
3783 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3784 mapped_rect->row_pitch = surface->resource.width * format->byte_count;
3785 else
3786 mapped_rect->row_pitch = wined3d_surface_get_pitch(surface);
3788 if (!rect)
3790 mapped_rect->data = surface->resource.allocatedMemory;
3791 surface->lockedRect.left = 0;
3792 surface->lockedRect.top = 0;
3793 surface->lockedRect.right = surface->resource.width;
3794 surface->lockedRect.bottom = surface->resource.height;
3796 else
3798 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3800 /* Compressed textures are block based, so calculate the offset of
3801 * the block that contains the top-left pixel of the locked rectangle. */
3802 mapped_rect->data = surface->resource.allocatedMemory
3803 + ((rect->top / format->block_height) * mapped_rect->row_pitch)
3804 + ((rect->left / format->block_width) * format->block_byte_count);
3806 else
3808 mapped_rect->data = surface->resource.allocatedMemory
3809 + (mapped_rect->row_pitch * rect->top)
3810 + (rect->left * format->byte_count);
3812 surface->lockedRect.left = rect->left;
3813 surface->lockedRect.top = rect->top;
3814 surface->lockedRect.right = rect->right;
3815 surface->lockedRect.bottom = rect->bottom;
3818 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3819 TRACE("Returning memory %p, pitch %u.\n", mapped_rect->data, mapped_rect->row_pitch);
3821 return WINED3D_OK;
3824 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3826 struct wined3d_mapped_rect map;
3827 HRESULT hr;
3829 TRACE("surface %p, dc %p.\n", surface, dc);
3831 if (surface->flags & SFLAG_USERPTR)
3833 ERR("Not supported on surfaces with application-provided memory.\n");
3834 return WINEDDERR_NODC;
3837 /* Give more detailed info for ddraw. */
3838 if (surface->flags & SFLAG_DCINUSE)
3839 return WINEDDERR_DCALREADYCREATED;
3841 /* Can't GetDC if the surface is locked. */
3842 if (surface->flags & SFLAG_LOCKED)
3843 return WINED3DERR_INVALIDCALL;
3845 /* Create a DIB section if there isn't a dc yet. */
3846 if (!surface->hDC)
3848 if (surface->flags & SFLAG_CLIENT)
3850 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3851 surface_release_client_storage(surface);
3853 hr = surface_create_dib_section(surface);
3854 if (FAILED(hr))
3855 return WINED3DERR_INVALIDCALL;
3857 /* Use the DIB section from now on if we are not using a PBO. */
3858 if (!(surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)))
3860 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
3861 surface->resource.heapMemory = NULL;
3862 surface->resource.allocatedMemory = surface->dib.bitmap_data;
3866 /* Map the surface. */
3867 hr = wined3d_surface_map(surface, &map, NULL, 0);
3868 if (FAILED(hr))
3870 ERR("Map failed, hr %#x.\n", hr);
3871 return hr;
3874 /* Sync the DIB with the PBO. This can't be done earlier because Map()
3875 * activates the allocatedMemory. */
3876 if (surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM))
3877 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
3879 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3880 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3882 /* GetDC on palettized formats is unsupported in D3D9, and the method
3883 * is missing in D3D8, so this should only be used for DX <=7
3884 * surfaces (with non-device palettes). */
3885 const PALETTEENTRY *pal = NULL;
3887 if (surface->palette)
3889 pal = surface->palette->palents;
3891 else
3893 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3894 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3896 if (dds_primary && dds_primary->palette)
3897 pal = dds_primary->palette->palents;
3900 if (pal)
3902 RGBQUAD col[256];
3903 unsigned int i;
3905 for (i = 0; i < 256; ++i)
3907 col[i].rgbRed = pal[i].peRed;
3908 col[i].rgbGreen = pal[i].peGreen;
3909 col[i].rgbBlue = pal[i].peBlue;
3910 col[i].rgbReserved = 0;
3912 SetDIBColorTable(surface->hDC, 0, 256, col);
3916 surface->flags |= SFLAG_DCINUSE;
3918 *dc = surface->hDC;
3919 TRACE("Returning dc %p.\n", *dc);
3921 return WINED3D_OK;
3924 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3926 TRACE("surface %p, dc %p.\n", surface, dc);
3928 if (!(surface->flags & SFLAG_DCINUSE))
3929 return WINEDDERR_NODC;
3931 if (surface->hDC != dc)
3933 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3934 dc, surface->hDC);
3935 return WINEDDERR_NODC;
3938 /* Copy the contents of the DIB over to the PBO. */
3939 if ((surface->flags & (SFLAG_PBO | SFLAG_PIN_SYSMEM)) && surface->resource.allocatedMemory)
3940 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3942 /* We locked first, so unlock now. */
3943 wined3d_surface_unmap(surface);
3945 surface->flags &= ~SFLAG_DCINUSE;
3947 return WINED3D_OK;
3950 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3952 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3954 if (flags)
3956 static UINT once;
3957 if (!once++)
3958 FIXME("Ignoring flags %#x.\n", flags);
3959 else
3960 WARN("Ignoring flags %#x.\n", flags);
3963 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
3965 ERR("Not supported on swapchain surfaces.\n");
3966 return WINEDDERR_NOTFLIPPABLE;
3969 /* Flipping is only supported on render targets and overlays. */
3970 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
3972 WARN("Tried to flip a non-render target, non-overlay surface.\n");
3973 return WINEDDERR_NOTFLIPPABLE;
3976 flip_surface(surface, override);
3978 /* Update overlays if they're visible. */
3979 if ((surface->resource.usage & WINED3DUSAGE_OVERLAY) && surface->overlay_dest)
3980 return surface_draw_overlay(surface);
3982 return WINED3D_OK;
3985 /* Do not call while under the GL lock. */
3986 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3988 struct wined3d_device *device = surface->resource.device;
3990 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3992 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3994 struct wined3d_texture *texture = surface->container.u.texture;
3996 TRACE("Passing to container (%p).\n", texture);
3997 texture->texture_ops->texture_preload(texture, srgb);
3999 else
4001 struct wined3d_context *context;
4003 TRACE("(%p) : About to load surface\n", surface);
4005 /* TODO: Use already acquired context when possible. */
4006 context = context_acquire(device, NULL);
4008 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
4010 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
4012 /* Tell opengl to try and keep this texture in video ram (well mostly) */
4013 GLclampf tmp;
4014 tmp = 0.9f;
4015 ENTER_GL();
4016 glPrioritizeTextures(1, &surface->texture_name, &tmp);
4017 LEAVE_GL();
4020 context_release(context);
4024 BOOL surface_init_sysmem(struct wined3d_surface *surface)
4026 if (!surface->resource.allocatedMemory)
4028 if (!surface->resource.heapMemory)
4030 if (!(surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
4031 surface->resource.size + RESOURCE_ALIGNMENT)))
4033 ERR("Failed to allocate memory.\n");
4034 return FALSE;
4037 else if (!(surface->flags & SFLAG_CLIENT))
4039 ERR("Surface %p has heapMemory %p and flags %#x.\n",
4040 surface, surface->resource.heapMemory, surface->flags);
4043 surface->resource.allocatedMemory =
4044 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
4046 else
4048 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
4051 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
4053 return TRUE;
4056 /* Read the framebuffer back into the surface */
4057 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
4059 struct wined3d_device *device = surface->resource.device;
4060 const struct wined3d_gl_info *gl_info;
4061 struct wined3d_context *context;
4062 BYTE *mem;
4063 GLint fmt;
4064 GLint type;
4065 BYTE *row, *top, *bottom;
4066 int i;
4067 BOOL bpp;
4068 RECT local_rect;
4069 BOOL srcIsUpsideDown;
4070 GLint rowLen = 0;
4071 GLint skipPix = 0;
4072 GLint skipRow = 0;
4074 context = context_acquire(device, surface);
4075 context_apply_blit_state(context, device);
4076 gl_info = context->gl_info;
4078 ENTER_GL();
4080 /* Select the correct read buffer, and give some debug output.
4081 * There is no need to keep track of the current read buffer or reset it, every part of the code
4082 * that reads sets the read buffer as desired.
4084 if (surface_is_offscreen(surface))
4086 /* Mapping the primary render target which is not on a swapchain.
4087 * Read from the back buffer. */
4088 TRACE("Mapping offscreen render target.\n");
4089 glReadBuffer(device->offscreenBuffer);
4090 srcIsUpsideDown = TRUE;
4092 else
4094 /* Onscreen surfaces are always part of a swapchain */
4095 GLenum buffer = surface_get_gl_buffer(surface);
4096 TRACE("Mapping %#x buffer.\n", buffer);
4097 glReadBuffer(buffer);
4098 checkGLcall("glReadBuffer");
4099 srcIsUpsideDown = FALSE;
4102 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
4103 if (!rect)
4105 local_rect.left = 0;
4106 local_rect.top = 0;
4107 local_rect.right = surface->resource.width;
4108 local_rect.bottom = surface->resource.height;
4110 else
4112 local_rect = *rect;
4114 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
4116 switch (surface->resource.format->id)
4118 case WINED3DFMT_P8_UINT:
4120 if (primary_render_target_is_p8(device))
4122 /* In case of P8 render targets the index is stored in the alpha component */
4123 fmt = GL_ALPHA;
4124 type = GL_UNSIGNED_BYTE;
4125 mem = dest;
4126 bpp = surface->resource.format->byte_count;
4128 else
4130 /* GL can't return palettized data, so read ARGB pixels into a
4131 * separate block of memory and convert them into palettized format
4132 * in software. Slow, but if the app means to use palettized render
4133 * targets and locks it...
4135 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4136 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4137 * for the color channels when palettizing the colors.
4139 fmt = GL_RGB;
4140 type = GL_UNSIGNED_BYTE;
4141 pitch *= 3;
4142 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4143 if (!mem)
4145 ERR("Out of memory\n");
4146 LEAVE_GL();
4147 return;
4149 bpp = surface->resource.format->byte_count * 3;
4152 break;
4154 default:
4155 mem = dest;
4156 fmt = surface->resource.format->glFormat;
4157 type = surface->resource.format->glType;
4158 bpp = surface->resource.format->byte_count;
4161 if (surface->flags & SFLAG_PBO)
4163 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4164 checkGLcall("glBindBufferARB");
4165 if (mem)
4167 ERR("mem not null for pbo -- unexpected\n");
4168 mem = NULL;
4172 /* Save old pixel store pack state */
4173 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4174 checkGLcall("glGetIntegerv");
4175 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4176 checkGLcall("glGetIntegerv");
4177 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4178 checkGLcall("glGetIntegerv");
4180 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4181 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4182 checkGLcall("glPixelStorei");
4183 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4184 checkGLcall("glPixelStorei");
4185 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4186 checkGLcall("glPixelStorei");
4188 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4189 local_rect.right - local_rect.left,
4190 local_rect.bottom - local_rect.top,
4191 fmt, type, mem);
4192 checkGLcall("glReadPixels");
4194 /* Reset previous pixel store pack state */
4195 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4196 checkGLcall("glPixelStorei");
4197 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4198 checkGLcall("glPixelStorei");
4199 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4200 checkGLcall("glPixelStorei");
4202 if (surface->flags & SFLAG_PBO)
4204 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4205 checkGLcall("glBindBufferARB");
4207 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4208 * to get a pointer to it and perform the flipping in software. This is a lot
4209 * faster than calling glReadPixels for each line. In case we want more speed
4210 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4211 if (!srcIsUpsideDown)
4213 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4214 checkGLcall("glBindBufferARB");
4216 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4217 checkGLcall("glMapBufferARB");
4221 /* TODO: Merge this with the palettization loop below for P8 targets */
4222 if(!srcIsUpsideDown) {
4223 UINT len, off;
4224 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4225 Flip the lines in software */
4226 len = (local_rect.right - local_rect.left) * bpp;
4227 off = local_rect.left * bpp;
4229 row = HeapAlloc(GetProcessHeap(), 0, len);
4230 if(!row) {
4231 ERR("Out of memory\n");
4232 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4233 HeapFree(GetProcessHeap(), 0, mem);
4234 LEAVE_GL();
4235 return;
4238 top = mem + pitch * local_rect.top;
4239 bottom = mem + pitch * (local_rect.bottom - 1);
4240 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4241 memcpy(row, top + off, len);
4242 memcpy(top + off, bottom + off, len);
4243 memcpy(bottom + off, row, len);
4244 top += pitch;
4245 bottom -= pitch;
4247 HeapFree(GetProcessHeap(), 0, row);
4249 /* Unmap the temp PBO buffer */
4250 if (surface->flags & SFLAG_PBO)
4252 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4253 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4257 LEAVE_GL();
4258 context_release(context);
4260 /* For P8 textures we need to perform an inverse palette lookup. This is
4261 * done by searching for a palette index which matches the RGB value.
4262 * Note this isn't guaranteed to work when there are multiple entries for
4263 * the same color but we have no choice. In case of P8 render targets,
4264 * the index is stored in the alpha component so no conversion is needed. */
4265 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4267 const PALETTEENTRY *pal = NULL;
4268 DWORD width = pitch / 3;
4269 int x, y, c;
4271 if (surface->palette)
4273 pal = surface->palette->palents;
4275 else
4277 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4278 HeapFree(GetProcessHeap(), 0, mem);
4279 return;
4282 for(y = local_rect.top; y < local_rect.bottom; y++) {
4283 for(x = local_rect.left; x < local_rect.right; x++) {
4284 /* start lines pixels */
4285 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4286 const BYTE *green = blue + 1;
4287 const BYTE *red = green + 1;
4289 for(c = 0; c < 256; c++) {
4290 if(*red == pal[c].peRed &&
4291 *green == pal[c].peGreen &&
4292 *blue == pal[c].peBlue)
4294 *((BYTE *) dest + y * width + x) = c;
4295 break;
4300 HeapFree(GetProcessHeap(), 0, mem);
4304 /* Read the framebuffer contents into a texture. Note that this function
4305 * doesn't do any kind of flipping. Using this on an onscreen surface will
4306 * result in a flipped D3D texture. */
4307 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
4309 struct wined3d_device *device = surface->resource.device;
4310 struct wined3d_context *context;
4312 context = context_acquire(device, surface);
4313 device_invalidate_state(device, STATE_FRAMEBUFFER);
4315 surface_prepare_texture(surface, context, srgb);
4316 surface_bind_and_dirtify(surface, context, srgb);
4318 TRACE("Reading back offscreen render target %p.\n", surface);
4320 ENTER_GL();
4322 if (surface_is_offscreen(surface))
4323 glReadBuffer(device->offscreenBuffer);
4324 else
4325 glReadBuffer(surface_get_gl_buffer(surface));
4326 checkGLcall("glReadBuffer");
4328 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4329 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4330 checkGLcall("glCopyTexSubImage2D");
4332 LEAVE_GL();
4334 context_release(context);
4337 /* Context activation is done by the caller. */
4338 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4339 struct wined3d_context *context, BOOL srgb)
4341 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4342 CONVERT_TYPES convert;
4343 struct wined3d_format format;
4345 if (surface->flags & alloc_flag) return;
4347 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4348 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4349 else surface->flags &= ~SFLAG_CONVERTED;
4351 surface_bind_and_dirtify(surface, context, srgb);
4352 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4353 surface->flags |= alloc_flag;
4356 /* Context activation is done by the caller. */
4357 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4359 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4361 struct wined3d_texture *texture = surface->container.u.texture;
4362 UINT sub_count = texture->level_count * texture->layer_count;
4363 UINT i;
4365 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4367 for (i = 0; i < sub_count; ++i)
4369 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4370 surface_prepare_texture_internal(s, context, srgb);
4373 return;
4376 surface_prepare_texture_internal(surface, context, srgb);
4379 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4381 if (multisample)
4383 if (surface->rb_multisample)
4384 return;
4386 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4387 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4388 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4389 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4390 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4392 else
4394 if (surface->rb_resolved)
4395 return;
4397 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4398 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4399 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4400 surface->pow2Width, surface->pow2Height);
4401 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4405 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4406 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4408 struct wined3d_device *device = surface->resource.device;
4409 UINT pitch = wined3d_surface_get_pitch(surface);
4410 const struct wined3d_gl_info *gl_info;
4411 struct wined3d_context *context;
4412 RECT local_rect;
4413 UINT w, h;
4415 surface_get_rect(surface, rect, &local_rect);
4417 mem += local_rect.top * pitch + local_rect.left * bpp;
4418 w = local_rect.right - local_rect.left;
4419 h = local_rect.bottom - local_rect.top;
4421 /* Activate the correct context for the render target */
4422 context = context_acquire(device, surface);
4423 context_apply_blit_state(context, device);
4424 gl_info = context->gl_info;
4426 ENTER_GL();
4428 if (!surface_is_offscreen(surface))
4430 GLenum buffer = surface_get_gl_buffer(surface);
4431 TRACE("Unlocking %#x buffer.\n", buffer);
4432 context_set_draw_buffer(context, buffer);
4434 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4435 glPixelZoom(1.0f, -1.0f);
4437 else
4439 /* Primary offscreen render target */
4440 TRACE("Offscreen render target.\n");
4441 context_set_draw_buffer(context, device->offscreenBuffer);
4443 glPixelZoom(1.0f, 1.0f);
4446 glRasterPos3i(local_rect.left, local_rect.top, 1);
4447 checkGLcall("glRasterPos3i");
4449 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4450 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4452 if (surface->flags & SFLAG_PBO)
4454 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4455 checkGLcall("glBindBufferARB");
4458 glDrawPixels(w, h, fmt, type, mem);
4459 checkGLcall("glDrawPixels");
4461 if (surface->flags & SFLAG_PBO)
4463 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4464 checkGLcall("glBindBufferARB");
4467 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4468 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4470 LEAVE_GL();
4472 if (wined3d_settings.strict_draw_ordering
4473 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4474 && surface->container.u.swapchain->front_buffer == surface))
4475 wglFlush();
4477 context_release(context);
4480 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4481 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4483 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4484 const struct wined3d_device *device = surface->resource.device;
4485 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4486 BOOL blit_supported = FALSE;
4488 /* Copy the default values from the surface. Below we might perform fixups */
4489 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4490 *format = *surface->resource.format;
4491 *convert = NO_CONVERSION;
4493 /* Ok, now look if we have to do any conversion */
4494 switch (surface->resource.format->id)
4496 case WINED3DFMT_P8_UINT:
4497 /* Below the call to blit_supported is disabled for Wine 1.2
4498 * because the function isn't operating correctly yet. At the
4499 * moment 8-bit blits are handled in software and if certain GL
4500 * extensions are around, surface conversion is performed at
4501 * upload time. The blit_supported call recognizes it as a
4502 * destination fixup. This type of upload 'fixup' and 8-bit to
4503 * 8-bit blits need to be handled by the blit_shader.
4504 * TODO: get rid of this #if 0. */
4505 #if 0
4506 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4507 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4508 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4509 #endif
4510 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4512 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4513 * texturing. Further also use conversion in case of color keying.
4514 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4515 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4516 * conflicts with this.
4518 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4519 || colorkey_active || !use_texturing)
4521 format->glFormat = GL_RGBA;
4522 format->glInternal = GL_RGBA;
4523 format->glType = GL_UNSIGNED_BYTE;
4524 format->conv_byte_count = 4;
4525 if (colorkey_active)
4526 *convert = CONVERT_PALETTED_CK;
4527 else
4528 *convert = CONVERT_PALETTED;
4530 break;
4532 case WINED3DFMT_B2G3R3_UNORM:
4533 /* **********************
4534 GL_UNSIGNED_BYTE_3_3_2
4535 ********************** */
4536 if (colorkey_active) {
4537 /* This texture format will never be used.. So do not care about color keying
4538 up until the point in time it will be needed :-) */
4539 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4541 break;
4543 case WINED3DFMT_B5G6R5_UNORM:
4544 if (colorkey_active)
4546 *convert = CONVERT_CK_565;
4547 format->glFormat = GL_RGBA;
4548 format->glInternal = GL_RGB5_A1;
4549 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4550 format->conv_byte_count = 2;
4552 break;
4554 case WINED3DFMT_B5G5R5X1_UNORM:
4555 if (colorkey_active)
4557 *convert = CONVERT_CK_5551;
4558 format->glFormat = GL_BGRA;
4559 format->glInternal = GL_RGB5_A1;
4560 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4561 format->conv_byte_count = 2;
4563 break;
4565 case WINED3DFMT_B8G8R8_UNORM:
4566 if (colorkey_active)
4568 *convert = CONVERT_CK_RGB24;
4569 format->glFormat = GL_RGBA;
4570 format->glInternal = GL_RGBA8;
4571 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4572 format->conv_byte_count = 4;
4574 break;
4576 case WINED3DFMT_B8G8R8X8_UNORM:
4577 if (colorkey_active)
4579 *convert = CONVERT_RGB32_888;
4580 format->glFormat = GL_RGBA;
4581 format->glInternal = GL_RGBA8;
4582 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4583 format->conv_byte_count = 4;
4585 break;
4587 default:
4588 break;
4591 if (*convert != NO_CONVERSION)
4593 format->rtInternal = format->glInternal;
4594 format->glGammaInternal = format->glInternal;
4597 return WINED3D_OK;
4600 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
4602 /* FIXME: Is this really how color keys are supposed to work? I think it
4603 * makes more sense to compare the individual channels. */
4604 return color >= color_key->color_space_low_value
4605 && color <= color_key->color_space_high_value;
4608 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4610 const struct wined3d_device *device = surface->resource.device;
4611 const struct wined3d_palette *pal = surface->palette;
4612 BOOL index_in_alpha = FALSE;
4613 unsigned int i;
4615 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4616 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4617 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4618 * duplicate entries. Store the color key in the unused alpha component to speed the
4619 * download up and to make conversion unneeded. */
4620 index_in_alpha = primary_render_target_is_p8(device);
4622 if (!pal)
4624 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4625 if (index_in_alpha)
4627 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4628 * there's no palette at this time. */
4629 for (i = 0; i < 256; i++) table[i][3] = i;
4632 else
4634 TRACE("Using surface palette %p\n", pal);
4635 /* Get the surface's palette */
4636 for (i = 0; i < 256; ++i)
4638 table[i][0] = pal->palents[i].peRed;
4639 table[i][1] = pal->palents[i].peGreen;
4640 table[i][2] = pal->palents[i].peBlue;
4642 /* When index_in_alpha is set the palette index is stored in the
4643 * alpha component. In case of a readback we can then read
4644 * GL_ALPHA. Color keying is handled in BltOverride using a
4645 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4646 * color key itself is passed to glAlphaFunc in other cases the
4647 * alpha component of pixels that should be masked away is set to 0. */
4648 if (index_in_alpha)
4649 table[i][3] = i;
4650 else if (colorkey && color_in_range(&surface->src_blt_color_key, i))
4651 table[i][3] = 0x00;
4652 else if (pal->flags & WINEDDPCAPS_ALPHA)
4653 table[i][3] = pal->palents[i].peFlags;
4654 else
4655 table[i][3] = 0xFF;
4660 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4661 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4663 const BYTE *source;
4664 BYTE *dest;
4665 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4667 switch (convert) {
4668 case NO_CONVERSION:
4670 memcpy(dst, src, pitch * height);
4671 break;
4673 case CONVERT_PALETTED:
4674 case CONVERT_PALETTED_CK:
4676 BYTE table[256][4];
4677 unsigned int x, y;
4679 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4681 for (y = 0; y < height; y++)
4683 source = src + pitch * y;
4684 dest = dst + outpitch * y;
4685 /* This is an 1 bpp format, using the width here is fine */
4686 for (x = 0; x < width; x++) {
4687 BYTE color = *source++;
4688 *dest++ = table[color][0];
4689 *dest++ = table[color][1];
4690 *dest++ = table[color][2];
4691 *dest++ = table[color][3];
4695 break;
4697 case CONVERT_CK_565:
4699 /* Converting the 565 format in 5551 packed to emulate color-keying.
4701 Note : in all these conversion, it would be best to average the averaging
4702 pixels to get the color of the pixel that will be color-keyed to
4703 prevent 'color bleeding'. This will be done later on if ever it is
4704 too visible.
4706 Note2: Nvidia documents say that their driver does not support alpha + color keying
4707 on the same surface and disables color keying in such a case
4709 unsigned int x, y;
4710 const WORD *Source;
4711 WORD *Dest;
4713 TRACE("Color keyed 565\n");
4715 for (y = 0; y < height; y++) {
4716 Source = (const WORD *)(src + y * pitch);
4717 Dest = (WORD *) (dst + y * outpitch);
4718 for (x = 0; x < width; x++ ) {
4719 WORD color = *Source++;
4720 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4721 if (!color_in_range(&surface->src_blt_color_key, color))
4722 *Dest |= 0x0001;
4723 Dest++;
4727 break;
4729 case CONVERT_CK_5551:
4731 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4732 unsigned int x, y;
4733 const WORD *Source;
4734 WORD *Dest;
4735 TRACE("Color keyed 5551\n");
4736 for (y = 0; y < height; y++) {
4737 Source = (const WORD *)(src + y * pitch);
4738 Dest = (WORD *) (dst + y * outpitch);
4739 for (x = 0; x < width; x++ ) {
4740 WORD color = *Source++;
4741 *Dest = color;
4742 if (!color_in_range(&surface->src_blt_color_key, color))
4743 *Dest |= (1 << 15);
4744 else
4745 *Dest &= ~(1 << 15);
4746 Dest++;
4750 break;
4752 case CONVERT_CK_RGB24:
4754 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4755 unsigned int x, y;
4756 for (y = 0; y < height; y++)
4758 source = src + pitch * y;
4759 dest = dst + outpitch * y;
4760 for (x = 0; x < width; x++) {
4761 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4762 DWORD dstcolor = color << 8;
4763 if (!color_in_range(&surface->src_blt_color_key, color))
4764 dstcolor |= 0xff;
4765 *(DWORD*)dest = dstcolor;
4766 source += 3;
4767 dest += 4;
4771 break;
4773 case CONVERT_RGB32_888:
4775 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4776 unsigned int x, y;
4777 for (y = 0; y < height; y++)
4779 source = src + pitch * y;
4780 dest = dst + outpitch * y;
4781 for (x = 0; x < width; x++) {
4782 DWORD color = 0xffffff & *(const DWORD*)source;
4783 DWORD dstcolor = color << 8;
4784 if (!color_in_range(&surface->src_blt_color_key, color))
4785 dstcolor |= 0xff;
4786 *(DWORD*)dest = dstcolor;
4787 source += 4;
4788 dest += 4;
4792 break;
4794 default:
4795 ERR("Unsupported conversion type %#x.\n", convert);
4797 return WINED3D_OK;
4800 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4802 /* Flip the surface contents */
4803 /* Flip the DC */
4805 HDC tmp;
4806 tmp = front->hDC;
4807 front->hDC = back->hDC;
4808 back->hDC = tmp;
4811 /* Flip the DIBsection */
4813 HBITMAP tmp = front->dib.DIBsection;
4814 front->dib.DIBsection = back->dib.DIBsection;
4815 back->dib.DIBsection = tmp;
4818 /* Flip the surface data */
4820 void* tmp;
4822 tmp = front->dib.bitmap_data;
4823 front->dib.bitmap_data = back->dib.bitmap_data;
4824 back->dib.bitmap_data = tmp;
4826 tmp = front->resource.allocatedMemory;
4827 front->resource.allocatedMemory = back->resource.allocatedMemory;
4828 back->resource.allocatedMemory = tmp;
4830 tmp = front->resource.heapMemory;
4831 front->resource.heapMemory = back->resource.heapMemory;
4832 back->resource.heapMemory = tmp;
4835 /* Flip the PBO */
4837 GLuint tmp_pbo = front->pbo;
4838 front->pbo = back->pbo;
4839 back->pbo = tmp_pbo;
4842 /* Flip the opengl texture */
4844 GLuint tmp;
4846 tmp = back->texture_name;
4847 back->texture_name = front->texture_name;
4848 front->texture_name = tmp;
4850 tmp = back->texture_name_srgb;
4851 back->texture_name_srgb = front->texture_name_srgb;
4852 front->texture_name_srgb = tmp;
4854 tmp = back->rb_multisample;
4855 back->rb_multisample = front->rb_multisample;
4856 front->rb_multisample = tmp;
4858 tmp = back->rb_resolved;
4859 back->rb_resolved = front->rb_resolved;
4860 front->rb_resolved = tmp;
4862 resource_unload(&back->resource);
4863 resource_unload(&front->resource);
4867 DWORD tmp_flags = back->flags;
4868 back->flags = front->flags;
4869 front->flags = tmp_flags;
4873 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4874 * pixel copy calls. */
4875 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4876 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4878 struct wined3d_device *device = dst_surface->resource.device;
4879 float xrel, yrel;
4880 UINT row;
4881 struct wined3d_context *context;
4882 BOOL upsidedown = FALSE;
4883 RECT dst_rect = *dst_rect_in;
4885 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4886 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4888 if(dst_rect.top > dst_rect.bottom) {
4889 UINT tmp = dst_rect.bottom;
4890 dst_rect.bottom = dst_rect.top;
4891 dst_rect.top = tmp;
4892 upsidedown = TRUE;
4895 context = context_acquire(device, src_surface);
4896 context_apply_blit_state(context, device);
4897 surface_internal_preload(dst_surface, SRGB_RGB);
4898 ENTER_GL();
4900 /* Bind the target texture */
4901 context_bind_texture(context, dst_surface->texture_target, dst_surface->texture_name);
4902 if (surface_is_offscreen(src_surface))
4904 TRACE("Reading from an offscreen target\n");
4905 upsidedown = !upsidedown;
4906 glReadBuffer(device->offscreenBuffer);
4908 else
4910 glReadBuffer(surface_get_gl_buffer(src_surface));
4912 checkGLcall("glReadBuffer");
4914 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4915 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4917 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4919 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4921 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4922 ERR("Texture filtering not supported in direct blit.\n");
4924 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4925 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4927 ERR("Texture filtering not supported in direct blit\n");
4930 if (upsidedown
4931 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4932 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4934 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4936 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4937 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4938 src_rect->left, src_surface->resource.height - src_rect->bottom,
4939 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4941 else
4943 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4944 /* I have to process this row by row to swap the image,
4945 * otherwise it would be upside down, so stretching in y direction
4946 * doesn't cost extra time
4948 * However, stretching in x direction can be avoided if not necessary
4950 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4951 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4953 /* Well, that stuff works, but it's very slow.
4954 * find a better way instead
4956 UINT col;
4958 for (col = dst_rect.left; col < dst_rect.right; ++col)
4960 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4961 dst_rect.left + col /* x offset */, row /* y offset */,
4962 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4965 else
4967 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4968 dst_rect.left /* x offset */, row /* y offset */,
4969 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4973 checkGLcall("glCopyTexSubImage2D");
4975 LEAVE_GL();
4976 context_release(context);
4978 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4979 * path is never entered
4981 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4984 /* Uses the hardware to stretch and flip the image */
4985 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4986 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4988 struct wined3d_device *device = dst_surface->resource.device;
4989 struct wined3d_swapchain *src_swapchain = NULL;
4990 GLuint src, backup = 0;
4991 float left, right, top, bottom; /* Texture coordinates */
4992 UINT fbwidth = src_surface->resource.width;
4993 UINT fbheight = src_surface->resource.height;
4994 struct wined3d_context *context;
4995 GLenum drawBuffer = GL_BACK;
4996 GLenum texture_target;
4997 BOOL noBackBufferBackup;
4998 BOOL src_offscreen;
4999 BOOL upsidedown = FALSE;
5000 RECT dst_rect = *dst_rect_in;
5002 TRACE("Using hwstretch blit\n");
5003 /* Activate the Proper context for reading from the source surface, set it up for blitting */
5004 context = context_acquire(device, src_surface);
5005 context_apply_blit_state(context, device);
5006 surface_internal_preload(dst_surface, SRGB_RGB);
5008 src_offscreen = surface_is_offscreen(src_surface);
5009 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
5010 if (!noBackBufferBackup && !src_surface->texture_name)
5012 /* Get it a description */
5013 surface_internal_preload(src_surface, SRGB_RGB);
5015 ENTER_GL();
5017 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
5018 * This way we don't have to wait for the 2nd readback to finish to leave this function.
5020 if (context->aux_buffers >= 2)
5022 /* Got more than one aux buffer? Use the 2nd aux buffer */
5023 drawBuffer = GL_AUX1;
5025 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
5027 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
5028 drawBuffer = GL_AUX0;
5031 if(noBackBufferBackup) {
5032 glGenTextures(1, &backup);
5033 checkGLcall("glGenTextures");
5034 context_bind_texture(context, GL_TEXTURE_2D, backup);
5035 texture_target = GL_TEXTURE_2D;
5036 } else {
5037 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
5038 * we are reading from the back buffer, the backup can be used as source texture
5040 texture_target = src_surface->texture_target;
5041 context_bind_texture(context, texture_target, src_surface->texture_name);
5042 glEnable(texture_target);
5043 checkGLcall("glEnable(texture_target)");
5045 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
5046 src_surface->flags &= ~SFLAG_INTEXTURE;
5049 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
5050 * glCopyTexSubImage is a bit picky about the parameters we pass to it
5052 if(dst_rect.top > dst_rect.bottom) {
5053 UINT tmp = dst_rect.bottom;
5054 dst_rect.bottom = dst_rect.top;
5055 dst_rect.top = tmp;
5056 upsidedown = TRUE;
5059 if (src_offscreen)
5061 TRACE("Reading from an offscreen target\n");
5062 upsidedown = !upsidedown;
5063 glReadBuffer(device->offscreenBuffer);
5065 else
5067 glReadBuffer(surface_get_gl_buffer(src_surface));
5070 /* TODO: Only back up the part that will be overwritten */
5071 glCopyTexSubImage2D(texture_target, 0,
5072 0, 0 /* read offsets */,
5073 0, 0,
5074 fbwidth,
5075 fbheight);
5077 checkGLcall("glCopyTexSubImage2D");
5079 /* No issue with overriding these - the sampler is dirty due to blit usage */
5080 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
5081 wined3d_gl_mag_filter(magLookup, filter));
5082 checkGLcall("glTexParameteri");
5083 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5084 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
5085 checkGLcall("glTexParameteri");
5087 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5088 src_swapchain = src_surface->container.u.swapchain;
5089 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5091 src = backup ? backup : src_surface->texture_name;
5093 else
5095 glReadBuffer(GL_FRONT);
5096 checkGLcall("glReadBuffer(GL_FRONT)");
5098 glGenTextures(1, &src);
5099 checkGLcall("glGenTextures(1, &src)");
5100 context_bind_texture(context, GL_TEXTURE_2D, src);
5102 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5103 * out for power of 2 sizes
5105 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5106 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5107 checkGLcall("glTexImage2D");
5108 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5109 0, 0 /* read offsets */,
5110 0, 0,
5111 fbwidth,
5112 fbheight);
5114 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5115 checkGLcall("glTexParameteri");
5116 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5117 checkGLcall("glTexParameteri");
5119 glReadBuffer(GL_BACK);
5120 checkGLcall("glReadBuffer(GL_BACK)");
5122 if(texture_target != GL_TEXTURE_2D) {
5123 glDisable(texture_target);
5124 glEnable(GL_TEXTURE_2D);
5125 texture_target = GL_TEXTURE_2D;
5128 checkGLcall("glEnd and previous");
5130 left = src_rect->left;
5131 right = src_rect->right;
5133 if (!upsidedown)
5135 top = src_surface->resource.height - src_rect->top;
5136 bottom = src_surface->resource.height - src_rect->bottom;
5138 else
5140 top = src_surface->resource.height - src_rect->bottom;
5141 bottom = src_surface->resource.height - src_rect->top;
5144 if (src_surface->flags & SFLAG_NORMCOORD)
5146 left /= src_surface->pow2Width;
5147 right /= src_surface->pow2Width;
5148 top /= src_surface->pow2Height;
5149 bottom /= src_surface->pow2Height;
5152 /* draw the source texture stretched and upside down. The correct surface is bound already */
5153 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5154 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5156 context_set_draw_buffer(context, drawBuffer);
5157 glReadBuffer(drawBuffer);
5159 glBegin(GL_QUADS);
5160 /* bottom left */
5161 glTexCoord2f(left, bottom);
5162 glVertex2i(0, 0);
5164 /* top left */
5165 glTexCoord2f(left, top);
5166 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5168 /* top right */
5169 glTexCoord2f(right, top);
5170 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5172 /* bottom right */
5173 glTexCoord2f(right, bottom);
5174 glVertex2i(dst_rect.right - dst_rect.left, 0);
5175 glEnd();
5176 checkGLcall("glEnd and previous");
5178 if (texture_target != dst_surface->texture_target)
5180 glDisable(texture_target);
5181 glEnable(dst_surface->texture_target);
5182 texture_target = dst_surface->texture_target;
5185 /* Now read the stretched and upside down image into the destination texture */
5186 context_bind_texture(context, texture_target, dst_surface->texture_name);
5187 glCopyTexSubImage2D(texture_target,
5189 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5190 0, 0, /* We blitted the image to the origin */
5191 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5192 checkGLcall("glCopyTexSubImage2D");
5194 if(drawBuffer == GL_BACK) {
5195 /* Write the back buffer backup back */
5196 if(backup) {
5197 if(texture_target != GL_TEXTURE_2D) {
5198 glDisable(texture_target);
5199 glEnable(GL_TEXTURE_2D);
5200 texture_target = GL_TEXTURE_2D;
5202 context_bind_texture(context, GL_TEXTURE_2D, backup);
5204 else
5206 if (texture_target != src_surface->texture_target)
5208 glDisable(texture_target);
5209 glEnable(src_surface->texture_target);
5210 texture_target = src_surface->texture_target;
5212 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5215 glBegin(GL_QUADS);
5216 /* top left */
5217 glTexCoord2f(0.0f, 0.0f);
5218 glVertex2i(0, fbheight);
5220 /* bottom left */
5221 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5222 glVertex2i(0, 0);
5224 /* bottom right */
5225 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5226 (float)fbheight / (float)src_surface->pow2Height);
5227 glVertex2i(fbwidth, 0);
5229 /* top right */
5230 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5231 glVertex2i(fbwidth, fbheight);
5232 glEnd();
5234 glDisable(texture_target);
5235 checkGLcall("glDisable(texture_target)");
5237 /* Cleanup */
5238 if (src != src_surface->texture_name && src != backup)
5240 glDeleteTextures(1, &src);
5241 checkGLcall("glDeleteTextures(1, &src)");
5243 if(backup) {
5244 glDeleteTextures(1, &backup);
5245 checkGLcall("glDeleteTextures(1, &backup)");
5248 LEAVE_GL();
5250 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5252 context_release(context);
5254 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5255 * path is never entered
5257 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5260 /* Front buffer coordinates are always full screen coordinates, but our GL
5261 * drawable is limited to the window's client area. The sysmem and texture
5262 * copies do have the full screen size. Note that GL has a bottom-left
5263 * origin, while D3D has a top-left origin. */
5264 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5266 UINT drawable_height;
5268 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5269 && surface == surface->container.u.swapchain->front_buffer)
5271 POINT offset = {0, 0};
5272 RECT windowsize;
5274 ScreenToClient(window, &offset);
5275 OffsetRect(rect, offset.x, offset.y);
5277 GetClientRect(window, &windowsize);
5278 drawable_height = windowsize.bottom - windowsize.top;
5280 else
5282 drawable_height = surface->resource.height;
5285 rect->top = drawable_height - rect->top;
5286 rect->bottom = drawable_height - rect->bottom;
5289 static void surface_blt_to_drawable(const struct wined3d_device *device,
5290 enum wined3d_texture_filter_type filter, BOOL color_key,
5291 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5292 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5294 struct wined3d_context *context;
5295 RECT src_rect, dst_rect;
5297 src_rect = *src_rect_in;
5298 dst_rect = *dst_rect_in;
5300 /* Make sure the surface is up-to-date. This should probably use
5301 * surface_load_location() and worry about the destination surface too,
5302 * unless we're overwriting it completely. */
5303 surface_internal_preload(src_surface, SRGB_RGB);
5305 /* Activate the destination context, set it up for blitting */
5306 context = context_acquire(device, dst_surface);
5307 context_apply_blit_state(context, device);
5309 if (!surface_is_offscreen(dst_surface))
5310 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5312 device->blitter->set_shader(device->blit_priv, context, src_surface);
5314 ENTER_GL();
5316 if (color_key)
5318 glEnable(GL_ALPHA_TEST);
5319 checkGLcall("glEnable(GL_ALPHA_TEST)");
5321 /* When the primary render target uses P8, the alpha component
5322 * contains the palette index. Which means that the colorkey is one of
5323 * the palette entries. In other cases pixels that should be masked
5324 * away have alpha set to 0. */
5325 if (primary_render_target_is_p8(device))
5326 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->src_blt_color_key.color_space_low_value / 256.0f);
5327 else
5328 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5329 checkGLcall("glAlphaFunc");
5331 else
5333 glDisable(GL_ALPHA_TEST);
5334 checkGLcall("glDisable(GL_ALPHA_TEST)");
5337 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5339 if (color_key)
5341 glDisable(GL_ALPHA_TEST);
5342 checkGLcall("glDisable(GL_ALPHA_TEST)");
5345 LEAVE_GL();
5347 /* Leave the opengl state valid for blitting */
5348 device->blitter->unset_shader(context->gl_info);
5350 if (wined3d_settings.strict_draw_ordering
5351 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5352 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5353 wglFlush(); /* Flush to ensure ordering across contexts. */
5355 context_release(context);
5358 /* Do not call while under the GL lock. */
5359 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
5361 struct wined3d_device *device = s->resource.device;
5362 const struct blit_shader *blitter;
5364 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5365 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5366 if (!blitter)
5368 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5369 return WINED3DERR_INVALIDCALL;
5372 return blitter->color_fill(device, s, rect, color);
5375 /* Do not call while under the GL lock. */
5376 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5377 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5378 enum wined3d_texture_filter_type filter)
5380 struct wined3d_device *device = dst_surface->resource.device;
5381 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5382 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5384 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5385 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5386 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
5388 /* Get the swapchain. One of the surfaces has to be a primary surface */
5389 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
5391 WARN("Destination is in sysmem, rejecting gl blt\n");
5392 return WINED3DERR_INVALIDCALL;
5395 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5396 dstSwapchain = dst_surface->container.u.swapchain;
5398 if (src_surface)
5400 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
5402 WARN("Src is in sysmem, rejecting gl blt\n");
5403 return WINED3DERR_INVALIDCALL;
5406 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5407 srcSwapchain = src_surface->container.u.swapchain;
5410 /* Early sort out of cases where no render target is used */
5411 if (!dstSwapchain && !srcSwapchain
5412 && src_surface != device->fb.render_targets[0]
5413 && dst_surface != device->fb.render_targets[0])
5415 TRACE("No surface is render target, not using hardware blit.\n");
5416 return WINED3DERR_INVALIDCALL;
5419 /* No destination color keying supported */
5420 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5422 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5423 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5424 return WINED3DERR_INVALIDCALL;
5427 if (dstSwapchain && dstSwapchain == srcSwapchain)
5429 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5430 return WINED3DERR_INVALIDCALL;
5433 if (dstSwapchain && srcSwapchain)
5435 FIXME("Implement hardware blit between two different swapchains\n");
5436 return WINED3DERR_INVALIDCALL;
5439 if (dstSwapchain)
5441 /* Handled with regular texture -> swapchain blit */
5442 if (src_surface == device->fb.render_targets[0])
5443 TRACE("Blit from active render target to a swapchain\n");
5445 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5447 FIXME("Implement blit from a swapchain to the active render target\n");
5448 return WINED3DERR_INVALIDCALL;
5451 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5453 /* Blit from render target to texture */
5454 BOOL stretchx;
5456 /* P8 read back is not implemented */
5457 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5458 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5460 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5461 return WINED3DERR_INVALIDCALL;
5464 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5466 TRACE("Color keying not supported by frame buffer to texture blit\n");
5467 return WINED3DERR_INVALIDCALL;
5468 /* Destination color key is checked above */
5471 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5472 stretchx = TRUE;
5473 else
5474 stretchx = FALSE;
5476 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5477 * flip the image nor scale it.
5479 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5480 * -> If the app wants a image width an unscaled width, copy it line per line
5481 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5482 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5483 * back buffer. This is slower than reading line per line, thus not used for flipping
5484 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5485 * pixel by pixel. */
5486 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5487 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5489 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
5490 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
5492 else
5494 TRACE("Using hardware stretching to flip / stretch the texture.\n");
5495 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
5498 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5500 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5501 dst_surface->resource.allocatedMemory = NULL;
5502 dst_surface->resource.heapMemory = NULL;
5504 else
5506 dst_surface->flags &= ~SFLAG_INSYSMEM;
5509 return WINED3D_OK;
5511 else if (src_surface)
5513 /* Blit from offscreen surface to render target */
5514 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
5515 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5517 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5519 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5520 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5521 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5523 FIXME("Unsupported blit operation falling back to software\n");
5524 return WINED3DERR_INVALIDCALL;
5527 /* Color keying: Check if we have to do a color keyed blt,
5528 * and if not check if a color key is activated.
5530 * Just modify the color keying parameters in the surface and restore them afterwards
5531 * The surface keeps track of the color key last used to load the opengl surface.
5532 * PreLoad will catch the change to the flags and color key and reload if necessary.
5534 if (flags & WINEDDBLT_KEYSRC)
5536 /* Use color key from surface */
5538 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5540 /* Use color key from DDBltFx */
5541 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5542 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
5544 else
5546 /* Do not use color key */
5547 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5550 surface_blt_to_drawable(device, filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5551 src_surface, src_rect, dst_surface, dst_rect);
5553 /* Restore the color key parameters */
5554 src_surface->CKeyFlags = oldCKeyFlags;
5555 src_surface->src_blt_color_key = old_blt_key;
5557 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5559 return WINED3D_OK;
5562 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5563 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5564 return WINED3DERR_INVALIDCALL;
5567 /* GL locking is done by the caller */
5568 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5569 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5571 struct wined3d_device *device = surface->resource.device;
5572 const struct wined3d_gl_info *gl_info = context->gl_info;
5573 GLint compare_mode = GL_NONE;
5574 struct blt_info info;
5575 GLint old_binding = 0;
5576 RECT rect;
5578 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5580 glDisable(GL_CULL_FACE);
5581 glDisable(GL_BLEND);
5582 glDisable(GL_ALPHA_TEST);
5583 glDisable(GL_SCISSOR_TEST);
5584 glDisable(GL_STENCIL_TEST);
5585 glEnable(GL_DEPTH_TEST);
5586 glDepthFunc(GL_ALWAYS);
5587 glDepthMask(GL_TRUE);
5588 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5589 glViewport(x, y, w, h);
5591 SetRect(&rect, 0, h, w, 0);
5592 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5593 context_active_texture(context, context->gl_info, 0);
5594 glGetIntegerv(info.binding, &old_binding);
5595 glBindTexture(info.bind_target, texture);
5596 if (gl_info->supported[ARB_SHADOW])
5598 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5599 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5602 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5603 gl_info, info.tex_type, &surface->ds_current_size);
5605 glBegin(GL_TRIANGLE_STRIP);
5606 glTexCoord3fv(info.coords[0]);
5607 glVertex2f(-1.0f, -1.0f);
5608 glTexCoord3fv(info.coords[1]);
5609 glVertex2f(1.0f, -1.0f);
5610 glTexCoord3fv(info.coords[2]);
5611 glVertex2f(-1.0f, 1.0f);
5612 glTexCoord3fv(info.coords[3]);
5613 glVertex2f(1.0f, 1.0f);
5614 glEnd();
5616 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5617 glBindTexture(info.bind_target, old_binding);
5619 glPopAttrib();
5621 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5624 void surface_modify_ds_location(struct wined3d_surface *surface,
5625 DWORD location, UINT w, UINT h)
5627 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5629 if (location & ~(SFLAG_LOCATIONS | SFLAG_LOST))
5630 FIXME("Invalid location (%#x) specified.\n", location);
5632 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5633 || (!(surface->flags & SFLAG_INTEXTURE) && (location & SFLAG_INTEXTURE)))
5635 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5637 TRACE("Passing to container.\n");
5638 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5642 surface->ds_current_size.cx = w;
5643 surface->ds_current_size.cy = h;
5644 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_LOST);
5645 surface->flags |= location;
5648 /* Context activation is done by the caller. */
5649 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5651 struct wined3d_device *device = surface->resource.device;
5652 GLsizei w, h;
5654 TRACE("surface %p, new location %#x.\n", surface, location);
5656 /* TODO: Make this work for modes other than FBO */
5657 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5659 if (!(surface->flags & location))
5661 w = surface->ds_current_size.cx;
5662 h = surface->ds_current_size.cy;
5663 surface->ds_current_size.cx = 0;
5664 surface->ds_current_size.cy = 0;
5666 else
5668 w = surface->resource.width;
5669 h = surface->resource.height;
5672 if (surface->ds_current_size.cx == surface->resource.width
5673 && surface->ds_current_size.cy == surface->resource.height)
5675 TRACE("Location (%#x) is already up to date.\n", location);
5676 return;
5679 if (surface->current_renderbuffer)
5681 FIXME("Not supported with fixed up depth stencil.\n");
5682 return;
5685 if (surface->flags & SFLAG_LOST)
5687 TRACE("Surface was discarded, no need copy data.\n");
5688 switch (location)
5690 case SFLAG_INTEXTURE:
5691 surface_prepare_texture(surface, context, FALSE);
5692 break;
5693 case SFLAG_INRB_MULTISAMPLE:
5694 surface_prepare_rb(surface, context->gl_info, TRUE);
5695 break;
5696 case SFLAG_INDRAWABLE:
5697 /* Nothing to do */
5698 break;
5699 default:
5700 FIXME("Unhandled location %#x\n", location);
5702 surface->flags &= ~SFLAG_LOST;
5703 surface->flags |= location;
5704 surface->ds_current_size.cx = surface->resource.width;
5705 surface->ds_current_size.cy = surface->resource.height;
5706 return;
5709 if (!(surface->flags & SFLAG_LOCATIONS))
5711 FIXME("No up to date depth stencil location.\n");
5712 surface->flags |= location;
5713 surface->ds_current_size.cx = surface->resource.width;
5714 surface->ds_current_size.cy = surface->resource.height;
5715 return;
5718 if (location == SFLAG_INTEXTURE)
5720 GLint old_binding = 0;
5721 GLenum bind_target;
5723 /* The render target is allowed to be smaller than the depth/stencil
5724 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5725 * than the offscreen surface. Don't overwrite the offscreen surface
5726 * with undefined data. */
5727 w = min(w, context->swapchain->desc.backbuffer_width);
5728 h = min(h, context->swapchain->desc.backbuffer_height);
5730 TRACE("Copying onscreen depth buffer to depth texture.\n");
5732 ENTER_GL();
5734 if (!device->depth_blt_texture)
5736 glGenTextures(1, &device->depth_blt_texture);
5739 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5740 * directly on the FBO texture. That's because we need to flip. */
5741 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5742 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5743 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5745 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5746 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5748 else
5750 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5751 bind_target = GL_TEXTURE_2D;
5753 glBindTexture(bind_target, device->depth_blt_texture);
5754 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5755 * internal format, because the internal format might include stencil
5756 * data. In principle we should copy stencil data as well, but unless
5757 * the driver supports stencil export it's hard to do, and doesn't
5758 * seem to be needed in practice. If the hardware doesn't support
5759 * writing stencil data, the glCopyTexImage2D() call might trigger
5760 * software fallbacks. */
5761 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5762 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5763 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5764 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5765 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5766 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5767 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5768 glBindTexture(bind_target, old_binding);
5770 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5771 NULL, surface, SFLAG_INTEXTURE);
5772 context_set_draw_buffer(context, GL_NONE);
5773 glReadBuffer(GL_NONE);
5775 /* Do the actual blit */
5776 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5777 checkGLcall("depth_blt");
5779 context_invalidate_state(context, STATE_FRAMEBUFFER);
5781 LEAVE_GL();
5783 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5785 else if (location == SFLAG_INDRAWABLE)
5787 TRACE("Copying depth texture to onscreen depth buffer.\n");
5789 ENTER_GL();
5791 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5792 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5793 surface_depth_blt(surface, context, surface->texture_name,
5794 0, surface->pow2Height - h, w, h, surface->texture_target);
5795 checkGLcall("depth_blt");
5797 context_invalidate_state(context, STATE_FRAMEBUFFER);
5799 LEAVE_GL();
5801 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5803 else
5805 ERR("Invalid location (%#x) specified.\n", location);
5808 surface->flags |= location;
5809 surface->ds_current_size.cx = surface->resource.width;
5810 surface->ds_current_size.cy = surface->resource.height;
5813 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5815 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5816 struct wined3d_surface *overlay;
5818 TRACE("surface %p, location %s, persistent %#x.\n",
5819 surface, debug_surflocation(location), persistent);
5821 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5822 && !(surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5823 && (location & SFLAG_INDRAWABLE))
5824 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5826 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5827 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5828 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5830 if (persistent)
5832 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5833 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5835 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5837 TRACE("Passing to container.\n");
5838 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5841 surface->flags &= ~SFLAG_LOCATIONS;
5842 surface->flags |= location;
5844 /* Redraw emulated overlays, if any */
5845 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5847 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5849 surface_draw_overlay(overlay);
5853 else
5855 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5857 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5859 TRACE("Passing to container\n");
5860 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5863 surface->flags &= ~location;
5866 if (!(surface->flags & SFLAG_LOCATIONS))
5868 ERR("Surface %p does not have any up to date location.\n", surface);
5872 static DWORD resource_access_from_location(DWORD location)
5874 switch (location)
5876 case SFLAG_INSYSMEM:
5877 return WINED3D_RESOURCE_ACCESS_CPU;
5879 case SFLAG_INDRAWABLE:
5880 case SFLAG_INSRGBTEX:
5881 case SFLAG_INTEXTURE:
5882 case SFLAG_INRB_MULTISAMPLE:
5883 case SFLAG_INRB_RESOLVED:
5884 return WINED3D_RESOURCE_ACCESS_GPU;
5886 default:
5887 FIXME("Unhandled location %#x.\n", location);
5888 return 0;
5892 static void surface_load_sysmem(struct wined3d_surface *surface,
5893 const struct wined3d_gl_info *gl_info, const RECT *rect)
5895 surface_prepare_system_memory(surface);
5897 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5898 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5900 /* Download the surface to system memory. */
5901 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5903 struct wined3d_device *device = surface->resource.device;
5904 struct wined3d_context *context;
5906 /* TODO: Use already acquired context when possible. */
5907 context = context_acquire(device, NULL);
5909 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5910 surface_download_data(surface, gl_info);
5912 context_release(context);
5914 return;
5917 if (surface->flags & SFLAG_INDRAWABLE)
5919 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5920 wined3d_surface_get_pitch(surface));
5921 return;
5924 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5925 surface, surface->flags & SFLAG_LOCATIONS);
5928 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5929 const struct wined3d_gl_info *gl_info, const RECT *rect)
5931 struct wined3d_device *device = surface->resource.device;
5932 struct wined3d_format format;
5933 CONVERT_TYPES convert;
5934 UINT byte_count;
5935 BYTE *mem;
5937 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5939 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5940 return WINED3DERR_INVALIDCALL;
5943 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5944 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5946 if (surface->flags & SFLAG_INTEXTURE)
5948 RECT r;
5950 surface_get_rect(surface, rect, &r);
5951 surface_blt_to_drawable(device, WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
5953 return WINED3D_OK;
5956 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5958 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5959 * path through sysmem. */
5960 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5963 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5965 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5966 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5967 * called. */
5968 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5970 struct wined3d_context *context;
5972 TRACE("Removing the pbo attached to surface %p.\n", surface);
5974 /* TODO: Use already acquired context when possible. */
5975 context = context_acquire(device, NULL);
5977 surface_remove_pbo(surface, gl_info);
5979 context_release(context);
5982 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5984 UINT height = surface->resource.height;
5985 UINT width = surface->resource.width;
5986 UINT src_pitch, dst_pitch;
5988 byte_count = format.conv_byte_count;
5989 src_pitch = wined3d_surface_get_pitch(surface);
5991 /* Stick to the alignment for the converted surface too, makes it
5992 * easier to load the surface. */
5993 dst_pitch = width * byte_count;
5994 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5996 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5998 ERR("Out of memory (%u).\n", dst_pitch * height);
5999 return E_OUTOFMEMORY;
6002 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
6003 src_pitch, width, height, dst_pitch, convert, surface);
6005 surface->flags |= SFLAG_CONVERTED;
6007 else
6009 surface->flags &= ~SFLAG_CONVERTED;
6010 mem = surface->resource.allocatedMemory;
6011 byte_count = format.byte_count;
6014 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
6016 /* Don't delete PBO memory. */
6017 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6018 HeapFree(GetProcessHeap(), 0, mem);
6020 return WINED3D_OK;
6023 static HRESULT surface_load_texture(struct wined3d_surface *surface,
6024 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
6026 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
6027 struct wined3d_device *device = surface->resource.device;
6028 struct wined3d_context *context;
6029 UINT width, src_pitch, dst_pitch;
6030 struct wined3d_bo_address data;
6031 struct wined3d_format format;
6032 POINT dst_point = {0, 0};
6033 CONVERT_TYPES convert;
6034 BYTE *mem;
6036 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
6037 && surface_is_offscreen(surface)
6038 && (surface->flags & SFLAG_INDRAWABLE))
6040 surface_load_fb_texture(surface, srgb);
6042 return WINED3D_OK;
6045 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6046 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
6047 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6048 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6049 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6051 if (srgb)
6052 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INTEXTURE,
6053 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6054 else
6055 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INSRGBTEX,
6056 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6058 return WINED3D_OK;
6061 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
6062 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
6063 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6064 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6065 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6067 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
6068 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
6069 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6071 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
6072 &rect, surface, dst_location, &rect);
6074 return WINED3D_OK;
6077 /* Upload from system memory */
6079 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6080 TRUE /* We will use textures */, &format, &convert);
6082 if (srgb)
6084 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6086 /* Performance warning... */
6087 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6088 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6091 else
6093 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6095 /* Performance warning... */
6096 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6097 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6101 if (!(surface->flags & SFLAG_INSYSMEM))
6103 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6104 /* Lets hope we get it from somewhere... */
6105 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6108 /* TODO: Use already acquired context when possible. */
6109 context = context_acquire(device, NULL);
6111 surface_prepare_texture(surface, context, srgb);
6112 surface_bind_and_dirtify(surface, context, srgb);
6114 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6116 surface->flags |= SFLAG_GLCKEY;
6117 surface->gl_color_key = surface->src_blt_color_key;
6119 else surface->flags &= ~SFLAG_GLCKEY;
6121 width = surface->resource.width;
6122 src_pitch = wined3d_surface_get_pitch(surface);
6124 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6125 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6126 * called. */
6127 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6129 TRACE("Removing the pbo attached to surface %p.\n", surface);
6130 surface_remove_pbo(surface, gl_info);
6133 if (format.convert)
6135 /* This code is entered for texture formats which need a fixup. */
6136 UINT height = surface->resource.height;
6138 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6139 dst_pitch = width * format.conv_byte_count;
6140 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6142 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6144 ERR("Out of memory (%u).\n", dst_pitch * height);
6145 context_release(context);
6146 return E_OUTOFMEMORY;
6148 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6149 format.byte_count = format.conv_byte_count;
6150 src_pitch = dst_pitch;
6152 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6154 /* This code is only entered for color keying fixups */
6155 UINT height = surface->resource.height;
6157 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6158 dst_pitch = width * format.conv_byte_count;
6159 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6161 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6163 ERR("Out of memory (%u).\n", dst_pitch * height);
6164 context_release(context);
6165 return E_OUTOFMEMORY;
6167 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6168 width, height, dst_pitch, convert, surface);
6169 format.byte_count = format.conv_byte_count;
6170 src_pitch = dst_pitch;
6172 else
6174 mem = surface->resource.allocatedMemory;
6177 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6178 data.addr = mem;
6179 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
6181 context_release(context);
6183 /* Don't delete PBO memory. */
6184 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6185 HeapFree(GetProcessHeap(), 0, mem);
6187 return WINED3D_OK;
6190 static void surface_multisample_resolve(struct wined3d_surface *surface)
6192 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6194 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6195 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6197 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
6198 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6201 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6203 struct wined3d_device *device = surface->resource.device;
6204 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6205 HRESULT hr;
6207 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6209 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6211 if (location == SFLAG_INTEXTURE)
6213 struct wined3d_context *context = context_acquire(device, NULL);
6214 surface_load_ds_location(surface, context, location);
6215 context_release(context);
6216 return WINED3D_OK;
6218 else
6220 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6221 return WINED3DERR_INVALIDCALL;
6225 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6226 location = SFLAG_INTEXTURE;
6228 if (surface->flags & location)
6230 TRACE("Location already up to date.\n");
6232 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
6233 && surface_need_pbo(surface, gl_info))
6234 surface_load_pbo(surface, gl_info);
6236 return WINED3D_OK;
6239 if (WARN_ON(d3d_surface))
6241 DWORD required_access = resource_access_from_location(location);
6242 if ((surface->resource.access_flags & required_access) != required_access)
6243 WARN("Operation requires %#x access, but surface only has %#x.\n",
6244 required_access, surface->resource.access_flags);
6247 if (!(surface->flags & SFLAG_LOCATIONS))
6249 ERR("Surface %p does not have any up to date location.\n", surface);
6250 surface->flags |= SFLAG_LOST;
6251 return WINED3DERR_DEVICELOST;
6254 switch (location)
6256 case SFLAG_INSYSMEM:
6257 surface_load_sysmem(surface, gl_info, rect);
6258 break;
6260 case SFLAG_INDRAWABLE:
6261 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6262 return hr;
6263 break;
6265 case SFLAG_INRB_RESOLVED:
6266 surface_multisample_resolve(surface);
6267 break;
6269 case SFLAG_INTEXTURE:
6270 case SFLAG_INSRGBTEX:
6271 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6272 return hr;
6273 break;
6275 default:
6276 ERR("Don't know how to handle location %#x.\n", location);
6277 break;
6280 if (!rect)
6282 surface->flags |= location;
6284 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6285 surface_evict_sysmem(surface);
6288 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6289 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6291 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6294 return WINED3D_OK;
6297 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6299 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6301 /* Not on a swapchain - must be offscreen */
6302 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6304 /* The front buffer is always onscreen */
6305 if (surface == swapchain->front_buffer) return FALSE;
6307 /* If the swapchain is rendered to an FBO, the backbuffer is
6308 * offscreen, otherwise onscreen */
6309 return swapchain->render_to_fbo;
6312 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6313 /* Context activation is done by the caller. */
6314 static void ffp_blit_free(struct wined3d_device *device) { }
6316 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6317 /* Context activation is done by the caller. */
6318 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6320 BYTE table[256][4];
6321 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6323 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6325 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6326 ENTER_GL();
6327 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6328 LEAVE_GL();
6331 /* Context activation is done by the caller. */
6332 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6334 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6336 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6337 * else the surface is converted in software at upload time in LoadLocation.
6339 if (!(surface->flags & SFLAG_CONVERTED) && fixup == COMPLEX_FIXUP_P8
6340 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6341 ffp_blit_p8_upload_palette(surface, context->gl_info);
6343 ENTER_GL();
6344 glEnable(surface->texture_target);
6345 checkGLcall("glEnable(surface->texture_target)");
6346 LEAVE_GL();
6347 return WINED3D_OK;
6350 /* Context activation is done by the caller. */
6351 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6353 ENTER_GL();
6354 glDisable(GL_TEXTURE_2D);
6355 checkGLcall("glDisable(GL_TEXTURE_2D)");
6356 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6358 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6359 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6361 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6363 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6364 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6366 LEAVE_GL();
6369 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6370 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
6371 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
6373 enum complex_fixup src_fixup;
6375 switch (blit_op)
6377 case WINED3D_BLIT_OP_COLOR_BLIT:
6378 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
6379 return FALSE;
6381 src_fixup = get_complex_fixup(src_format->color_fixup);
6382 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6384 TRACE("Checking support for fixup:\n");
6385 dump_color_fixup_desc(src_format->color_fixup);
6388 if (!is_identity_fixup(dst_format->color_fixup))
6390 TRACE("Destination fixups are not supported\n");
6391 return FALSE;
6394 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6396 TRACE("P8 fixup supported\n");
6397 return TRUE;
6400 /* We only support identity conversions. */
6401 if (is_identity_fixup(src_format->color_fixup))
6403 TRACE("[OK]\n");
6404 return TRUE;
6407 TRACE("[FAILED]\n");
6408 return FALSE;
6410 case WINED3D_BLIT_OP_COLOR_FILL:
6411 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
6412 return FALSE;
6414 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6416 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6417 return FALSE;
6419 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6421 TRACE("Color fill not supported\n");
6422 return FALSE;
6425 /* FIXME: We should reject color fills on formats with fixups,
6426 * but this would break P8 color fills for example. */
6428 return TRUE;
6430 case WINED3D_BLIT_OP_DEPTH_FILL:
6431 return TRUE;
6433 default:
6434 TRACE("Unsupported blit_op=%d\n", blit_op);
6435 return FALSE;
6439 /* Do not call while under the GL lock. */
6440 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6441 const RECT *dst_rect, const struct wined3d_color *color)
6443 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6444 struct wined3d_fb_state fb = {&dst_surface, NULL};
6446 return device_clear_render_targets(device, 1, &fb,
6447 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6450 /* Do not call while under the GL lock. */
6451 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6452 struct wined3d_surface *surface, const RECT *rect, float depth)
6454 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6455 struct wined3d_fb_state fb = {NULL, surface};
6457 return device_clear_render_targets(device, 0, &fb,
6458 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6461 const struct blit_shader ffp_blit = {
6462 ffp_blit_alloc,
6463 ffp_blit_free,
6464 ffp_blit_set,
6465 ffp_blit_unset,
6466 ffp_blit_supported,
6467 ffp_blit_color_fill,
6468 ffp_blit_depth_fill,
6471 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6473 return WINED3D_OK;
6476 /* Context activation is done by the caller. */
6477 static void cpu_blit_free(struct wined3d_device *device)
6481 /* Context activation is done by the caller. */
6482 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6484 return WINED3D_OK;
6487 /* Context activation is done by the caller. */
6488 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6492 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6493 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
6494 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
6496 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6498 return TRUE;
6501 return FALSE;
6504 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
6505 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
6506 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
6508 UINT row_block_count;
6509 const BYTE *src_row;
6510 BYTE *dst_row;
6511 UINT x, y;
6513 src_row = src_data;
6514 dst_row = dst_data;
6516 row_block_count = (update_w + format->block_width - 1) / format->block_width;
6518 if (!flags)
6520 for (y = 0; y < update_h; y += format->block_height)
6522 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
6523 src_row += src_pitch;
6524 dst_row += dst_pitch;
6527 return WINED3D_OK;
6530 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
6532 src_row += (((update_h / format->block_height) - 1) * src_pitch);
6534 switch (format->id)
6536 case WINED3DFMT_DXT1:
6537 for (y = 0; y < update_h; y += format->block_height)
6539 struct block
6541 WORD color[2];
6542 BYTE control_row[4];
6545 const struct block *s = (const struct block *)src_row;
6546 struct block *d = (struct block *)dst_row;
6548 for (x = 0; x < row_block_count; ++x)
6550 d[x].color[0] = s[x].color[0];
6551 d[x].color[1] = s[x].color[1];
6552 d[x].control_row[0] = s[x].control_row[3];
6553 d[x].control_row[1] = s[x].control_row[2];
6554 d[x].control_row[2] = s[x].control_row[1];
6555 d[x].control_row[3] = s[x].control_row[0];
6557 src_row -= src_pitch;
6558 dst_row += dst_pitch;
6560 return WINED3D_OK;
6562 case WINED3DFMT_DXT3:
6563 for (y = 0; y < update_h; y += format->block_height)
6565 struct block
6567 WORD alpha_row[4];
6568 WORD color[2];
6569 BYTE control_row[4];
6572 const struct block *s = (const struct block *)src_row;
6573 struct block *d = (struct block *)dst_row;
6575 for (x = 0; x < row_block_count; ++x)
6577 d[x].alpha_row[0] = s[x].alpha_row[3];
6578 d[x].alpha_row[1] = s[x].alpha_row[2];
6579 d[x].alpha_row[2] = s[x].alpha_row[1];
6580 d[x].alpha_row[3] = s[x].alpha_row[0];
6581 d[x].color[0] = s[x].color[0];
6582 d[x].color[1] = s[x].color[1];
6583 d[x].control_row[0] = s[x].control_row[3];
6584 d[x].control_row[1] = s[x].control_row[2];
6585 d[x].control_row[2] = s[x].control_row[1];
6586 d[x].control_row[3] = s[x].control_row[0];
6588 src_row -= src_pitch;
6589 dst_row += dst_pitch;
6591 return WINED3D_OK;
6593 default:
6594 FIXME("Compressed flip not implemented for format %s.\n",
6595 debug_d3dformat(format->id));
6596 return E_NOTIMPL;
6600 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
6601 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
6603 return E_NOTIMPL;
6606 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6607 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6608 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
6610 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6611 const struct wined3d_format *src_format, *dst_format;
6612 struct wined3d_surface *orig_src = src_surface;
6613 struct wined3d_mapped_rect dst_map, src_map;
6614 HRESULT hr = WINED3D_OK;
6615 const BYTE *sbuf;
6616 RECT xdst,xsrc;
6617 BYTE *dbuf;
6618 int x, y;
6620 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6621 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6622 flags, fx, debug_d3dtexturefiltertype(filter));
6624 xsrc = *src_rect;
6626 if (!src_surface)
6628 RECT full_rect;
6630 full_rect.left = 0;
6631 full_rect.top = 0;
6632 full_rect.right = dst_surface->resource.width;
6633 full_rect.bottom = dst_surface->resource.height;
6634 IntersectRect(&xdst, &full_rect, dst_rect);
6636 else
6638 BOOL clip_horiz, clip_vert;
6640 xdst = *dst_rect;
6641 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6642 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6644 if (clip_vert || clip_horiz)
6646 /* Now check if this is a special case or not... */
6647 if ((flags & WINEDDBLT_DDFX)
6648 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6649 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6651 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6652 return WINED3D_OK;
6655 if (clip_horiz)
6657 if (xdst.left < 0)
6659 xsrc.left -= xdst.left;
6660 xdst.left = 0;
6662 if (xdst.right > dst_surface->resource.width)
6664 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6665 xdst.right = (int)dst_surface->resource.width;
6669 if (clip_vert)
6671 if (xdst.top < 0)
6673 xsrc.top -= xdst.top;
6674 xdst.top = 0;
6676 if (xdst.bottom > dst_surface->resource.height)
6678 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6679 xdst.bottom = (int)dst_surface->resource.height;
6683 /* And check if after clipping something is still to be done... */
6684 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6685 || (xdst.left >= (int)dst_surface->resource.width)
6686 || (xdst.top >= (int)dst_surface->resource.height)
6687 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6688 || (xsrc.left >= (int)src_surface->resource.width)
6689 || (xsrc.top >= (int)src_surface->resource.height))
6691 TRACE("Nothing to be done after clipping.\n");
6692 return WINED3D_OK;
6697 if (src_surface == dst_surface)
6699 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6700 src_map = dst_map;
6701 src_format = dst_surface->resource.format;
6702 dst_format = src_format;
6704 else
6706 dst_format = dst_surface->resource.format;
6707 if (src_surface)
6709 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6711 src_surface = surface_convert_format(src_surface, dst_format->id);
6712 if (!src_surface)
6714 /* The conv function writes a FIXME */
6715 WARN("Cannot convert source surface format to dest format.\n");
6716 goto release;
6719 wined3d_surface_map(src_surface, &src_map, NULL, WINED3DLOCK_READONLY);
6720 src_format = src_surface->resource.format;
6722 else
6724 src_format = dst_format;
6726 if (dst_rect)
6727 wined3d_surface_map(dst_surface, &dst_map, &xdst, 0);
6728 else
6729 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6732 bpp = dst_surface->resource.format->byte_count;
6733 srcheight = xsrc.bottom - xsrc.top;
6734 srcwidth = xsrc.right - xsrc.left;
6735 dstheight = xdst.bottom - xdst.top;
6736 dstwidth = xdst.right - xdst.left;
6737 width = (xdst.right - xdst.left) * bpp;
6739 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
6741 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6743 if (src_surface == dst_surface)
6745 FIXME("Only plain blits supported on compressed surfaces.\n");
6746 hr = E_NOTIMPL;
6747 goto release;
6750 if (srcheight != dstheight || srcwidth != dstwidth)
6752 WARN("Stretching not supported on compressed surfaces.\n");
6753 hr = WINED3DERR_INVALIDCALL;
6754 goto release;
6757 if (srcwidth & (src_format->block_width - 1) || srcheight & (src_format->block_height - 1))
6759 WARN("Rectangle not block-aligned.\n");
6760 hr = WINED3DERR_INVALIDCALL;
6761 goto release;
6764 hr = surface_cpu_blt_compressed(src_map.data, dst_map.data,
6765 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
6766 src_format, flags, fx);
6767 goto release;
6770 if (dst_rect && src_surface != dst_surface)
6771 dbuf = dst_map.data;
6772 else
6773 dbuf = (BYTE *)dst_map.data + (xdst.top * dst_map.row_pitch) + (xdst.left * bpp);
6775 /* First, all the 'source-less' blits */
6776 if (flags & WINEDDBLT_COLORFILL)
6778 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
6779 flags &= ~WINEDDBLT_COLORFILL;
6782 if (flags & WINEDDBLT_DEPTHFILL)
6784 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6786 if (flags & WINEDDBLT_ROP)
6788 /* Catch some degenerate cases here. */
6789 switch (fx->dwROP)
6791 case BLACKNESS:
6792 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
6793 break;
6794 case 0xAA0029: /* No-op */
6795 break;
6796 case WHITENESS:
6797 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
6798 break;
6799 case SRCCOPY: /* Well, we do that below? */
6800 break;
6801 default:
6802 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6803 goto error;
6805 flags &= ~WINEDDBLT_ROP;
6807 if (flags & WINEDDBLT_DDROPS)
6809 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6811 /* Now the 'with source' blits. */
6812 if (src_surface)
6814 const BYTE *sbase;
6815 int sx, xinc, sy, yinc;
6817 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6818 goto release;
6820 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
6821 && (srcwidth != dstwidth || srcheight != dstheight))
6823 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6824 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6827 sbase = (BYTE *)src_map.data + (xsrc.top * src_map.row_pitch) + xsrc.left * bpp;
6828 xinc = (srcwidth << 16) / dstwidth;
6829 yinc = (srcheight << 16) / dstheight;
6831 if (!flags)
6833 /* No effects, we can cheat here. */
6834 if (dstwidth == srcwidth)
6836 if (dstheight == srcheight)
6838 /* No stretching in either direction. This needs to be as
6839 * fast as possible. */
6840 sbuf = sbase;
6842 /* Check for overlapping surfaces. */
6843 if (src_surface != dst_surface || xdst.top < xsrc.top
6844 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6846 /* No overlap, or dst above src, so copy from top downwards. */
6847 for (y = 0; y < dstheight; ++y)
6849 memcpy(dbuf, sbuf, width);
6850 sbuf += src_map.row_pitch;
6851 dbuf += dst_map.row_pitch;
6854 else if (xdst.top > xsrc.top)
6856 /* Copy from bottom upwards. */
6857 sbuf += src_map.row_pitch * dstheight;
6858 dbuf += dst_map.row_pitch * dstheight;
6859 for (y = 0; y < dstheight; ++y)
6861 sbuf -= src_map.row_pitch;
6862 dbuf -= dst_map.row_pitch;
6863 memcpy(dbuf, sbuf, width);
6866 else
6868 /* Src and dst overlapping on the same line, use memmove. */
6869 for (y = 0; y < dstheight; ++y)
6871 memmove(dbuf, sbuf, width);
6872 sbuf += src_map.row_pitch;
6873 dbuf += dst_map.row_pitch;
6877 else
6879 /* Stretching in y direction only. */
6880 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6882 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6883 memcpy(dbuf, sbuf, width);
6884 dbuf += dst_map.row_pitch;
6888 else
6890 /* Stretching in X direction. */
6891 int last_sy = -1;
6892 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6894 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6896 if ((sy >> 16) == (last_sy >> 16))
6898 /* This source row is the same as last source row -
6899 * Copy the already stretched row. */
6900 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
6902 else
6904 #define STRETCH_ROW(type) \
6905 do { \
6906 const type *s = (const type *)sbuf; \
6907 type *d = (type *)dbuf; \
6908 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6909 d[x] = s[sx >> 16]; \
6910 } while(0)
6912 switch(bpp)
6914 case 1:
6915 STRETCH_ROW(BYTE);
6916 break;
6917 case 2:
6918 STRETCH_ROW(WORD);
6919 break;
6920 case 4:
6921 STRETCH_ROW(DWORD);
6922 break;
6923 case 3:
6925 const BYTE *s;
6926 BYTE *d = dbuf;
6927 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6929 DWORD pixel;
6931 s = sbuf + 3 * (sx >> 16);
6932 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6933 d[0] = (pixel ) & 0xff;
6934 d[1] = (pixel >> 8) & 0xff;
6935 d[2] = (pixel >> 16) & 0xff;
6936 d += 3;
6938 break;
6940 default:
6941 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6942 hr = WINED3DERR_NOTAVAILABLE;
6943 goto error;
6945 #undef STRETCH_ROW
6947 dbuf += dst_map.row_pitch;
6948 last_sy = sy;
6952 else
6954 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
6955 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6956 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6957 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6959 /* The color keying flags are checked for correctness in ddraw */
6960 if (flags & WINEDDBLT_KEYSRC)
6962 keylow = src_surface->src_blt_color_key.color_space_low_value;
6963 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
6965 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6967 keylow = fx->ddckSrcColorkey.color_space_low_value;
6968 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
6971 if (flags & WINEDDBLT_KEYDEST)
6973 /* Destination color keys are taken from the source surface! */
6974 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
6975 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
6977 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6979 destkeylow = fx->ddckDestColorkey.color_space_low_value;
6980 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
6983 if (bpp == 1)
6985 keymask = 0xff;
6987 else
6989 keymask = src_format->red_mask
6990 | src_format->green_mask
6991 | src_format->blue_mask;
6993 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6996 if (flags & WINEDDBLT_DDFX)
6998 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6999 LONG tmpxy;
7000 dTopLeft = dbuf;
7001 dTopRight = dbuf + ((dstwidth - 1) * bpp);
7002 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
7003 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
7005 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
7007 /* I don't think we need to do anything about this flag */
7008 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
7010 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
7012 tmp = dTopRight;
7013 dTopRight = dTopLeft;
7014 dTopLeft = tmp;
7015 tmp = dBottomRight;
7016 dBottomRight = dBottomLeft;
7017 dBottomLeft = tmp;
7018 dstxinc = dstxinc * -1;
7020 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
7022 tmp = dTopLeft;
7023 dTopLeft = dBottomLeft;
7024 dBottomLeft = tmp;
7025 tmp = dTopRight;
7026 dTopRight = dBottomRight;
7027 dBottomRight = tmp;
7028 dstyinc = dstyinc * -1;
7030 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
7032 /* I don't think we need to do anything about this flag */
7033 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
7035 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
7037 tmp = dBottomRight;
7038 dBottomRight = dTopLeft;
7039 dTopLeft = tmp;
7040 tmp = dBottomLeft;
7041 dBottomLeft = dTopRight;
7042 dTopRight = tmp;
7043 dstxinc = dstxinc * -1;
7044 dstyinc = dstyinc * -1;
7046 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
7048 tmp = dTopLeft;
7049 dTopLeft = dBottomLeft;
7050 dBottomLeft = dBottomRight;
7051 dBottomRight = dTopRight;
7052 dTopRight = tmp;
7053 tmpxy = dstxinc;
7054 dstxinc = dstyinc;
7055 dstyinc = tmpxy;
7056 dstxinc = dstxinc * -1;
7058 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
7060 tmp = dTopLeft;
7061 dTopLeft = dTopRight;
7062 dTopRight = dBottomRight;
7063 dBottomRight = dBottomLeft;
7064 dBottomLeft = tmp;
7065 tmpxy = dstxinc;
7066 dstxinc = dstyinc;
7067 dstyinc = tmpxy;
7068 dstyinc = dstyinc * -1;
7070 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
7072 /* I don't think we need to do anything about this flag */
7073 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
7075 dbuf = dTopLeft;
7076 flags &= ~(WINEDDBLT_DDFX);
7079 #define COPY_COLORKEY_FX(type) \
7080 do { \
7081 const type *s; \
7082 type *d = (type *)dbuf, *dx, tmp; \
7083 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
7085 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
7086 dx = d; \
7087 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
7089 tmp = s[sx >> 16]; \
7090 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
7091 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
7093 dx[0] = tmp; \
7095 dx = (type *)(((BYTE *)dx) + dstxinc); \
7097 d = (type *)(((BYTE *)d) + dstyinc); \
7099 } while(0)
7101 switch (bpp)
7103 case 1:
7104 COPY_COLORKEY_FX(BYTE);
7105 break;
7106 case 2:
7107 COPY_COLORKEY_FX(WORD);
7108 break;
7109 case 4:
7110 COPY_COLORKEY_FX(DWORD);
7111 break;
7112 case 3:
7114 const BYTE *s;
7115 BYTE *d = dbuf, *dx;
7116 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
7118 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
7119 dx = d;
7120 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
7122 DWORD pixel, dpixel = 0;
7123 s = sbuf + 3 * (sx>>16);
7124 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
7125 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
7126 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
7127 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
7129 dx[0] = (pixel ) & 0xff;
7130 dx[1] = (pixel >> 8) & 0xff;
7131 dx[2] = (pixel >> 16) & 0xff;
7133 dx += dstxinc;
7135 d += dstyinc;
7137 break;
7139 default:
7140 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7141 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7142 hr = WINED3DERR_NOTAVAILABLE;
7143 goto error;
7144 #undef COPY_COLORKEY_FX
7149 error:
7150 if (flags && FIXME_ON(d3d_surface))
7152 FIXME("\tUnsupported flags: %#x.\n", flags);
7155 release:
7156 wined3d_surface_unmap(dst_surface);
7157 if (src_surface && src_surface != dst_surface)
7158 wined3d_surface_unmap(src_surface);
7159 /* Release the converted surface, if any. */
7160 if (src_surface && src_surface != orig_src)
7161 wined3d_surface_decref(src_surface);
7163 return hr;
7166 /* Do not call while under the GL lock. */
7167 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7168 const RECT *dst_rect, const struct wined3d_color *color)
7170 static const RECT src_rect;
7171 WINEDDBLTFX BltFx;
7173 memset(&BltFx, 0, sizeof(BltFx));
7174 BltFx.dwSize = sizeof(BltFx);
7175 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7176 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7177 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
7180 /* Do not call while under the GL lock. */
7181 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7182 struct wined3d_surface *surface, const RECT *rect, float depth)
7184 FIXME("Depth filling not implemented by cpu_blit.\n");
7185 return WINED3DERR_INVALIDCALL;
7188 const struct blit_shader cpu_blit = {
7189 cpu_blit_alloc,
7190 cpu_blit_free,
7191 cpu_blit_set,
7192 cpu_blit_unset,
7193 cpu_blit_supported,
7194 cpu_blit_color_fill,
7195 cpu_blit_depth_fill,
7198 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7199 UINT width, UINT height, UINT level, enum wined3d_multisample_type multisample_type,
7200 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7201 enum wined3d_pool pool, DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops)
7203 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7204 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7205 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
7206 unsigned int resource_size;
7207 HRESULT hr;
7209 if (multisample_quality > 0)
7211 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7212 multisample_quality = 0;
7215 /* Quick lockable sanity check.
7216 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7217 * this function is too deep to need to care about things like this.
7218 * Levels need to be checked too, since they all affect what can be done. */
7219 switch (pool)
7221 case WINED3D_POOL_SCRATCH:
7222 if (!lockable)
7224 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7225 "which are mutually exclusive, setting lockable to TRUE.\n");
7226 lockable = TRUE;
7228 break;
7230 case WINED3D_POOL_SYSTEM_MEM:
7231 if (!lockable)
7232 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7233 break;
7235 case WINED3D_POOL_MANAGED:
7236 if (usage & WINED3DUSAGE_DYNAMIC)
7237 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7238 break;
7240 case WINED3D_POOL_DEFAULT:
7241 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7242 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7243 break;
7245 default:
7246 FIXME("Unknown pool %#x.\n", pool);
7247 break;
7250 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3D_POOL_DEFAULT)
7251 FIXME("Trying to create a render target that isn't in the default pool.\n");
7253 /* FIXME: Check that the format is supported by the device. */
7255 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7256 if (!resource_size)
7257 return WINED3DERR_INVALIDCALL;
7259 surface->surface_type = surface_type;
7261 switch (surface_type)
7263 case SURFACE_OPENGL:
7264 surface->surface_ops = &surface_ops;
7265 break;
7267 case SURFACE_GDI:
7268 surface->surface_ops = &gdi_surface_ops;
7269 break;
7271 default:
7272 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7273 return WINED3DERR_INVALIDCALL;
7276 hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
7277 multisample_type, multisample_quality, usage, pool, width, height, 1,
7278 resource_size, parent, parent_ops, &surface_resource_ops);
7279 if (FAILED(hr))
7281 WARN("Failed to initialize resource, returning %#x.\n", hr);
7282 return hr;
7285 /* "Standalone" surface. */
7286 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7288 surface->texture_level = level;
7289 list_init(&surface->overlays);
7291 /* Flags */
7292 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7293 if (flags & WINED3D_SURFACE_DISCARD)
7294 surface->flags |= SFLAG_DISCARD;
7295 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
7296 surface->flags |= SFLAG_PIN_SYSMEM;
7297 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7298 surface->flags |= SFLAG_LOCKABLE;
7299 /* I'm not sure if this qualifies as a hack or as an optimization. It
7300 * seems reasonable to assume that lockable render targets will get
7301 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7302 * creation. However, the other reason we want to do this is that several
7303 * ddraw applications access surface memory while the surface isn't
7304 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7305 * future locks prevents these from crashing. */
7306 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7307 surface->flags |= SFLAG_DYNLOCK;
7309 /* Mark the texture as dirty so that it gets loaded first time around. */
7310 surface_add_dirty_rect(surface, NULL);
7311 list_init(&surface->renderbuffers);
7313 TRACE("surface %p, memory %p, size %u\n",
7314 surface, surface->resource.allocatedMemory, surface->resource.size);
7316 /* Call the private setup routine */
7317 hr = surface->surface_ops->surface_private_setup(surface);
7318 if (FAILED(hr))
7320 ERR("Private setup failed, returning %#x\n", hr);
7321 surface_cleanup(surface);
7322 return hr;
7325 /* Similar to lockable rendertargets above, creating the DIB section
7326 * during surface initialization prevents the sysmem pointer from changing
7327 * after a wined3d_surface_getdc() call. */
7328 if ((usage & WINED3DUSAGE_OWNDC) && !surface->hDC
7329 && SUCCEEDED(surface_create_dib_section(surface)))
7331 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
7332 surface->resource.heapMemory = NULL;
7333 surface->resource.allocatedMemory = surface->dib.bitmap_data;
7336 return hr;
7339 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7340 enum wined3d_format_id format_id, UINT level, DWORD usage, enum wined3d_pool pool,
7341 enum wined3d_multisample_type multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7342 DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7344 struct wined3d_surface *object;
7345 HRESULT hr;
7347 TRACE("device %p, width %u, height %u, format %s, level %u\n",
7348 device, width, height, debug_d3dformat(format_id), level);
7349 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7350 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7351 TRACE("surface_type %#x, flags %#x, parent %p, parent_ops %p.\n", surface_type, flags, parent, parent_ops);
7353 if (surface_type == SURFACE_OPENGL && !device->adapter)
7355 ERR("OpenGL surfaces are not available without OpenGL.\n");
7356 return WINED3DERR_NOTAVAILABLE;
7359 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7360 if (!object)
7362 ERR("Failed to allocate surface memory.\n");
7363 return WINED3DERR_OUTOFVIDEOMEMORY;
7366 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level,
7367 multisample_type, multisample_quality, device, usage, format_id, pool, flags, parent, parent_ops);
7368 if (FAILED(hr))
7370 WARN("Failed to initialize surface, returning %#x.\n", hr);
7371 HeapFree(GetProcessHeap(), 0, object);
7372 return hr;
7375 TRACE("Created surface %p.\n", object);
7376 *surface = object;
7378 return hr;