mshtml: Respond to SID_SContainerDispatch service id.
[wine/multimedia.git] / dlls / wined3d / surface.c
blob023bf6afd0ccd534238b080acc372eb701ab774f
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 enum wined3d_conversion_type convert;
2417 struct wined3d_context *context;
2418 struct wined3d_bo_address data;
2419 struct wined3d_format format;
2420 UINT update_w, update_h;
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 != WINED3D_CT_NONE || 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 enum wined3d_conversion_type 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 != WINED3D_CT_NONE || format.convert)
4349 surface->flags |= SFLAG_CONVERTED;
4350 else surface->flags &= ~SFLAG_CONVERTED;
4352 surface_bind_and_dirtify(surface, context, srgb);
4353 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4354 surface->flags |= alloc_flag;
4357 /* Context activation is done by the caller. */
4358 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4360 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4362 struct wined3d_texture *texture = surface->container.u.texture;
4363 UINT sub_count = texture->level_count * texture->layer_count;
4364 UINT i;
4366 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4368 for (i = 0; i < sub_count; ++i)
4370 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4371 surface_prepare_texture_internal(s, context, srgb);
4374 return;
4377 surface_prepare_texture_internal(surface, context, srgb);
4380 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4382 if (multisample)
4384 if (surface->rb_multisample)
4385 return;
4387 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4388 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4389 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4390 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4391 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4393 else
4395 if (surface->rb_resolved)
4396 return;
4398 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4399 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4400 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4401 surface->pow2Width, surface->pow2Height);
4402 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4406 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4407 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4409 struct wined3d_device *device = surface->resource.device;
4410 UINT pitch = wined3d_surface_get_pitch(surface);
4411 const struct wined3d_gl_info *gl_info;
4412 struct wined3d_context *context;
4413 RECT local_rect;
4414 UINT w, h;
4416 surface_get_rect(surface, rect, &local_rect);
4418 mem += local_rect.top * pitch + local_rect.left * bpp;
4419 w = local_rect.right - local_rect.left;
4420 h = local_rect.bottom - local_rect.top;
4422 /* Activate the correct context for the render target */
4423 context = context_acquire(device, surface);
4424 context_apply_blit_state(context, device);
4425 gl_info = context->gl_info;
4427 ENTER_GL();
4429 if (!surface_is_offscreen(surface))
4431 GLenum buffer = surface_get_gl_buffer(surface);
4432 TRACE("Unlocking %#x buffer.\n", buffer);
4433 context_set_draw_buffer(context, buffer);
4435 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4436 glPixelZoom(1.0f, -1.0f);
4438 else
4440 /* Primary offscreen render target */
4441 TRACE("Offscreen render target.\n");
4442 context_set_draw_buffer(context, device->offscreenBuffer);
4444 glPixelZoom(1.0f, 1.0f);
4447 glRasterPos3i(local_rect.left, local_rect.top, 1);
4448 checkGLcall("glRasterPos3i");
4450 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4451 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4453 if (surface->flags & SFLAG_PBO)
4455 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4456 checkGLcall("glBindBufferARB");
4459 glDrawPixels(w, h, fmt, type, mem);
4460 checkGLcall("glDrawPixels");
4462 if (surface->flags & SFLAG_PBO)
4464 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4465 checkGLcall("glBindBufferARB");
4468 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4469 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4471 LEAVE_GL();
4473 if (wined3d_settings.strict_draw_ordering
4474 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4475 && surface->container.u.swapchain->front_buffer == surface))
4476 wglFlush();
4478 context_release(context);
4481 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck, BOOL use_texturing,
4482 struct wined3d_format *format, enum wined3d_conversion_type *conversion_type)
4484 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4485 const struct wined3d_device *device = surface->resource.device;
4486 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4487 BOOL blit_supported = FALSE;
4489 /* Copy the default values from the surface. Below we might perform fixups */
4490 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4491 *format = *surface->resource.format;
4492 *conversion_type = WINED3D_CT_NONE;
4494 /* Ok, now look if we have to do any conversion */
4495 switch (surface->resource.format->id)
4497 case WINED3DFMT_P8_UINT:
4498 /* Below the call to blit_supported is disabled for Wine 1.2
4499 * because the function isn't operating correctly yet. At the
4500 * moment 8-bit blits are handled in software and if certain GL
4501 * extensions are around, surface conversion is performed at
4502 * upload time. The blit_supported call recognizes it as a
4503 * destination fixup. This type of upload 'fixup' and 8-bit to
4504 * 8-bit blits need to be handled by the blit_shader.
4505 * TODO: get rid of this #if 0. */
4506 #if 0
4507 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4508 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4509 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4510 #endif
4511 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4513 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4514 * texturing. Further also use conversion in case of color keying.
4515 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4516 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4517 * conflicts with this.
4519 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4520 || colorkey_active || !use_texturing)
4522 format->glFormat = GL_RGBA;
4523 format->glInternal = GL_RGBA;
4524 format->glType = GL_UNSIGNED_BYTE;
4525 format->conv_byte_count = 4;
4526 if (colorkey_active)
4527 *conversion_type = WINED3D_CT_PALETTED_CK;
4528 else
4529 *conversion_type = WINED3D_CT_PALETTED;
4531 break;
4533 case WINED3DFMT_B2G3R3_UNORM:
4534 /* **********************
4535 GL_UNSIGNED_BYTE_3_3_2
4536 ********************** */
4537 if (colorkey_active) {
4538 /* This texture format will never be used.. So do not care about color keying
4539 up until the point in time it will be needed :-) */
4540 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4542 break;
4544 case WINED3DFMT_B5G6R5_UNORM:
4545 if (colorkey_active)
4547 *conversion_type = WINED3D_CT_CK_565;
4548 format->glFormat = GL_RGBA;
4549 format->glInternal = GL_RGB5_A1;
4550 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4551 format->conv_byte_count = 2;
4553 break;
4555 case WINED3DFMT_B5G5R5X1_UNORM:
4556 if (colorkey_active)
4558 *conversion_type = WINED3D_CT_CK_5551;
4559 format->glFormat = GL_BGRA;
4560 format->glInternal = GL_RGB5_A1;
4561 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4562 format->conv_byte_count = 2;
4564 break;
4566 case WINED3DFMT_B8G8R8_UNORM:
4567 if (colorkey_active)
4569 *conversion_type = WINED3D_CT_CK_RGB24;
4570 format->glFormat = GL_RGBA;
4571 format->glInternal = GL_RGBA8;
4572 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4573 format->conv_byte_count = 4;
4575 break;
4577 case WINED3DFMT_B8G8R8X8_UNORM:
4578 if (colorkey_active)
4580 *conversion_type = WINED3D_CT_RGB32_888;
4581 format->glFormat = GL_RGBA;
4582 format->glInternal = GL_RGBA8;
4583 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4584 format->conv_byte_count = 4;
4586 break;
4588 default:
4589 break;
4592 if (*conversion_type != WINED3D_CT_NONE)
4594 format->rtInternal = format->glInternal;
4595 format->glGammaInternal = format->glInternal;
4598 return WINED3D_OK;
4601 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
4603 /* FIXME: Is this really how color keys are supposed to work? I think it
4604 * makes more sense to compare the individual channels. */
4605 return color >= color_key->color_space_low_value
4606 && color <= color_key->color_space_high_value;
4609 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4611 const struct wined3d_device *device = surface->resource.device;
4612 const struct wined3d_palette *pal = surface->palette;
4613 BOOL index_in_alpha = FALSE;
4614 unsigned int i;
4616 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4617 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4618 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4619 * duplicate entries. Store the color key in the unused alpha component to speed the
4620 * download up and to make conversion unneeded. */
4621 index_in_alpha = primary_render_target_is_p8(device);
4623 if (!pal)
4625 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4626 if (index_in_alpha)
4628 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4629 * there's no palette at this time. */
4630 for (i = 0; i < 256; i++) table[i][3] = i;
4633 else
4635 TRACE("Using surface palette %p\n", pal);
4636 /* Get the surface's palette */
4637 for (i = 0; i < 256; ++i)
4639 table[i][0] = pal->palents[i].peRed;
4640 table[i][1] = pal->palents[i].peGreen;
4641 table[i][2] = pal->palents[i].peBlue;
4643 /* When index_in_alpha is set the palette index is stored in the
4644 * alpha component. In case of a readback we can then read
4645 * GL_ALPHA. Color keying is handled in BltOverride using a
4646 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4647 * color key itself is passed to glAlphaFunc in other cases the
4648 * alpha component of pixels that should be masked away is set to 0. */
4649 if (index_in_alpha)
4650 table[i][3] = i;
4651 else if (colorkey && color_in_range(&surface->src_blt_color_key, i))
4652 table[i][3] = 0x00;
4653 else if (pal->flags & WINEDDPCAPS_ALPHA)
4654 table[i][3] = pal->palents[i].peFlags;
4655 else
4656 table[i][3] = 0xFF;
4661 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height,
4662 UINT outpitch, enum wined3d_conversion_type conversion_type, struct wined3d_surface *surface)
4664 const BYTE *source;
4665 BYTE *dest;
4667 TRACE("src %p, dst %p, pitch %u, width %u, height %u, outpitch %u, conversion_type %#x, surface %p.\n",
4668 src, dst, pitch, width, height, outpitch, conversion_type, surface);
4670 switch (conversion_type)
4672 case WINED3D_CT_NONE:
4674 memcpy(dst, src, pitch * height);
4675 break;
4678 case WINED3D_CT_PALETTED:
4679 case WINED3D_CT_PALETTED_CK:
4681 BYTE table[256][4];
4682 unsigned int x, y;
4684 d3dfmt_p8_init_palette(surface, table, (conversion_type == WINED3D_CT_PALETTED_CK));
4686 for (y = 0; y < height; y++)
4688 source = src + pitch * y;
4689 dest = dst + outpitch * y;
4690 /* This is an 1 bpp format, using the width here is fine */
4691 for (x = 0; x < width; x++) {
4692 BYTE color = *source++;
4693 *dest++ = table[color][0];
4694 *dest++ = table[color][1];
4695 *dest++ = table[color][2];
4696 *dest++ = table[color][3];
4700 break;
4702 case WINED3D_CT_CK_565:
4704 /* Converting the 565 format in 5551 packed to emulate color-keying.
4706 Note : in all these conversion, it would be best to average the averaging
4707 pixels to get the color of the pixel that will be color-keyed to
4708 prevent 'color bleeding'. This will be done later on if ever it is
4709 too visible.
4711 Note2: Nvidia documents say that their driver does not support alpha + color keying
4712 on the same surface and disables color keying in such a case
4714 unsigned int x, y;
4715 const WORD *Source;
4716 WORD *Dest;
4718 TRACE("Color keyed 565\n");
4720 for (y = 0; y < height; y++) {
4721 Source = (const WORD *)(src + y * pitch);
4722 Dest = (WORD *) (dst + y * outpitch);
4723 for (x = 0; x < width; x++ ) {
4724 WORD color = *Source++;
4725 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4726 if (!color_in_range(&surface->src_blt_color_key, color))
4727 *Dest |= 0x0001;
4728 Dest++;
4732 break;
4734 case WINED3D_CT_CK_5551:
4736 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4737 unsigned int x, y;
4738 const WORD *Source;
4739 WORD *Dest;
4740 TRACE("Color keyed 5551\n");
4741 for (y = 0; y < height; y++) {
4742 Source = (const WORD *)(src + y * pitch);
4743 Dest = (WORD *) (dst + y * outpitch);
4744 for (x = 0; x < width; x++ ) {
4745 WORD color = *Source++;
4746 *Dest = color;
4747 if (!color_in_range(&surface->src_blt_color_key, color))
4748 *Dest |= (1 << 15);
4749 else
4750 *Dest &= ~(1 << 15);
4751 Dest++;
4755 break;
4757 case WINED3D_CT_CK_RGB24:
4759 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4760 unsigned int x, y;
4761 for (y = 0; y < height; y++)
4763 source = src + pitch * y;
4764 dest = dst + outpitch * y;
4765 for (x = 0; x < width; x++) {
4766 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4767 DWORD dstcolor = color << 8;
4768 if (!color_in_range(&surface->src_blt_color_key, color))
4769 dstcolor |= 0xff;
4770 *(DWORD*)dest = dstcolor;
4771 source += 3;
4772 dest += 4;
4776 break;
4778 case WINED3D_CT_RGB32_888:
4780 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4781 unsigned int x, y;
4782 for (y = 0; y < height; y++)
4784 source = src + pitch * y;
4785 dest = dst + outpitch * y;
4786 for (x = 0; x < width; x++) {
4787 DWORD color = 0xffffff & *(const DWORD*)source;
4788 DWORD dstcolor = color << 8;
4789 if (!color_in_range(&surface->src_blt_color_key, color))
4790 dstcolor |= 0xff;
4791 *(DWORD*)dest = dstcolor;
4792 source += 4;
4793 dest += 4;
4797 break;
4799 default:
4800 ERR("Unsupported conversion type %#x.\n", conversion_type);
4802 return WINED3D_OK;
4805 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4807 /* Flip the surface contents */
4808 /* Flip the DC */
4810 HDC tmp;
4811 tmp = front->hDC;
4812 front->hDC = back->hDC;
4813 back->hDC = tmp;
4816 /* Flip the DIBsection */
4818 HBITMAP tmp = front->dib.DIBsection;
4819 front->dib.DIBsection = back->dib.DIBsection;
4820 back->dib.DIBsection = tmp;
4823 /* Flip the surface data */
4825 void* tmp;
4827 tmp = front->dib.bitmap_data;
4828 front->dib.bitmap_data = back->dib.bitmap_data;
4829 back->dib.bitmap_data = tmp;
4831 tmp = front->resource.allocatedMemory;
4832 front->resource.allocatedMemory = back->resource.allocatedMemory;
4833 back->resource.allocatedMemory = tmp;
4835 tmp = front->resource.heapMemory;
4836 front->resource.heapMemory = back->resource.heapMemory;
4837 back->resource.heapMemory = tmp;
4840 /* Flip the PBO */
4842 GLuint tmp_pbo = front->pbo;
4843 front->pbo = back->pbo;
4844 back->pbo = tmp_pbo;
4847 /* Flip the opengl texture */
4849 GLuint tmp;
4851 tmp = back->texture_name;
4852 back->texture_name = front->texture_name;
4853 front->texture_name = tmp;
4855 tmp = back->texture_name_srgb;
4856 back->texture_name_srgb = front->texture_name_srgb;
4857 front->texture_name_srgb = tmp;
4859 tmp = back->rb_multisample;
4860 back->rb_multisample = front->rb_multisample;
4861 front->rb_multisample = tmp;
4863 tmp = back->rb_resolved;
4864 back->rb_resolved = front->rb_resolved;
4865 front->rb_resolved = tmp;
4867 resource_unload(&back->resource);
4868 resource_unload(&front->resource);
4872 DWORD tmp_flags = back->flags;
4873 back->flags = front->flags;
4874 front->flags = tmp_flags;
4878 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4879 * pixel copy calls. */
4880 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4881 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4883 struct wined3d_device *device = dst_surface->resource.device;
4884 float xrel, yrel;
4885 UINT row;
4886 struct wined3d_context *context;
4887 BOOL upsidedown = FALSE;
4888 RECT dst_rect = *dst_rect_in;
4890 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4891 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4893 if(dst_rect.top > dst_rect.bottom) {
4894 UINT tmp = dst_rect.bottom;
4895 dst_rect.bottom = dst_rect.top;
4896 dst_rect.top = tmp;
4897 upsidedown = TRUE;
4900 context = context_acquire(device, src_surface);
4901 context_apply_blit_state(context, device);
4902 surface_internal_preload(dst_surface, SRGB_RGB);
4903 ENTER_GL();
4905 /* Bind the target texture */
4906 context_bind_texture(context, dst_surface->texture_target, dst_surface->texture_name);
4907 if (surface_is_offscreen(src_surface))
4909 TRACE("Reading from an offscreen target\n");
4910 upsidedown = !upsidedown;
4911 glReadBuffer(device->offscreenBuffer);
4913 else
4915 glReadBuffer(surface_get_gl_buffer(src_surface));
4917 checkGLcall("glReadBuffer");
4919 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4920 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4922 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4924 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4926 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4927 ERR("Texture filtering not supported in direct blit.\n");
4929 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
4930 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4932 ERR("Texture filtering not supported in direct blit\n");
4935 if (upsidedown
4936 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4937 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4939 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4941 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4942 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4943 src_rect->left, src_surface->resource.height - src_rect->bottom,
4944 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4946 else
4948 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4949 /* I have to process this row by row to swap the image,
4950 * otherwise it would be upside down, so stretching in y direction
4951 * doesn't cost extra time
4953 * However, stretching in x direction can be avoided if not necessary
4955 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4956 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4958 /* Well, that stuff works, but it's very slow.
4959 * find a better way instead
4961 UINT col;
4963 for (col = dst_rect.left; col < dst_rect.right; ++col)
4965 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4966 dst_rect.left + col /* x offset */, row /* y offset */,
4967 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4970 else
4972 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4973 dst_rect.left /* x offset */, row /* y offset */,
4974 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4978 checkGLcall("glCopyTexSubImage2D");
4980 LEAVE_GL();
4981 context_release(context);
4983 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4984 * path is never entered
4986 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4989 /* Uses the hardware to stretch and flip the image */
4990 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4991 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
4993 struct wined3d_device *device = dst_surface->resource.device;
4994 struct wined3d_swapchain *src_swapchain = NULL;
4995 GLuint src, backup = 0;
4996 float left, right, top, bottom; /* Texture coordinates */
4997 UINT fbwidth = src_surface->resource.width;
4998 UINT fbheight = src_surface->resource.height;
4999 struct wined3d_context *context;
5000 GLenum drawBuffer = GL_BACK;
5001 GLenum texture_target;
5002 BOOL noBackBufferBackup;
5003 BOOL src_offscreen;
5004 BOOL upsidedown = FALSE;
5005 RECT dst_rect = *dst_rect_in;
5007 TRACE("Using hwstretch blit\n");
5008 /* Activate the Proper context for reading from the source surface, set it up for blitting */
5009 context = context_acquire(device, src_surface);
5010 context_apply_blit_state(context, device);
5011 surface_internal_preload(dst_surface, SRGB_RGB);
5013 src_offscreen = surface_is_offscreen(src_surface);
5014 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
5015 if (!noBackBufferBackup && !src_surface->texture_name)
5017 /* Get it a description */
5018 surface_internal_preload(src_surface, SRGB_RGB);
5020 ENTER_GL();
5022 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
5023 * This way we don't have to wait for the 2nd readback to finish to leave this function.
5025 if (context->aux_buffers >= 2)
5027 /* Got more than one aux buffer? Use the 2nd aux buffer */
5028 drawBuffer = GL_AUX1;
5030 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
5032 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
5033 drawBuffer = GL_AUX0;
5036 if(noBackBufferBackup) {
5037 glGenTextures(1, &backup);
5038 checkGLcall("glGenTextures");
5039 context_bind_texture(context, GL_TEXTURE_2D, backup);
5040 texture_target = GL_TEXTURE_2D;
5041 } else {
5042 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
5043 * we are reading from the back buffer, the backup can be used as source texture
5045 texture_target = src_surface->texture_target;
5046 context_bind_texture(context, texture_target, src_surface->texture_name);
5047 glEnable(texture_target);
5048 checkGLcall("glEnable(texture_target)");
5050 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
5051 src_surface->flags &= ~SFLAG_INTEXTURE;
5054 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
5055 * glCopyTexSubImage is a bit picky about the parameters we pass to it
5057 if(dst_rect.top > dst_rect.bottom) {
5058 UINT tmp = dst_rect.bottom;
5059 dst_rect.bottom = dst_rect.top;
5060 dst_rect.top = tmp;
5061 upsidedown = TRUE;
5064 if (src_offscreen)
5066 TRACE("Reading from an offscreen target\n");
5067 upsidedown = !upsidedown;
5068 glReadBuffer(device->offscreenBuffer);
5070 else
5072 glReadBuffer(surface_get_gl_buffer(src_surface));
5075 /* TODO: Only back up the part that will be overwritten */
5076 glCopyTexSubImage2D(texture_target, 0,
5077 0, 0 /* read offsets */,
5078 0, 0,
5079 fbwidth,
5080 fbheight);
5082 checkGLcall("glCopyTexSubImage2D");
5084 /* No issue with overriding these - the sampler is dirty due to blit usage */
5085 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
5086 wined3d_gl_mag_filter(magLookup, filter));
5087 checkGLcall("glTexParameteri");
5088 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5089 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
5090 checkGLcall("glTexParameteri");
5092 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5093 src_swapchain = src_surface->container.u.swapchain;
5094 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5096 src = backup ? backup : src_surface->texture_name;
5098 else
5100 glReadBuffer(GL_FRONT);
5101 checkGLcall("glReadBuffer(GL_FRONT)");
5103 glGenTextures(1, &src);
5104 checkGLcall("glGenTextures(1, &src)");
5105 context_bind_texture(context, GL_TEXTURE_2D, src);
5107 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5108 * out for power of 2 sizes
5110 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5111 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5112 checkGLcall("glTexImage2D");
5113 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5114 0, 0 /* read offsets */,
5115 0, 0,
5116 fbwidth,
5117 fbheight);
5119 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5120 checkGLcall("glTexParameteri");
5121 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5122 checkGLcall("glTexParameteri");
5124 glReadBuffer(GL_BACK);
5125 checkGLcall("glReadBuffer(GL_BACK)");
5127 if(texture_target != GL_TEXTURE_2D) {
5128 glDisable(texture_target);
5129 glEnable(GL_TEXTURE_2D);
5130 texture_target = GL_TEXTURE_2D;
5133 checkGLcall("glEnd and previous");
5135 left = src_rect->left;
5136 right = src_rect->right;
5138 if (!upsidedown)
5140 top = src_surface->resource.height - src_rect->top;
5141 bottom = src_surface->resource.height - src_rect->bottom;
5143 else
5145 top = src_surface->resource.height - src_rect->bottom;
5146 bottom = src_surface->resource.height - src_rect->top;
5149 if (src_surface->flags & SFLAG_NORMCOORD)
5151 left /= src_surface->pow2Width;
5152 right /= src_surface->pow2Width;
5153 top /= src_surface->pow2Height;
5154 bottom /= src_surface->pow2Height;
5157 /* draw the source texture stretched and upside down. The correct surface is bound already */
5158 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5159 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5161 context_set_draw_buffer(context, drawBuffer);
5162 glReadBuffer(drawBuffer);
5164 glBegin(GL_QUADS);
5165 /* bottom left */
5166 glTexCoord2f(left, bottom);
5167 glVertex2i(0, 0);
5169 /* top left */
5170 glTexCoord2f(left, top);
5171 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5173 /* top right */
5174 glTexCoord2f(right, top);
5175 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5177 /* bottom right */
5178 glTexCoord2f(right, bottom);
5179 glVertex2i(dst_rect.right - dst_rect.left, 0);
5180 glEnd();
5181 checkGLcall("glEnd and previous");
5183 if (texture_target != dst_surface->texture_target)
5185 glDisable(texture_target);
5186 glEnable(dst_surface->texture_target);
5187 texture_target = dst_surface->texture_target;
5190 /* Now read the stretched and upside down image into the destination texture */
5191 context_bind_texture(context, texture_target, dst_surface->texture_name);
5192 glCopyTexSubImage2D(texture_target,
5194 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5195 0, 0, /* We blitted the image to the origin */
5196 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5197 checkGLcall("glCopyTexSubImage2D");
5199 if(drawBuffer == GL_BACK) {
5200 /* Write the back buffer backup back */
5201 if(backup) {
5202 if(texture_target != GL_TEXTURE_2D) {
5203 glDisable(texture_target);
5204 glEnable(GL_TEXTURE_2D);
5205 texture_target = GL_TEXTURE_2D;
5207 context_bind_texture(context, GL_TEXTURE_2D, backup);
5209 else
5211 if (texture_target != src_surface->texture_target)
5213 glDisable(texture_target);
5214 glEnable(src_surface->texture_target);
5215 texture_target = src_surface->texture_target;
5217 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5220 glBegin(GL_QUADS);
5221 /* top left */
5222 glTexCoord2f(0.0f, 0.0f);
5223 glVertex2i(0, fbheight);
5225 /* bottom left */
5226 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5227 glVertex2i(0, 0);
5229 /* bottom right */
5230 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5231 (float)fbheight / (float)src_surface->pow2Height);
5232 glVertex2i(fbwidth, 0);
5234 /* top right */
5235 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5236 glVertex2i(fbwidth, fbheight);
5237 glEnd();
5239 glDisable(texture_target);
5240 checkGLcall("glDisable(texture_target)");
5242 /* Cleanup */
5243 if (src != src_surface->texture_name && src != backup)
5245 glDeleteTextures(1, &src);
5246 checkGLcall("glDeleteTextures(1, &src)");
5248 if(backup) {
5249 glDeleteTextures(1, &backup);
5250 checkGLcall("glDeleteTextures(1, &backup)");
5253 LEAVE_GL();
5255 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5257 context_release(context);
5259 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5260 * path is never entered
5262 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5265 /* Front buffer coordinates are always full screen coordinates, but our GL
5266 * drawable is limited to the window's client area. The sysmem and texture
5267 * copies do have the full screen size. Note that GL has a bottom-left
5268 * origin, while D3D has a top-left origin. */
5269 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5271 UINT drawable_height;
5273 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5274 && surface == surface->container.u.swapchain->front_buffer)
5276 POINT offset = {0, 0};
5277 RECT windowsize;
5279 ScreenToClient(window, &offset);
5280 OffsetRect(rect, offset.x, offset.y);
5282 GetClientRect(window, &windowsize);
5283 drawable_height = windowsize.bottom - windowsize.top;
5285 else
5287 drawable_height = surface->resource.height;
5290 rect->top = drawable_height - rect->top;
5291 rect->bottom = drawable_height - rect->bottom;
5294 static void surface_blt_to_drawable(const struct wined3d_device *device,
5295 enum wined3d_texture_filter_type filter, BOOL color_key,
5296 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5297 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5299 struct wined3d_context *context;
5300 RECT src_rect, dst_rect;
5302 src_rect = *src_rect_in;
5303 dst_rect = *dst_rect_in;
5305 /* Make sure the surface is up-to-date. This should probably use
5306 * surface_load_location() and worry about the destination surface too,
5307 * unless we're overwriting it completely. */
5308 surface_internal_preload(src_surface, SRGB_RGB);
5310 /* Activate the destination context, set it up for blitting */
5311 context = context_acquire(device, dst_surface);
5312 context_apply_blit_state(context, device);
5314 if (!surface_is_offscreen(dst_surface))
5315 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5317 device->blitter->set_shader(device->blit_priv, context, src_surface);
5319 ENTER_GL();
5321 if (color_key)
5323 glEnable(GL_ALPHA_TEST);
5324 checkGLcall("glEnable(GL_ALPHA_TEST)");
5326 /* When the primary render target uses P8, the alpha component
5327 * contains the palette index. Which means that the colorkey is one of
5328 * the palette entries. In other cases pixels that should be masked
5329 * away have alpha set to 0. */
5330 if (primary_render_target_is_p8(device))
5331 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->src_blt_color_key.color_space_low_value / 256.0f);
5332 else
5333 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5334 checkGLcall("glAlphaFunc");
5336 else
5338 glDisable(GL_ALPHA_TEST);
5339 checkGLcall("glDisable(GL_ALPHA_TEST)");
5342 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5344 if (color_key)
5346 glDisable(GL_ALPHA_TEST);
5347 checkGLcall("glDisable(GL_ALPHA_TEST)");
5350 LEAVE_GL();
5352 /* Leave the opengl state valid for blitting */
5353 device->blitter->unset_shader(context->gl_info);
5355 if (wined3d_settings.strict_draw_ordering
5356 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5357 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5358 wglFlush(); /* Flush to ensure ordering across contexts. */
5360 context_release(context);
5363 /* Do not call while under the GL lock. */
5364 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
5366 struct wined3d_device *device = s->resource.device;
5367 const struct blit_shader *blitter;
5369 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5370 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5371 if (!blitter)
5373 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5374 return WINED3DERR_INVALIDCALL;
5377 return blitter->color_fill(device, s, rect, color);
5380 /* Do not call while under the GL lock. */
5381 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5382 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5383 enum wined3d_texture_filter_type filter)
5385 struct wined3d_device *device = dst_surface->resource.device;
5386 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5387 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5389 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5390 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5391 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
5393 /* Get the swapchain. One of the surfaces has to be a primary surface */
5394 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
5396 WARN("Destination is in sysmem, rejecting gl blt\n");
5397 return WINED3DERR_INVALIDCALL;
5400 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5401 dstSwapchain = dst_surface->container.u.swapchain;
5403 if (src_surface)
5405 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
5407 WARN("Src is in sysmem, rejecting gl blt\n");
5408 return WINED3DERR_INVALIDCALL;
5411 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5412 srcSwapchain = src_surface->container.u.swapchain;
5415 /* Early sort out of cases where no render target is used */
5416 if (!dstSwapchain && !srcSwapchain
5417 && src_surface != device->fb.render_targets[0]
5418 && dst_surface != device->fb.render_targets[0])
5420 TRACE("No surface is render target, not using hardware blit.\n");
5421 return WINED3DERR_INVALIDCALL;
5424 /* No destination color keying supported */
5425 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5427 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5428 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5429 return WINED3DERR_INVALIDCALL;
5432 if (dstSwapchain && dstSwapchain == srcSwapchain)
5434 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5435 return WINED3DERR_INVALIDCALL;
5438 if (dstSwapchain && srcSwapchain)
5440 FIXME("Implement hardware blit between two different swapchains\n");
5441 return WINED3DERR_INVALIDCALL;
5444 if (dstSwapchain)
5446 /* Handled with regular texture -> swapchain blit */
5447 if (src_surface == device->fb.render_targets[0])
5448 TRACE("Blit from active render target to a swapchain\n");
5450 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5452 FIXME("Implement blit from a swapchain to the active render target\n");
5453 return WINED3DERR_INVALIDCALL;
5456 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5458 /* Blit from render target to texture */
5459 BOOL stretchx;
5461 /* P8 read back is not implemented */
5462 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5463 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5465 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5466 return WINED3DERR_INVALIDCALL;
5469 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5471 TRACE("Color keying not supported by frame buffer to texture blit\n");
5472 return WINED3DERR_INVALIDCALL;
5473 /* Destination color key is checked above */
5476 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5477 stretchx = TRUE;
5478 else
5479 stretchx = FALSE;
5481 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5482 * flip the image nor scale it.
5484 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5485 * -> If the app wants a image width an unscaled width, copy it line per line
5486 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5487 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5488 * back buffer. This is slower than reading line per line, thus not used for flipping
5489 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5490 * pixel by pixel. */
5491 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5492 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5494 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
5495 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
5497 else
5499 TRACE("Using hardware stretching to flip / stretch the texture.\n");
5500 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
5503 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5505 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5506 dst_surface->resource.allocatedMemory = NULL;
5507 dst_surface->resource.heapMemory = NULL;
5509 else
5511 dst_surface->flags &= ~SFLAG_INSYSMEM;
5514 return WINED3D_OK;
5516 else if (src_surface)
5518 /* Blit from offscreen surface to render target */
5519 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
5520 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5522 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5524 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5525 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5526 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5528 FIXME("Unsupported blit operation falling back to software\n");
5529 return WINED3DERR_INVALIDCALL;
5532 /* Color keying: Check if we have to do a color keyed blt,
5533 * and if not check if a color key is activated.
5535 * Just modify the color keying parameters in the surface and restore them afterwards
5536 * The surface keeps track of the color key last used to load the opengl surface.
5537 * PreLoad will catch the change to the flags and color key and reload if necessary.
5539 if (flags & WINEDDBLT_KEYSRC)
5541 /* Use color key from surface */
5543 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5545 /* Use color key from DDBltFx */
5546 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5547 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
5549 else
5551 /* Do not use color key */
5552 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5555 surface_blt_to_drawable(device, filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5556 src_surface, src_rect, dst_surface, dst_rect);
5558 /* Restore the color key parameters */
5559 src_surface->CKeyFlags = oldCKeyFlags;
5560 src_surface->src_blt_color_key = old_blt_key;
5562 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5564 return WINED3D_OK;
5567 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5568 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5569 return WINED3DERR_INVALIDCALL;
5572 /* GL locking is done by the caller */
5573 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5574 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5576 struct wined3d_device *device = surface->resource.device;
5577 const struct wined3d_gl_info *gl_info = context->gl_info;
5578 GLint compare_mode = GL_NONE;
5579 struct blt_info info;
5580 GLint old_binding = 0;
5581 RECT rect;
5583 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5585 glDisable(GL_CULL_FACE);
5586 glDisable(GL_BLEND);
5587 glDisable(GL_ALPHA_TEST);
5588 glDisable(GL_SCISSOR_TEST);
5589 glDisable(GL_STENCIL_TEST);
5590 glEnable(GL_DEPTH_TEST);
5591 glDepthFunc(GL_ALWAYS);
5592 glDepthMask(GL_TRUE);
5593 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5594 glViewport(x, y, w, h);
5596 SetRect(&rect, 0, h, w, 0);
5597 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5598 context_active_texture(context, context->gl_info, 0);
5599 glGetIntegerv(info.binding, &old_binding);
5600 glBindTexture(info.bind_target, texture);
5601 if (gl_info->supported[ARB_SHADOW])
5603 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5604 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5607 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5608 gl_info, info.tex_type, &surface->ds_current_size);
5610 glBegin(GL_TRIANGLE_STRIP);
5611 glTexCoord3fv(info.coords[0]);
5612 glVertex2f(-1.0f, -1.0f);
5613 glTexCoord3fv(info.coords[1]);
5614 glVertex2f(1.0f, -1.0f);
5615 glTexCoord3fv(info.coords[2]);
5616 glVertex2f(-1.0f, 1.0f);
5617 glTexCoord3fv(info.coords[3]);
5618 glVertex2f(1.0f, 1.0f);
5619 glEnd();
5621 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5622 glBindTexture(info.bind_target, old_binding);
5624 glPopAttrib();
5626 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5629 void surface_modify_ds_location(struct wined3d_surface *surface,
5630 DWORD location, UINT w, UINT h)
5632 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5634 if (location & ~(SFLAG_LOCATIONS | SFLAG_LOST))
5635 FIXME("Invalid location (%#x) specified.\n", location);
5637 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5638 || (!(surface->flags & SFLAG_INTEXTURE) && (location & SFLAG_INTEXTURE)))
5640 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5642 TRACE("Passing to container.\n");
5643 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5647 surface->ds_current_size.cx = w;
5648 surface->ds_current_size.cy = h;
5649 surface->flags &= ~(SFLAG_LOCATIONS | SFLAG_LOST);
5650 surface->flags |= location;
5653 /* Context activation is done by the caller. */
5654 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5656 struct wined3d_device *device = surface->resource.device;
5657 GLsizei w, h;
5659 TRACE("surface %p, new location %#x.\n", surface, location);
5661 /* TODO: Make this work for modes other than FBO */
5662 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5664 if (!(surface->flags & location))
5666 w = surface->ds_current_size.cx;
5667 h = surface->ds_current_size.cy;
5668 surface->ds_current_size.cx = 0;
5669 surface->ds_current_size.cy = 0;
5671 else
5673 w = surface->resource.width;
5674 h = surface->resource.height;
5677 if (surface->ds_current_size.cx == surface->resource.width
5678 && surface->ds_current_size.cy == surface->resource.height)
5680 TRACE("Location (%#x) is already up to date.\n", location);
5681 return;
5684 if (surface->current_renderbuffer)
5686 FIXME("Not supported with fixed up depth stencil.\n");
5687 return;
5690 if (surface->flags & SFLAG_LOST)
5692 TRACE("Surface was discarded, no need copy data.\n");
5693 switch (location)
5695 case SFLAG_INTEXTURE:
5696 surface_prepare_texture(surface, context, FALSE);
5697 break;
5698 case SFLAG_INRB_MULTISAMPLE:
5699 surface_prepare_rb(surface, context->gl_info, TRUE);
5700 break;
5701 case SFLAG_INDRAWABLE:
5702 /* Nothing to do */
5703 break;
5704 default:
5705 FIXME("Unhandled location %#x\n", location);
5707 surface->flags &= ~SFLAG_LOST;
5708 surface->flags |= location;
5709 surface->ds_current_size.cx = surface->resource.width;
5710 surface->ds_current_size.cy = surface->resource.height;
5711 return;
5714 if (!(surface->flags & SFLAG_LOCATIONS))
5716 FIXME("No up to date depth stencil location.\n");
5717 surface->flags |= location;
5718 surface->ds_current_size.cx = surface->resource.width;
5719 surface->ds_current_size.cy = surface->resource.height;
5720 return;
5723 if (location == SFLAG_INTEXTURE)
5725 GLint old_binding = 0;
5726 GLenum bind_target;
5728 /* The render target is allowed to be smaller than the depth/stencil
5729 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5730 * than the offscreen surface. Don't overwrite the offscreen surface
5731 * with undefined data. */
5732 w = min(w, context->swapchain->desc.backbuffer_width);
5733 h = min(h, context->swapchain->desc.backbuffer_height);
5735 TRACE("Copying onscreen depth buffer to depth texture.\n");
5737 ENTER_GL();
5739 if (!device->depth_blt_texture)
5741 glGenTextures(1, &device->depth_blt_texture);
5744 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5745 * directly on the FBO texture. That's because we need to flip. */
5746 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5747 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5748 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5750 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5751 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5753 else
5755 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5756 bind_target = GL_TEXTURE_2D;
5758 glBindTexture(bind_target, device->depth_blt_texture);
5759 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5760 * internal format, because the internal format might include stencil
5761 * data. In principle we should copy stencil data as well, but unless
5762 * the driver supports stencil export it's hard to do, and doesn't
5763 * seem to be needed in practice. If the hardware doesn't support
5764 * writing stencil data, the glCopyTexImage2D() call might trigger
5765 * software fallbacks. */
5766 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5767 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5768 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5769 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5770 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5771 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5772 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5773 glBindTexture(bind_target, old_binding);
5775 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5776 NULL, surface, SFLAG_INTEXTURE);
5777 context_set_draw_buffer(context, GL_NONE);
5778 glReadBuffer(GL_NONE);
5780 /* Do the actual blit */
5781 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5782 checkGLcall("depth_blt");
5784 context_invalidate_state(context, STATE_FRAMEBUFFER);
5786 LEAVE_GL();
5788 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5790 else if (location == SFLAG_INDRAWABLE)
5792 TRACE("Copying depth texture to onscreen depth buffer.\n");
5794 ENTER_GL();
5796 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5797 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5798 surface_depth_blt(surface, context, surface->texture_name,
5799 0, surface->pow2Height - h, w, h, surface->texture_target);
5800 checkGLcall("depth_blt");
5802 context_invalidate_state(context, STATE_FRAMEBUFFER);
5804 LEAVE_GL();
5806 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5808 else
5810 ERR("Invalid location (%#x) specified.\n", location);
5813 surface->flags |= location;
5814 surface->ds_current_size.cx = surface->resource.width;
5815 surface->ds_current_size.cy = surface->resource.height;
5818 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5820 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5821 struct wined3d_surface *overlay;
5823 TRACE("surface %p, location %s, persistent %#x.\n",
5824 surface, debug_surflocation(location), persistent);
5826 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5827 && !(surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
5828 && (location & SFLAG_INDRAWABLE))
5829 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5831 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5832 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5833 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5835 if (persistent)
5837 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5838 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5840 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5842 TRACE("Passing to container.\n");
5843 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5846 surface->flags &= ~SFLAG_LOCATIONS;
5847 surface->flags |= location;
5849 /* Redraw emulated overlays, if any */
5850 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5852 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5854 surface_draw_overlay(overlay);
5858 else
5860 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5862 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5864 TRACE("Passing to container\n");
5865 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5868 surface->flags &= ~location;
5871 if (!(surface->flags & SFLAG_LOCATIONS))
5873 ERR("Surface %p does not have any up to date location.\n", surface);
5877 static DWORD resource_access_from_location(DWORD location)
5879 switch (location)
5881 case SFLAG_INSYSMEM:
5882 return WINED3D_RESOURCE_ACCESS_CPU;
5884 case SFLAG_INDRAWABLE:
5885 case SFLAG_INSRGBTEX:
5886 case SFLAG_INTEXTURE:
5887 case SFLAG_INRB_MULTISAMPLE:
5888 case SFLAG_INRB_RESOLVED:
5889 return WINED3D_RESOURCE_ACCESS_GPU;
5891 default:
5892 FIXME("Unhandled location %#x.\n", location);
5893 return 0;
5897 static void surface_load_sysmem(struct wined3d_surface *surface,
5898 const struct wined3d_gl_info *gl_info, const RECT *rect)
5900 surface_prepare_system_memory(surface);
5902 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5903 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5905 /* Download the surface to system memory. */
5906 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5908 struct wined3d_device *device = surface->resource.device;
5909 struct wined3d_context *context;
5911 /* TODO: Use already acquired context when possible. */
5912 context = context_acquire(device, NULL);
5914 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5915 surface_download_data(surface, gl_info);
5917 context_release(context);
5919 return;
5922 if (surface->flags & SFLAG_INDRAWABLE)
5924 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5925 wined3d_surface_get_pitch(surface));
5926 return;
5929 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5930 surface, surface->flags & SFLAG_LOCATIONS);
5933 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5934 const struct wined3d_gl_info *gl_info, const RECT *rect)
5936 struct wined3d_device *device = surface->resource.device;
5937 enum wined3d_conversion_type convert;
5938 struct wined3d_format format;
5939 UINT byte_count;
5940 BYTE *mem;
5942 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5944 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5945 return WINED3DERR_INVALIDCALL;
5948 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5949 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5951 if (surface->flags & SFLAG_INTEXTURE)
5953 RECT r;
5955 surface_get_rect(surface, rect, &r);
5956 surface_blt_to_drawable(device, WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
5958 return WINED3D_OK;
5961 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5963 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5964 * path through sysmem. */
5965 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5968 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5970 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5971 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5972 * called. */
5973 if ((convert != WINED3D_CT_NONE) && (surface->flags & SFLAG_PBO))
5975 struct wined3d_context *context;
5977 TRACE("Removing the pbo attached to surface %p.\n", surface);
5979 /* TODO: Use already acquired context when possible. */
5980 context = context_acquire(device, NULL);
5982 surface_remove_pbo(surface, gl_info);
5984 context_release(context);
5987 if ((convert != WINED3D_CT_NONE) && surface->resource.allocatedMemory)
5989 UINT height = surface->resource.height;
5990 UINT width = surface->resource.width;
5991 UINT src_pitch, dst_pitch;
5993 byte_count = format.conv_byte_count;
5994 src_pitch = wined3d_surface_get_pitch(surface);
5996 /* Stick to the alignment for the converted surface too, makes it
5997 * easier to load the surface. */
5998 dst_pitch = width * byte_count;
5999 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6001 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6003 ERR("Out of memory (%u).\n", dst_pitch * height);
6004 return E_OUTOFMEMORY;
6007 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
6008 src_pitch, width, height, dst_pitch, convert, surface);
6010 surface->flags |= SFLAG_CONVERTED;
6012 else
6014 surface->flags &= ~SFLAG_CONVERTED;
6015 mem = surface->resource.allocatedMemory;
6016 byte_count = format.byte_count;
6019 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
6021 /* Don't delete PBO memory. */
6022 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6023 HeapFree(GetProcessHeap(), 0, mem);
6025 return WINED3D_OK;
6028 static HRESULT surface_load_texture(struct wined3d_surface *surface,
6029 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
6031 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
6032 struct wined3d_device *device = surface->resource.device;
6033 enum wined3d_conversion_type convert;
6034 struct wined3d_context *context;
6035 UINT width, src_pitch, dst_pitch;
6036 struct wined3d_bo_address data;
6037 struct wined3d_format format;
6038 POINT dst_point = {0, 0};
6039 BYTE *mem;
6041 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
6042 && surface_is_offscreen(surface)
6043 && (surface->flags & SFLAG_INDRAWABLE))
6045 surface_load_fb_texture(surface, srgb);
6047 return WINED3D_OK;
6050 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6051 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
6052 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6053 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6054 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6056 if (srgb)
6057 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INTEXTURE,
6058 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6059 else
6060 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, SFLAG_INSRGBTEX,
6061 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6063 return WINED3D_OK;
6066 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
6067 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
6068 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6069 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6070 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6072 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
6073 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
6074 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6076 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
6077 &rect, surface, dst_location, &rect);
6079 return WINED3D_OK;
6082 /* Upload from system memory */
6084 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6085 TRUE /* We will use textures */, &format, &convert);
6087 if (srgb)
6089 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6091 /* Performance warning... */
6092 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6093 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6096 else
6098 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6100 /* Performance warning... */
6101 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6102 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6106 if (!(surface->flags & SFLAG_INSYSMEM))
6108 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6109 /* Lets hope we get it from somewhere... */
6110 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6113 /* TODO: Use already acquired context when possible. */
6114 context = context_acquire(device, NULL);
6116 surface_prepare_texture(surface, context, srgb);
6117 surface_bind_and_dirtify(surface, context, srgb);
6119 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6121 surface->flags |= SFLAG_GLCKEY;
6122 surface->gl_color_key = surface->src_blt_color_key;
6124 else surface->flags &= ~SFLAG_GLCKEY;
6126 width = surface->resource.width;
6127 src_pitch = wined3d_surface_get_pitch(surface);
6129 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6130 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6131 * called. */
6132 if ((convert != WINED3D_CT_NONE || format.convert) && (surface->flags & SFLAG_PBO))
6134 TRACE("Removing the pbo attached to surface %p.\n", surface);
6135 surface_remove_pbo(surface, gl_info);
6138 if (format.convert)
6140 /* This code is entered for texture formats which need a fixup. */
6141 UINT height = surface->resource.height;
6143 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6144 dst_pitch = width * format.conv_byte_count;
6145 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6147 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6149 ERR("Out of memory (%u).\n", dst_pitch * height);
6150 context_release(context);
6151 return E_OUTOFMEMORY;
6153 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6154 format.byte_count = format.conv_byte_count;
6155 src_pitch = dst_pitch;
6157 else if (convert != WINED3D_CT_NONE && surface->resource.allocatedMemory)
6159 /* This code is only entered for color keying fixups */
6160 UINT height = surface->resource.height;
6162 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6163 dst_pitch = width * format.conv_byte_count;
6164 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6166 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6168 ERR("Out of memory (%u).\n", dst_pitch * height);
6169 context_release(context);
6170 return E_OUTOFMEMORY;
6172 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6173 width, height, dst_pitch, convert, surface);
6174 format.byte_count = format.conv_byte_count;
6175 src_pitch = dst_pitch;
6177 else
6179 mem = surface->resource.allocatedMemory;
6182 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6183 data.addr = mem;
6184 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
6186 context_release(context);
6188 /* Don't delete PBO memory. */
6189 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6190 HeapFree(GetProcessHeap(), 0, mem);
6192 return WINED3D_OK;
6195 static void surface_multisample_resolve(struct wined3d_surface *surface)
6197 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6199 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6200 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6202 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
6203 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6206 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6208 struct wined3d_device *device = surface->resource.device;
6209 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6210 HRESULT hr;
6212 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6214 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6216 if (location == SFLAG_INTEXTURE)
6218 struct wined3d_context *context = context_acquire(device, NULL);
6219 surface_load_ds_location(surface, context, location);
6220 context_release(context);
6221 return WINED3D_OK;
6223 else
6225 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6226 return WINED3DERR_INVALIDCALL;
6230 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6231 location = SFLAG_INTEXTURE;
6233 if (surface->flags & location)
6235 TRACE("Location already up to date.\n");
6237 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
6238 && surface_need_pbo(surface, gl_info))
6239 surface_load_pbo(surface, gl_info);
6241 return WINED3D_OK;
6244 if (WARN_ON(d3d_surface))
6246 DWORD required_access = resource_access_from_location(location);
6247 if ((surface->resource.access_flags & required_access) != required_access)
6248 WARN("Operation requires %#x access, but surface only has %#x.\n",
6249 required_access, surface->resource.access_flags);
6252 if (!(surface->flags & SFLAG_LOCATIONS))
6254 ERR("Surface %p does not have any up to date location.\n", surface);
6255 surface->flags |= SFLAG_LOST;
6256 return WINED3DERR_DEVICELOST;
6259 switch (location)
6261 case SFLAG_INSYSMEM:
6262 surface_load_sysmem(surface, gl_info, rect);
6263 break;
6265 case SFLAG_INDRAWABLE:
6266 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6267 return hr;
6268 break;
6270 case SFLAG_INRB_RESOLVED:
6271 surface_multisample_resolve(surface);
6272 break;
6274 case SFLAG_INTEXTURE:
6275 case SFLAG_INSRGBTEX:
6276 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6277 return hr;
6278 break;
6280 default:
6281 ERR("Don't know how to handle location %#x.\n", location);
6282 break;
6285 if (!rect)
6287 surface->flags |= location;
6289 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6290 surface_evict_sysmem(surface);
6293 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6294 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6296 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6299 return WINED3D_OK;
6302 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6304 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6306 /* Not on a swapchain - must be offscreen */
6307 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6309 /* The front buffer is always onscreen */
6310 if (surface == swapchain->front_buffer) return FALSE;
6312 /* If the swapchain is rendered to an FBO, the backbuffer is
6313 * offscreen, otherwise onscreen */
6314 return swapchain->render_to_fbo;
6317 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6318 /* Context activation is done by the caller. */
6319 static void ffp_blit_free(struct wined3d_device *device) { }
6321 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6322 /* Context activation is done by the caller. */
6323 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6325 BYTE table[256][4];
6326 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6328 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6330 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6331 ENTER_GL();
6332 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6333 LEAVE_GL();
6336 /* Context activation is done by the caller. */
6337 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6339 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6341 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6342 * else the surface is converted in software at upload time in LoadLocation.
6344 if (!(surface->flags & SFLAG_CONVERTED) && fixup == COMPLEX_FIXUP_P8
6345 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6346 ffp_blit_p8_upload_palette(surface, context->gl_info);
6348 ENTER_GL();
6349 glEnable(surface->texture_target);
6350 checkGLcall("glEnable(surface->texture_target)");
6351 LEAVE_GL();
6352 return WINED3D_OK;
6355 /* Context activation is done by the caller. */
6356 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6358 ENTER_GL();
6359 glDisable(GL_TEXTURE_2D);
6360 checkGLcall("glDisable(GL_TEXTURE_2D)");
6361 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6363 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6364 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6366 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6368 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6369 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6371 LEAVE_GL();
6374 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6375 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
6376 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
6378 enum complex_fixup src_fixup;
6380 switch (blit_op)
6382 case WINED3D_BLIT_OP_COLOR_BLIT:
6383 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
6384 return FALSE;
6386 src_fixup = get_complex_fixup(src_format->color_fixup);
6387 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6389 TRACE("Checking support for fixup:\n");
6390 dump_color_fixup_desc(src_format->color_fixup);
6393 if (!is_identity_fixup(dst_format->color_fixup))
6395 TRACE("Destination fixups are not supported\n");
6396 return FALSE;
6399 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6401 TRACE("P8 fixup supported\n");
6402 return TRUE;
6405 /* We only support identity conversions. */
6406 if (is_identity_fixup(src_format->color_fixup))
6408 TRACE("[OK]\n");
6409 return TRUE;
6412 TRACE("[FAILED]\n");
6413 return FALSE;
6415 case WINED3D_BLIT_OP_COLOR_FILL:
6416 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
6417 return FALSE;
6419 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6421 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6422 return FALSE;
6424 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6426 TRACE("Color fill not supported\n");
6427 return FALSE;
6430 /* FIXME: We should reject color fills on formats with fixups,
6431 * but this would break P8 color fills for example. */
6433 return TRUE;
6435 case WINED3D_BLIT_OP_DEPTH_FILL:
6436 return TRUE;
6438 default:
6439 TRACE("Unsupported blit_op=%d\n", blit_op);
6440 return FALSE;
6444 /* Do not call while under the GL lock. */
6445 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6446 const RECT *dst_rect, const struct wined3d_color *color)
6448 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6449 struct wined3d_fb_state fb = {&dst_surface, NULL};
6451 return device_clear_render_targets(device, 1, &fb,
6452 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6455 /* Do not call while under the GL lock. */
6456 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6457 struct wined3d_surface *surface, const RECT *rect, float depth)
6459 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6460 struct wined3d_fb_state fb = {NULL, surface};
6462 return device_clear_render_targets(device, 0, &fb,
6463 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6466 const struct blit_shader ffp_blit = {
6467 ffp_blit_alloc,
6468 ffp_blit_free,
6469 ffp_blit_set,
6470 ffp_blit_unset,
6471 ffp_blit_supported,
6472 ffp_blit_color_fill,
6473 ffp_blit_depth_fill,
6476 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6478 return WINED3D_OK;
6481 /* Context activation is done by the caller. */
6482 static void cpu_blit_free(struct wined3d_device *device)
6486 /* Context activation is done by the caller. */
6487 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6489 return WINED3D_OK;
6492 /* Context activation is done by the caller. */
6493 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6497 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6498 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
6499 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
6501 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6503 return TRUE;
6506 return FALSE;
6509 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
6510 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
6511 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
6513 UINT row_block_count;
6514 const BYTE *src_row;
6515 BYTE *dst_row;
6516 UINT x, y;
6518 src_row = src_data;
6519 dst_row = dst_data;
6521 row_block_count = (update_w + format->block_width - 1) / format->block_width;
6523 if (!flags)
6525 for (y = 0; y < update_h; y += format->block_height)
6527 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
6528 src_row += src_pitch;
6529 dst_row += dst_pitch;
6532 return WINED3D_OK;
6535 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
6537 src_row += (((update_h / format->block_height) - 1) * src_pitch);
6539 switch (format->id)
6541 case WINED3DFMT_DXT1:
6542 for (y = 0; y < update_h; y += format->block_height)
6544 struct block
6546 WORD color[2];
6547 BYTE control_row[4];
6550 const struct block *s = (const struct block *)src_row;
6551 struct block *d = (struct block *)dst_row;
6553 for (x = 0; x < row_block_count; ++x)
6555 d[x].color[0] = s[x].color[0];
6556 d[x].color[1] = s[x].color[1];
6557 d[x].control_row[0] = s[x].control_row[3];
6558 d[x].control_row[1] = s[x].control_row[2];
6559 d[x].control_row[2] = s[x].control_row[1];
6560 d[x].control_row[3] = s[x].control_row[0];
6562 src_row -= src_pitch;
6563 dst_row += dst_pitch;
6565 return WINED3D_OK;
6567 case WINED3DFMT_DXT3:
6568 for (y = 0; y < update_h; y += format->block_height)
6570 struct block
6572 WORD alpha_row[4];
6573 WORD color[2];
6574 BYTE control_row[4];
6577 const struct block *s = (const struct block *)src_row;
6578 struct block *d = (struct block *)dst_row;
6580 for (x = 0; x < row_block_count; ++x)
6582 d[x].alpha_row[0] = s[x].alpha_row[3];
6583 d[x].alpha_row[1] = s[x].alpha_row[2];
6584 d[x].alpha_row[2] = s[x].alpha_row[1];
6585 d[x].alpha_row[3] = s[x].alpha_row[0];
6586 d[x].color[0] = s[x].color[0];
6587 d[x].color[1] = s[x].color[1];
6588 d[x].control_row[0] = s[x].control_row[3];
6589 d[x].control_row[1] = s[x].control_row[2];
6590 d[x].control_row[2] = s[x].control_row[1];
6591 d[x].control_row[3] = s[x].control_row[0];
6593 src_row -= src_pitch;
6594 dst_row += dst_pitch;
6596 return WINED3D_OK;
6598 default:
6599 FIXME("Compressed flip not implemented for format %s.\n",
6600 debug_d3dformat(format->id));
6601 return E_NOTIMPL;
6605 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
6606 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
6608 return E_NOTIMPL;
6611 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6612 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6613 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
6615 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6616 const struct wined3d_format *src_format, *dst_format;
6617 struct wined3d_surface *orig_src = src_surface;
6618 struct wined3d_mapped_rect dst_map, src_map;
6619 HRESULT hr = WINED3D_OK;
6620 const BYTE *sbuf;
6621 RECT xdst,xsrc;
6622 BYTE *dbuf;
6623 int x, y;
6625 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6626 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6627 flags, fx, debug_d3dtexturefiltertype(filter));
6629 xsrc = *src_rect;
6631 if (!src_surface)
6633 RECT full_rect;
6635 full_rect.left = 0;
6636 full_rect.top = 0;
6637 full_rect.right = dst_surface->resource.width;
6638 full_rect.bottom = dst_surface->resource.height;
6639 IntersectRect(&xdst, &full_rect, dst_rect);
6641 else
6643 BOOL clip_horiz, clip_vert;
6645 xdst = *dst_rect;
6646 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6647 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6649 if (clip_vert || clip_horiz)
6651 /* Now check if this is a special case or not... */
6652 if ((flags & WINEDDBLT_DDFX)
6653 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6654 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6656 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6657 return WINED3D_OK;
6660 if (clip_horiz)
6662 if (xdst.left < 0)
6664 xsrc.left -= xdst.left;
6665 xdst.left = 0;
6667 if (xdst.right > dst_surface->resource.width)
6669 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6670 xdst.right = (int)dst_surface->resource.width;
6674 if (clip_vert)
6676 if (xdst.top < 0)
6678 xsrc.top -= xdst.top;
6679 xdst.top = 0;
6681 if (xdst.bottom > dst_surface->resource.height)
6683 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6684 xdst.bottom = (int)dst_surface->resource.height;
6688 /* And check if after clipping something is still to be done... */
6689 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6690 || (xdst.left >= (int)dst_surface->resource.width)
6691 || (xdst.top >= (int)dst_surface->resource.height)
6692 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6693 || (xsrc.left >= (int)src_surface->resource.width)
6694 || (xsrc.top >= (int)src_surface->resource.height))
6696 TRACE("Nothing to be done after clipping.\n");
6697 return WINED3D_OK;
6702 if (src_surface == dst_surface)
6704 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6705 src_map = dst_map;
6706 src_format = dst_surface->resource.format;
6707 dst_format = src_format;
6709 else
6711 dst_format = dst_surface->resource.format;
6712 if (src_surface)
6714 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6716 src_surface = surface_convert_format(src_surface, dst_format->id);
6717 if (!src_surface)
6719 /* The conv function writes a FIXME */
6720 WARN("Cannot convert source surface format to dest format.\n");
6721 goto release;
6724 wined3d_surface_map(src_surface, &src_map, NULL, WINED3DLOCK_READONLY);
6725 src_format = src_surface->resource.format;
6727 else
6729 src_format = dst_format;
6731 if (dst_rect)
6732 wined3d_surface_map(dst_surface, &dst_map, &xdst, 0);
6733 else
6734 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6737 bpp = dst_surface->resource.format->byte_count;
6738 srcheight = xsrc.bottom - xsrc.top;
6739 srcwidth = xsrc.right - xsrc.left;
6740 dstheight = xdst.bottom - xdst.top;
6741 dstwidth = xdst.right - xdst.left;
6742 width = (xdst.right - xdst.left) * bpp;
6744 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
6746 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6748 if (src_surface == dst_surface)
6750 FIXME("Only plain blits supported on compressed surfaces.\n");
6751 hr = E_NOTIMPL;
6752 goto release;
6755 if (srcheight != dstheight || srcwidth != dstwidth)
6757 WARN("Stretching not supported on compressed surfaces.\n");
6758 hr = WINED3DERR_INVALIDCALL;
6759 goto release;
6762 if (srcwidth & (src_format->block_width - 1) || srcheight & (src_format->block_height - 1))
6764 WARN("Rectangle not block-aligned.\n");
6765 hr = WINED3DERR_INVALIDCALL;
6766 goto release;
6769 hr = surface_cpu_blt_compressed(src_map.data, dst_map.data,
6770 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
6771 src_format, flags, fx);
6772 goto release;
6775 if (dst_rect && src_surface != dst_surface)
6776 dbuf = dst_map.data;
6777 else
6778 dbuf = (BYTE *)dst_map.data + (xdst.top * dst_map.row_pitch) + (xdst.left * bpp);
6780 /* First, all the 'source-less' blits */
6781 if (flags & WINEDDBLT_COLORFILL)
6783 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
6784 flags &= ~WINEDDBLT_COLORFILL;
6787 if (flags & WINEDDBLT_DEPTHFILL)
6789 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6791 if (flags & WINEDDBLT_ROP)
6793 /* Catch some degenerate cases here. */
6794 switch (fx->dwROP)
6796 case BLACKNESS:
6797 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
6798 break;
6799 case 0xAA0029: /* No-op */
6800 break;
6801 case WHITENESS:
6802 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
6803 break;
6804 case SRCCOPY: /* Well, we do that below? */
6805 break;
6806 default:
6807 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6808 goto error;
6810 flags &= ~WINEDDBLT_ROP;
6812 if (flags & WINEDDBLT_DDROPS)
6814 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6816 /* Now the 'with source' blits. */
6817 if (src_surface)
6819 const BYTE *sbase;
6820 int sx, xinc, sy, yinc;
6822 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6823 goto release;
6825 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
6826 && (srcwidth != dstwidth || srcheight != dstheight))
6828 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6829 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6832 sbase = (BYTE *)src_map.data + (xsrc.top * src_map.row_pitch) + xsrc.left * bpp;
6833 xinc = (srcwidth << 16) / dstwidth;
6834 yinc = (srcheight << 16) / dstheight;
6836 if (!flags)
6838 /* No effects, we can cheat here. */
6839 if (dstwidth == srcwidth)
6841 if (dstheight == srcheight)
6843 /* No stretching in either direction. This needs to be as
6844 * fast as possible. */
6845 sbuf = sbase;
6847 /* Check for overlapping surfaces. */
6848 if (src_surface != dst_surface || xdst.top < xsrc.top
6849 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6851 /* No overlap, or dst above src, so copy from top downwards. */
6852 for (y = 0; y < dstheight; ++y)
6854 memcpy(dbuf, sbuf, width);
6855 sbuf += src_map.row_pitch;
6856 dbuf += dst_map.row_pitch;
6859 else if (xdst.top > xsrc.top)
6861 /* Copy from bottom upwards. */
6862 sbuf += src_map.row_pitch * dstheight;
6863 dbuf += dst_map.row_pitch * dstheight;
6864 for (y = 0; y < dstheight; ++y)
6866 sbuf -= src_map.row_pitch;
6867 dbuf -= dst_map.row_pitch;
6868 memcpy(dbuf, sbuf, width);
6871 else
6873 /* Src and dst overlapping on the same line, use memmove. */
6874 for (y = 0; y < dstheight; ++y)
6876 memmove(dbuf, sbuf, width);
6877 sbuf += src_map.row_pitch;
6878 dbuf += dst_map.row_pitch;
6882 else
6884 /* Stretching in y direction only. */
6885 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6887 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6888 memcpy(dbuf, sbuf, width);
6889 dbuf += dst_map.row_pitch;
6893 else
6895 /* Stretching in X direction. */
6896 int last_sy = -1;
6897 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6899 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6901 if ((sy >> 16) == (last_sy >> 16))
6903 /* This source row is the same as last source row -
6904 * Copy the already stretched row. */
6905 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
6907 else
6909 #define STRETCH_ROW(type) \
6910 do { \
6911 const type *s = (const type *)sbuf; \
6912 type *d = (type *)dbuf; \
6913 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6914 d[x] = s[sx >> 16]; \
6915 } while(0)
6917 switch(bpp)
6919 case 1:
6920 STRETCH_ROW(BYTE);
6921 break;
6922 case 2:
6923 STRETCH_ROW(WORD);
6924 break;
6925 case 4:
6926 STRETCH_ROW(DWORD);
6927 break;
6928 case 3:
6930 const BYTE *s;
6931 BYTE *d = dbuf;
6932 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6934 DWORD pixel;
6936 s = sbuf + 3 * (sx >> 16);
6937 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6938 d[0] = (pixel ) & 0xff;
6939 d[1] = (pixel >> 8) & 0xff;
6940 d[2] = (pixel >> 16) & 0xff;
6941 d += 3;
6943 break;
6945 default:
6946 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6947 hr = WINED3DERR_NOTAVAILABLE;
6948 goto error;
6950 #undef STRETCH_ROW
6952 dbuf += dst_map.row_pitch;
6953 last_sy = sy;
6957 else
6959 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
6960 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6961 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6962 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6964 /* The color keying flags are checked for correctness in ddraw */
6965 if (flags & WINEDDBLT_KEYSRC)
6967 keylow = src_surface->src_blt_color_key.color_space_low_value;
6968 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
6970 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6972 keylow = fx->ddckSrcColorkey.color_space_low_value;
6973 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
6976 if (flags & WINEDDBLT_KEYDEST)
6978 /* Destination color keys are taken from the source surface! */
6979 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
6980 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
6982 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6984 destkeylow = fx->ddckDestColorkey.color_space_low_value;
6985 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
6988 if (bpp == 1)
6990 keymask = 0xff;
6992 else
6994 keymask = src_format->red_mask
6995 | src_format->green_mask
6996 | src_format->blue_mask;
6998 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
7001 if (flags & WINEDDBLT_DDFX)
7003 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
7004 LONG tmpxy;
7005 dTopLeft = dbuf;
7006 dTopRight = dbuf + ((dstwidth - 1) * bpp);
7007 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
7008 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
7010 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
7012 /* I don't think we need to do anything about this flag */
7013 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
7015 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
7017 tmp = dTopRight;
7018 dTopRight = dTopLeft;
7019 dTopLeft = tmp;
7020 tmp = dBottomRight;
7021 dBottomRight = dBottomLeft;
7022 dBottomLeft = tmp;
7023 dstxinc = dstxinc * -1;
7025 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
7027 tmp = dTopLeft;
7028 dTopLeft = dBottomLeft;
7029 dBottomLeft = tmp;
7030 tmp = dTopRight;
7031 dTopRight = dBottomRight;
7032 dBottomRight = tmp;
7033 dstyinc = dstyinc * -1;
7035 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
7037 /* I don't think we need to do anything about this flag */
7038 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
7040 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
7042 tmp = dBottomRight;
7043 dBottomRight = dTopLeft;
7044 dTopLeft = tmp;
7045 tmp = dBottomLeft;
7046 dBottomLeft = dTopRight;
7047 dTopRight = tmp;
7048 dstxinc = dstxinc * -1;
7049 dstyinc = dstyinc * -1;
7051 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
7053 tmp = dTopLeft;
7054 dTopLeft = dBottomLeft;
7055 dBottomLeft = dBottomRight;
7056 dBottomRight = dTopRight;
7057 dTopRight = tmp;
7058 tmpxy = dstxinc;
7059 dstxinc = dstyinc;
7060 dstyinc = tmpxy;
7061 dstxinc = dstxinc * -1;
7063 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
7065 tmp = dTopLeft;
7066 dTopLeft = dTopRight;
7067 dTopRight = dBottomRight;
7068 dBottomRight = dBottomLeft;
7069 dBottomLeft = tmp;
7070 tmpxy = dstxinc;
7071 dstxinc = dstyinc;
7072 dstyinc = tmpxy;
7073 dstyinc = dstyinc * -1;
7075 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
7077 /* I don't think we need to do anything about this flag */
7078 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
7080 dbuf = dTopLeft;
7081 flags &= ~(WINEDDBLT_DDFX);
7084 #define COPY_COLORKEY_FX(type) \
7085 do { \
7086 const type *s; \
7087 type *d = (type *)dbuf, *dx, tmp; \
7088 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
7090 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
7091 dx = d; \
7092 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
7094 tmp = s[sx >> 16]; \
7095 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
7096 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
7098 dx[0] = tmp; \
7100 dx = (type *)(((BYTE *)dx) + dstxinc); \
7102 d = (type *)(((BYTE *)d) + dstyinc); \
7104 } while(0)
7106 switch (bpp)
7108 case 1:
7109 COPY_COLORKEY_FX(BYTE);
7110 break;
7111 case 2:
7112 COPY_COLORKEY_FX(WORD);
7113 break;
7114 case 4:
7115 COPY_COLORKEY_FX(DWORD);
7116 break;
7117 case 3:
7119 const BYTE *s;
7120 BYTE *d = dbuf, *dx;
7121 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
7123 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
7124 dx = d;
7125 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
7127 DWORD pixel, dpixel = 0;
7128 s = sbuf + 3 * (sx>>16);
7129 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
7130 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
7131 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
7132 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
7134 dx[0] = (pixel ) & 0xff;
7135 dx[1] = (pixel >> 8) & 0xff;
7136 dx[2] = (pixel >> 16) & 0xff;
7138 dx += dstxinc;
7140 d += dstyinc;
7142 break;
7144 default:
7145 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7146 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7147 hr = WINED3DERR_NOTAVAILABLE;
7148 goto error;
7149 #undef COPY_COLORKEY_FX
7154 error:
7155 if (flags && FIXME_ON(d3d_surface))
7157 FIXME("\tUnsupported flags: %#x.\n", flags);
7160 release:
7161 wined3d_surface_unmap(dst_surface);
7162 if (src_surface && src_surface != dst_surface)
7163 wined3d_surface_unmap(src_surface);
7164 /* Release the converted surface, if any. */
7165 if (src_surface && src_surface != orig_src)
7166 wined3d_surface_decref(src_surface);
7168 return hr;
7171 /* Do not call while under the GL lock. */
7172 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7173 const RECT *dst_rect, const struct wined3d_color *color)
7175 static const RECT src_rect;
7176 WINEDDBLTFX BltFx;
7178 memset(&BltFx, 0, sizeof(BltFx));
7179 BltFx.dwSize = sizeof(BltFx);
7180 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7181 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7182 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
7185 /* Do not call while under the GL lock. */
7186 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7187 struct wined3d_surface *surface, const RECT *rect, float depth)
7189 FIXME("Depth filling not implemented by cpu_blit.\n");
7190 return WINED3DERR_INVALIDCALL;
7193 const struct blit_shader cpu_blit = {
7194 cpu_blit_alloc,
7195 cpu_blit_free,
7196 cpu_blit_set,
7197 cpu_blit_unset,
7198 cpu_blit_supported,
7199 cpu_blit_color_fill,
7200 cpu_blit_depth_fill,
7203 static HRESULT surface_init(struct wined3d_surface *surface, enum wined3d_surface_type surface_type, UINT alignment,
7204 UINT width, UINT height, UINT level, enum wined3d_multisample_type multisample_type,
7205 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7206 enum wined3d_pool pool, DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops)
7208 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7209 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7210 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
7211 unsigned int resource_size;
7212 HRESULT hr;
7214 if (multisample_quality > 0)
7216 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7217 multisample_quality = 0;
7220 /* Quick lockable sanity check.
7221 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7222 * this function is too deep to need to care about things like this.
7223 * Levels need to be checked too, since they all affect what can be done. */
7224 switch (pool)
7226 case WINED3D_POOL_SCRATCH:
7227 if (!lockable)
7229 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7230 "which are mutually exclusive, setting lockable to TRUE.\n");
7231 lockable = TRUE;
7233 break;
7235 case WINED3D_POOL_SYSTEM_MEM:
7236 if (!lockable)
7237 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7238 break;
7240 case WINED3D_POOL_MANAGED:
7241 if (usage & WINED3DUSAGE_DYNAMIC)
7242 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7243 break;
7245 case WINED3D_POOL_DEFAULT:
7246 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7247 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7248 break;
7250 default:
7251 FIXME("Unknown pool %#x.\n", pool);
7252 break;
7255 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3D_POOL_DEFAULT)
7256 FIXME("Trying to create a render target that isn't in the default pool.\n");
7258 /* FIXME: Check that the format is supported by the device. */
7260 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7261 if (!resource_size)
7262 return WINED3DERR_INVALIDCALL;
7264 surface->surface_type = surface_type;
7266 switch (surface_type)
7268 case WINED3D_SURFACE_TYPE_OPENGL:
7269 surface->surface_ops = &surface_ops;
7270 break;
7272 case WINED3D_SURFACE_TYPE_GDI:
7273 surface->surface_ops = &gdi_surface_ops;
7274 break;
7276 default:
7277 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7278 return WINED3DERR_INVALIDCALL;
7281 hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
7282 multisample_type, multisample_quality, usage, pool, width, height, 1,
7283 resource_size, parent, parent_ops, &surface_resource_ops);
7284 if (FAILED(hr))
7286 WARN("Failed to initialize resource, returning %#x.\n", hr);
7287 return hr;
7290 /* "Standalone" surface. */
7291 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7293 surface->texture_level = level;
7294 list_init(&surface->overlays);
7296 /* Flags */
7297 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7298 if (flags & WINED3D_SURFACE_DISCARD)
7299 surface->flags |= SFLAG_DISCARD;
7300 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
7301 surface->flags |= SFLAG_PIN_SYSMEM;
7302 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7303 surface->flags |= SFLAG_LOCKABLE;
7304 /* I'm not sure if this qualifies as a hack or as an optimization. It
7305 * seems reasonable to assume that lockable render targets will get
7306 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7307 * creation. However, the other reason we want to do this is that several
7308 * ddraw applications access surface memory while the surface isn't
7309 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7310 * future locks prevents these from crashing. */
7311 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7312 surface->flags |= SFLAG_DYNLOCK;
7314 /* Mark the texture as dirty so that it gets loaded first time around. */
7315 surface_add_dirty_rect(surface, NULL);
7316 list_init(&surface->renderbuffers);
7318 TRACE("surface %p, memory %p, size %u\n",
7319 surface, surface->resource.allocatedMemory, surface->resource.size);
7321 /* Call the private setup routine */
7322 hr = surface->surface_ops->surface_private_setup(surface);
7323 if (FAILED(hr))
7325 ERR("Private setup failed, returning %#x\n", hr);
7326 surface_cleanup(surface);
7327 return hr;
7330 /* Similar to lockable rendertargets above, creating the DIB section
7331 * during surface initialization prevents the sysmem pointer from changing
7332 * after a wined3d_surface_getdc() call. */
7333 if ((usage & WINED3DUSAGE_OWNDC) && !surface->hDC
7334 && SUCCEEDED(surface_create_dib_section(surface)))
7336 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
7337 surface->resource.heapMemory = NULL;
7338 surface->resource.allocatedMemory = surface->dib.bitmap_data;
7341 return hr;
7344 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7345 enum wined3d_format_id format_id, UINT level, DWORD usage, enum wined3d_pool pool,
7346 enum wined3d_multisample_type multisample_type, DWORD multisample_quality,
7347 enum wined3d_surface_type surface_type, DWORD flags, void *parent,
7348 const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7350 struct wined3d_surface *object;
7351 HRESULT hr;
7353 TRACE("device %p, width %u, height %u, format %s, level %u\n",
7354 device, width, height, debug_d3dformat(format_id), level);
7355 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7356 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7357 TRACE("surface_type %#x, flags %#x, parent %p, parent_ops %p.\n", surface_type, flags, parent, parent_ops);
7359 if (surface_type == WINED3D_SURFACE_TYPE_OPENGL && !device->adapter)
7361 ERR("OpenGL surfaces are not available without OpenGL.\n");
7362 return WINED3DERR_NOTAVAILABLE;
7365 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7366 if (!object)
7368 ERR("Failed to allocate surface memory.\n");
7369 return WINED3DERR_OUTOFVIDEOMEMORY;
7372 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level,
7373 multisample_type, multisample_quality, device, usage, format_id, pool, flags, parent, parent_ops);
7374 if (FAILED(hr))
7376 WARN("Failed to initialize surface, returning %#x.\n", hr);
7377 HeapFree(GetProcessHeap(), 0, object);
7378 return hr;
7381 TRACE("Created surface %p.\n", object);
7382 *surface = object;
7384 return hr;