wined3d: Handle client storage in surface_init_sysmem().
[wine/multimedia.git] / dlls / wined3d / surface.c
blob48d271f561554cdad1d80dfa3c64a9456363c648
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2011 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d);
36 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
37 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
38 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter);
39 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
40 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *fx,
41 WINED3DTEXTUREFILTERTYPE filter);
43 static void surface_cleanup(struct wined3d_surface *surface)
45 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->texture_name || (surface->flags & SFLAG_PBO)
50 || surface->rb_multisample || surface->rb_resolved
51 || !list_empty(&surface->renderbuffers))
53 struct wined3d_renderbuffer_entry *entry, *entry2;
54 const struct wined3d_gl_info *gl_info;
55 struct wined3d_context *context;
57 context = context_acquire(surface->resource.device, NULL);
58 gl_info = context->gl_info;
60 ENTER_GL();
62 if (surface->texture_name)
64 TRACE("Deleting texture %u.\n", surface->texture_name);
65 glDeleteTextures(1, &surface->texture_name);
68 if (surface->flags & SFLAG_PBO)
70 TRACE("Deleting PBO %u.\n", surface->pbo);
71 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
74 if (surface->rb_multisample)
76 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
77 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
80 if (surface->rb_resolved)
82 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
83 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
86 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
88 TRACE("Deleting renderbuffer %u.\n", entry->id);
89 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
90 HeapFree(GetProcessHeap(), 0, entry);
93 LEAVE_GL();
95 context_release(context);
98 if (surface->flags & SFLAG_DIBSECTION)
100 DeleteDC(surface->hDC);
101 DeleteObject(surface->dib.DIBsection);
102 surface->dib.bitmap_data = NULL;
103 surface->resource.allocatedMemory = NULL;
106 if (surface->flags & SFLAG_USERPTR)
107 wined3d_surface_set_mem(surface, NULL);
108 if (surface->overlay_dest)
109 list_remove(&surface->overlay_entry);
111 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
113 list_remove(&overlay->overlay_entry);
114 overlay->overlay_dest = NULL;
117 resource_cleanup(&surface->resource);
120 void surface_update_draw_binding(struct wined3d_surface *surface)
122 if (!surface_is_offscreen(surface) || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
123 surface->draw_binding = SFLAG_INDRAWABLE;
124 else if (surface->resource.multisample_type)
125 surface->draw_binding = SFLAG_INRB_MULTISAMPLE;
126 else
127 surface->draw_binding = SFLAG_INTEXTURE;
130 void surface_set_container(struct wined3d_surface *surface, enum wined3d_container_type type, void *container)
132 TRACE("surface %p, container %p.\n", surface, container);
134 if (!container && type != WINED3D_CONTAINER_NONE)
135 ERR("Setting NULL container of type %#x.\n", type);
137 if (type == WINED3D_CONTAINER_SWAPCHAIN)
139 surface->get_drawable_size = get_drawable_size_swapchain;
141 else
143 switch (wined3d_settings.offscreen_rendering_mode)
145 case ORM_FBO:
146 surface->get_drawable_size = get_drawable_size_fbo;
147 break;
149 case ORM_BACKBUFFER:
150 surface->get_drawable_size = get_drawable_size_backbuffer;
151 break;
153 default:
154 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
155 return;
159 surface->container.type = type;
160 surface->container.u.base = container;
161 surface_update_draw_binding(surface);
164 struct blt_info
166 GLenum binding;
167 GLenum bind_target;
168 enum tex_types tex_type;
169 GLfloat coords[4][3];
172 struct float_rect
174 float l;
175 float t;
176 float r;
177 float b;
180 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
182 f->l = ((r->left * 2.0f) / w) - 1.0f;
183 f->t = ((r->top * 2.0f) / h) - 1.0f;
184 f->r = ((r->right * 2.0f) / w) - 1.0f;
185 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
188 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
190 GLfloat (*coords)[3] = info->coords;
191 struct float_rect f;
193 switch (target)
195 default:
196 FIXME("Unsupported texture target %#x\n", target);
197 /* Fall back to GL_TEXTURE_2D */
198 case GL_TEXTURE_2D:
199 info->binding = GL_TEXTURE_BINDING_2D;
200 info->bind_target = GL_TEXTURE_2D;
201 info->tex_type = tex_2d;
202 coords[0][0] = (float)rect->left / w;
203 coords[0][1] = (float)rect->top / h;
204 coords[0][2] = 0.0f;
206 coords[1][0] = (float)rect->right / w;
207 coords[1][1] = (float)rect->top / h;
208 coords[1][2] = 0.0f;
210 coords[2][0] = (float)rect->left / w;
211 coords[2][1] = (float)rect->bottom / h;
212 coords[2][2] = 0.0f;
214 coords[3][0] = (float)rect->right / w;
215 coords[3][1] = (float)rect->bottom / h;
216 coords[3][2] = 0.0f;
217 break;
219 case GL_TEXTURE_RECTANGLE_ARB:
220 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
221 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
222 info->tex_type = tex_rect;
223 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
224 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
225 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
226 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
227 break;
229 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
230 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
231 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
232 info->tex_type = tex_cube;
233 cube_coords_float(rect, w, h, &f);
235 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
236 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
237 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
238 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
239 break;
241 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
242 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
243 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
244 info->tex_type = tex_cube;
245 cube_coords_float(rect, w, h, &f);
247 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
248 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
249 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
250 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
251 break;
253 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
254 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
255 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
256 info->tex_type = tex_cube;
257 cube_coords_float(rect, w, h, &f);
259 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
260 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
261 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
262 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
263 break;
265 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
266 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
267 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
268 info->tex_type = tex_cube;
269 cube_coords_float(rect, w, h, &f);
271 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
272 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
273 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
274 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
275 break;
277 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
278 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
279 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
280 info->tex_type = tex_cube;
281 cube_coords_float(rect, w, h, &f);
283 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
284 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
285 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
286 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
287 break;
289 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
290 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
291 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
292 info->tex_type = tex_cube;
293 cube_coords_float(rect, w, h, &f);
295 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
296 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
297 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
298 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
299 break;
303 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
305 if (rect_in)
306 *rect_out = *rect_in;
307 else
309 rect_out->left = 0;
310 rect_out->top = 0;
311 rect_out->right = surface->resource.width;
312 rect_out->bottom = surface->resource.height;
316 /* GL locking and context activation is done by the caller */
317 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
318 const RECT *src_rect, const RECT *dst_rect, WINED3DTEXTUREFILTERTYPE Filter)
320 struct blt_info info;
322 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
324 glEnable(info.bind_target);
325 checkGLcall("glEnable(bind_target)");
327 context_bind_texture(context, info.bind_target, src_surface->texture_name);
329 /* Filtering for StretchRect */
330 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
331 wined3d_gl_mag_filter(magLookup, Filter));
332 checkGLcall("glTexParameteri");
333 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
334 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
335 checkGLcall("glTexParameteri");
336 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
337 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
338 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
339 glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
340 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
341 checkGLcall("glTexEnvi");
343 /* Draw a quad */
344 glBegin(GL_TRIANGLE_STRIP);
345 glTexCoord3fv(info.coords[0]);
346 glVertex2i(dst_rect->left, dst_rect->top);
348 glTexCoord3fv(info.coords[1]);
349 glVertex2i(dst_rect->right, dst_rect->top);
351 glTexCoord3fv(info.coords[2]);
352 glVertex2i(dst_rect->left, dst_rect->bottom);
354 glTexCoord3fv(info.coords[3]);
355 glVertex2i(dst_rect->right, dst_rect->bottom);
356 glEnd();
358 /* Unbind the texture */
359 context_bind_texture(context, info.bind_target, 0);
361 /* We changed the filtering settings on the texture. Inform the
362 * container about this to get the filters reset properly next draw. */
363 if (src_surface->container.type == WINED3D_CONTAINER_TEXTURE)
365 struct wined3d_texture *texture = src_surface->container.u.texture;
366 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
367 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
368 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
369 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
373 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
375 const struct wined3d_format *format = surface->resource.format;
376 SYSTEM_INFO sysInfo;
377 BITMAPINFO *b_info;
378 int extraline = 0;
379 DWORD *masks;
380 UINT usage;
381 HDC dc;
383 TRACE("surface %p.\n", surface);
385 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
387 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
388 return WINED3DERR_INVALIDCALL;
391 switch (format->byte_count)
393 case 2:
394 case 4:
395 /* Allocate extra space to store the RGB bit masks. */
396 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
397 break;
399 case 3:
400 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
401 break;
403 default:
404 /* Allocate extra space for a palette. */
405 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
406 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
407 break;
410 if (!b_info)
411 return E_OUTOFMEMORY;
413 /* Some applications access the surface in via DWORDs, and do not take
414 * the necessary care at the end of the surface. So we need at least
415 * 4 extra bytes at the end of the surface. Check against the page size,
416 * if the last page used for the surface has at least 4 spare bytes we're
417 * safe, otherwise add an extra line to the DIB section. */
418 GetSystemInfo(&sysInfo);
419 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
421 extraline = 1;
422 TRACE("Adding an extra line to the DIB section.\n");
425 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
426 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
427 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
428 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
429 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
430 * wined3d_surface_get_pitch(surface);
431 b_info->bmiHeader.biPlanes = 1;
432 b_info->bmiHeader.biBitCount = format->byte_count * 8;
434 b_info->bmiHeader.biXPelsPerMeter = 0;
435 b_info->bmiHeader.biYPelsPerMeter = 0;
436 b_info->bmiHeader.biClrUsed = 0;
437 b_info->bmiHeader.biClrImportant = 0;
439 /* Get the bit masks */
440 masks = (DWORD *)b_info->bmiColors;
441 switch (surface->resource.format->id)
443 case WINED3DFMT_B8G8R8_UNORM:
444 usage = DIB_RGB_COLORS;
445 b_info->bmiHeader.biCompression = BI_RGB;
446 break;
448 case WINED3DFMT_B5G5R5X1_UNORM:
449 case WINED3DFMT_B5G5R5A1_UNORM:
450 case WINED3DFMT_B4G4R4A4_UNORM:
451 case WINED3DFMT_B4G4R4X4_UNORM:
452 case WINED3DFMT_B2G3R3_UNORM:
453 case WINED3DFMT_B2G3R3A8_UNORM:
454 case WINED3DFMT_R10G10B10A2_UNORM:
455 case WINED3DFMT_R8G8B8A8_UNORM:
456 case WINED3DFMT_R8G8B8X8_UNORM:
457 case WINED3DFMT_B10G10R10A2_UNORM:
458 case WINED3DFMT_B5G6R5_UNORM:
459 case WINED3DFMT_R16G16B16A16_UNORM:
460 usage = 0;
461 b_info->bmiHeader.biCompression = BI_BITFIELDS;
462 masks[0] = format->red_mask;
463 masks[1] = format->green_mask;
464 masks[2] = format->blue_mask;
465 break;
467 default:
468 /* Don't know palette */
469 b_info->bmiHeader.biCompression = BI_RGB;
470 usage = 0;
471 break;
474 if (!(dc = GetDC(0)))
476 HeapFree(GetProcessHeap(), 0, b_info);
477 return HRESULT_FROM_WIN32(GetLastError());
480 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
481 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
482 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
483 surface->dib.DIBsection = CreateDIBSection(dc, b_info, usage, &surface->dib.bitmap_data, 0, 0);
484 ReleaseDC(0, dc);
486 if (!surface->dib.DIBsection)
488 ERR("Failed to create DIB section.\n");
489 HeapFree(GetProcessHeap(), 0, b_info);
490 return HRESULT_FROM_WIN32(GetLastError());
493 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
494 /* Copy the existing surface to the dib section. */
495 if (surface->resource.allocatedMemory)
497 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
498 surface->resource.height * wined3d_surface_get_pitch(surface));
500 else
502 /* This is to make maps read the GL texture although memory is allocated. */
503 surface->flags &= ~SFLAG_INSYSMEM;
505 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
507 HeapFree(GetProcessHeap(), 0, b_info);
509 /* Now allocate a DC. */
510 surface->hDC = CreateCompatibleDC(0);
511 SelectObject(surface->hDC, surface->dib.DIBsection);
512 TRACE("Using wined3d palette %p.\n", surface->palette);
513 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
515 surface->flags |= SFLAG_DIBSECTION;
517 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
518 surface->resource.heapMemory = NULL;
520 return WINED3D_OK;
523 static BOOL surface_need_pbo(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
525 if (surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
526 return FALSE;
527 if (!(surface->flags & SFLAG_DYNLOCK))
528 return FALSE;
529 if (surface->flags & (SFLAG_CONVERTED | SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM))
530 return FALSE;
531 if (!gl_info->supported[ARB_PIXEL_BUFFER_OBJECT])
532 return FALSE;
534 return TRUE;
537 static void surface_load_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
539 struct wined3d_context *context;
540 GLenum error;
542 context = context_acquire(surface->resource.device, NULL);
543 ENTER_GL();
545 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
546 error = glGetError();
547 if (!surface->pbo || error != GL_NO_ERROR)
548 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
550 TRACE("Binding PBO %u.\n", surface->pbo);
552 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
553 checkGLcall("glBindBufferARB");
555 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
556 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
557 checkGLcall("glBufferDataARB");
559 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
560 checkGLcall("glBindBufferARB");
562 /* We don't need the system memory anymore and we can't even use it for PBOs. */
563 if (!(surface->flags & SFLAG_CLIENT))
565 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
566 surface->resource.heapMemory = NULL;
568 surface->resource.allocatedMemory = NULL;
569 surface->flags |= SFLAG_PBO;
570 LEAVE_GL();
571 context_release(context);
574 static void surface_prepare_system_memory(struct wined3d_surface *surface)
576 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
578 TRACE("surface %p.\n", surface);
580 if (!(surface->flags & SFLAG_PBO) && surface_need_pbo(surface, gl_info))
581 surface_load_pbo(surface, gl_info);
582 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
584 /* Whatever surface we have, make sure that there is memory allocated
585 * for the downloaded copy, or a PBO to map. */
586 if (!surface->resource.heapMemory)
587 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
589 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
590 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
592 if (surface->flags & SFLAG_INSYSMEM)
593 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
597 static void surface_evict_sysmem(struct wined3d_surface *surface)
599 if (surface->flags & SFLAG_DONOTFREE)
600 return;
602 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
603 surface->resource.allocatedMemory = NULL;
604 surface->resource.heapMemory = NULL;
605 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
608 /* Context activation is done by the caller. */
609 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
610 struct wined3d_context *context, BOOL srgb)
612 struct wined3d_device *device = surface->resource.device;
613 DWORD active_sampler;
615 /* We don't need a specific texture unit, but after binding the texture
616 * the current unit is dirty. Read the unit back instead of switching to
617 * 0, this avoids messing around with the state manager's GL states. The
618 * current texture unit should always be a valid one.
620 * To be more specific, this is tricky because we can implicitly be
621 * called from sampler() in state.c. This means we can't touch anything
622 * other than whatever happens to be the currently active texture, or we
623 * would risk marking already applied sampler states dirty again. */
624 active_sampler = device->rev_tex_unit_map[context->active_texture];
626 if (active_sampler != WINED3D_UNMAPPED_STAGE)
627 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
628 surface_bind(surface, context, srgb);
631 static void surface_force_reload(struct wined3d_surface *surface)
633 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
636 static void surface_release_client_storage(struct wined3d_surface *surface)
638 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
640 ENTER_GL();
641 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
642 if (surface->texture_name)
644 surface_bind_and_dirtify(surface, context, FALSE);
645 glTexImage2D(surface->texture_target, surface->texture_level,
646 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
648 if (surface->texture_name_srgb)
650 surface_bind_and_dirtify(surface, context, TRUE);
651 glTexImage2D(surface->texture_target, surface->texture_level,
652 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
654 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
655 LEAVE_GL();
657 context_release(context);
659 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
660 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
661 surface_force_reload(surface);
664 static HRESULT surface_private_setup(struct wined3d_surface *surface)
666 /* TODO: Check against the maximum texture sizes supported by the video card. */
667 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
668 unsigned int pow2Width, pow2Height;
670 TRACE("surface %p.\n", surface);
672 surface->texture_name = 0;
673 surface->texture_target = GL_TEXTURE_2D;
675 /* Non-power2 support */
676 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
678 pow2Width = surface->resource.width;
679 pow2Height = surface->resource.height;
681 else
683 /* Find the nearest pow2 match */
684 pow2Width = pow2Height = 1;
685 while (pow2Width < surface->resource.width)
686 pow2Width <<= 1;
687 while (pow2Height < surface->resource.height)
688 pow2Height <<= 1;
690 surface->pow2Width = pow2Width;
691 surface->pow2Height = pow2Height;
693 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
695 /* TODO: Add support for non power two compressed textures. */
696 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
698 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
699 surface, surface->resource.width, surface->resource.height);
700 return WINED3DERR_NOTAVAILABLE;
704 if (pow2Width != surface->resource.width
705 || pow2Height != surface->resource.height)
707 surface->flags |= SFLAG_NONPOW2;
710 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
711 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
713 /* One of three options:
714 * 1: Do the same as we do with NPOT and scale the texture, (any
715 * texture ops would require the texture to be scaled which is
716 * potentially slow)
717 * 2: Set the texture to the maximum size (bad idea).
718 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
719 * 4: Create the surface, but allow it to be used only for DirectDraw
720 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
721 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
722 * the render target. */
723 if (surface->resource.pool == WINED3DPOOL_DEFAULT || surface->resource.pool == WINED3DPOOL_MANAGED)
725 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
726 return WINED3DERR_NOTAVAILABLE;
729 /* We should never use this surface in combination with OpenGL! */
730 TRACE("Creating an oversized surface: %ux%u.\n",
731 surface->pow2Width, surface->pow2Height);
733 else
735 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
736 * and EXT_PALETTED_TEXTURE is used in combination with texture
737 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
738 * EXT_PALETTED_TEXTURE doesn't work in combination with
739 * ARB_TEXTURE_RECTANGLE. */
740 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
741 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
742 && gl_info->supported[EXT_PALETTED_TEXTURE]
743 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
745 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
746 surface->pow2Width = surface->resource.width;
747 surface->pow2Height = surface->resource.height;
748 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
752 switch (wined3d_settings.offscreen_rendering_mode)
754 case ORM_FBO:
755 surface->get_drawable_size = get_drawable_size_fbo;
756 break;
758 case ORM_BACKBUFFER:
759 surface->get_drawable_size = get_drawable_size_backbuffer;
760 break;
762 default:
763 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
764 return WINED3DERR_INVALIDCALL;
767 surface->flags |= SFLAG_INSYSMEM;
769 return WINED3D_OK;
772 static void surface_realize_palette(struct wined3d_surface *surface)
774 struct wined3d_palette *palette = surface->palette;
776 TRACE("surface %p.\n", surface);
778 if (!palette) return;
780 if (surface->resource.format->id == WINED3DFMT_P8_UINT
781 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
783 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
785 /* Make sure the texture is up to date. This call doesn't do
786 * anything if the texture is already up to date. */
787 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
789 /* We want to force a palette refresh, so mark the drawable as not being up to date */
790 if (!surface_is_offscreen(surface))
791 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
793 else
795 if (!(surface->flags & SFLAG_INSYSMEM))
797 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
798 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
800 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
804 if (surface->flags & SFLAG_DIBSECTION)
806 RGBQUAD col[256];
807 unsigned int i;
809 TRACE("Updating the DC's palette.\n");
811 for (i = 0; i < 256; ++i)
813 col[i].rgbRed = palette->palents[i].peRed;
814 col[i].rgbGreen = palette->palents[i].peGreen;
815 col[i].rgbBlue = palette->palents[i].peBlue;
816 col[i].rgbReserved = 0;
818 SetDIBColorTable(surface->hDC, 0, 256, col);
821 /* Propagate the changes to the drawable when we have a palette. */
822 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
823 surface_load_location(surface, surface->draw_binding, NULL);
826 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
828 HRESULT hr;
830 /* If there's no destination surface there is nothing to do. */
831 if (!surface->overlay_dest)
832 return WINED3D_OK;
834 /* Blt calls ModifyLocation on the dest surface, which in turn calls
835 * DrawOverlay to update the overlay. Prevent an endless recursion. */
836 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
837 return WINED3D_OK;
839 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
840 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
841 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3DTEXF_LINEAR);
842 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
844 return hr;
847 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
849 struct wined3d_device *device = surface->resource.device;
850 const RECT *pass_rect = rect;
852 TRACE("surface %p, rect %s, flags %#x.\n",
853 surface, wine_dbgstr_rect(rect), flags);
855 if (flags & WINED3DLOCK_DISCARD)
857 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
858 surface_prepare_system_memory(surface);
859 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
861 else
863 /* surface_load_location() does not check if the rectangle specifies
864 * the full surface. Most callers don't need that, so do it here. */
865 if (rect && !rect->top && !rect->left
866 && rect->right == surface->resource.width
867 && rect->bottom == surface->resource.height)
868 pass_rect = NULL;
869 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
872 if (surface->flags & SFLAG_PBO)
874 const struct wined3d_gl_info *gl_info;
875 struct wined3d_context *context;
877 context = context_acquire(device, NULL);
878 gl_info = context->gl_info;
880 ENTER_GL();
881 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
882 checkGLcall("glBindBufferARB");
884 /* This shouldn't happen but could occur if some other function
885 * didn't handle the PBO properly. */
886 if (surface->resource.allocatedMemory)
887 ERR("The surface already has PBO memory allocated.\n");
889 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
890 checkGLcall("glMapBufferARB");
892 /* Make sure the PBO isn't set anymore in order not to break non-PBO
893 * calls. */
894 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
895 checkGLcall("glBindBufferARB");
897 LEAVE_GL();
898 context_release(context);
901 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
903 if (!rect)
904 surface_add_dirty_rect(surface, NULL);
905 else
907 struct wined3d_box b;
909 b.left = rect->left;
910 b.top = rect->top;
911 b.right = rect->right;
912 b.bottom = rect->bottom;
913 b.front = 0;
914 b.back = 1;
915 surface_add_dirty_rect(surface, &b);
920 static void surface_unmap(struct wined3d_surface *surface)
922 struct wined3d_device *device = surface->resource.device;
923 BOOL fullsurface;
925 TRACE("surface %p.\n", surface);
927 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
929 if (surface->flags & SFLAG_PBO)
931 const struct wined3d_gl_info *gl_info;
932 struct wined3d_context *context;
934 TRACE("Freeing PBO memory.\n");
936 context = context_acquire(device, NULL);
937 gl_info = context->gl_info;
939 ENTER_GL();
940 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
941 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
942 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
943 checkGLcall("glUnmapBufferARB");
944 LEAVE_GL();
945 context_release(context);
947 surface->resource.allocatedMemory = NULL;
950 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
952 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
954 TRACE("Not dirtified, nothing to do.\n");
955 goto done;
958 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
959 || (device->fb.render_targets && surface == device->fb.render_targets[0]))
961 if (!surface->dirtyRect.left && !surface->dirtyRect.top
962 && surface->dirtyRect.right == surface->resource.width
963 && surface->dirtyRect.bottom == surface->resource.height)
965 fullsurface = TRUE;
967 else
969 /* TODO: Proper partial rectangle tracking. */
970 fullsurface = FALSE;
971 surface->flags |= SFLAG_INSYSMEM;
974 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
976 /* Partial rectangle tracking is not commonly implemented, it is only
977 * done for render targets. INSYSMEM was set before to tell
978 * surface_load_location() where to read the rectangle from.
979 * Indrawable is set because all modifications from the partial
980 * sysmem copy are written back to the drawable, thus the surface is
981 * merged again in the drawable. The sysmem copy is not fully up to
982 * date because only a subrectangle was read in Map(). */
983 if (!fullsurface)
985 surface_modify_location(surface, surface->draw_binding, TRUE);
986 surface_evict_sysmem(surface);
989 surface->dirtyRect.left = surface->resource.width;
990 surface->dirtyRect.top = surface->resource.height;
991 surface->dirtyRect.right = 0;
992 surface->dirtyRect.bottom = 0;
994 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
996 FIXME("Depth / stencil buffer locking is not implemented.\n");
999 done:
1000 /* Overlays have to be redrawn manually after changes with the GL implementation */
1001 if (surface->overlay_dest)
1002 surface_draw_overlay(surface);
1005 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1007 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1008 return FALSE;
1009 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1010 return FALSE;
1011 return TRUE;
1014 static void wined3d_surface_depth_blt_fbo(const struct wined3d_device *device, struct wined3d_surface *src_surface,
1015 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1017 const struct wined3d_gl_info *gl_info;
1018 struct wined3d_context *context;
1019 DWORD src_mask, dst_mask;
1020 GLbitfield gl_mask;
1022 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1023 device, src_surface, wine_dbgstr_rect(src_rect),
1024 dst_surface, wine_dbgstr_rect(dst_rect));
1026 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1027 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1029 if (src_mask != dst_mask)
1031 ERR("Incompatible formats %s and %s.\n",
1032 debug_d3dformat(src_surface->resource.format->id),
1033 debug_d3dformat(dst_surface->resource.format->id));
1034 return;
1037 if (!src_mask)
1039 ERR("Not a depth / stencil format: %s.\n",
1040 debug_d3dformat(src_surface->resource.format->id));
1041 return;
1044 gl_mask = 0;
1045 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1046 gl_mask |= GL_DEPTH_BUFFER_BIT;
1047 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1048 gl_mask |= GL_STENCIL_BUFFER_BIT;
1050 /* Make sure the locations are up-to-date. Loading the destination
1051 * surface isn't required if the entire surface is overwritten. */
1052 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1053 if (!surface_is_full_rect(dst_surface, dst_rect))
1054 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1056 context = context_acquire(device, NULL);
1057 if (!context->valid)
1059 context_release(context);
1060 WARN("Invalid context, skipping blit.\n");
1061 return;
1064 gl_info = context->gl_info;
1066 ENTER_GL();
1068 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1069 glReadBuffer(GL_NONE);
1070 checkGLcall("glReadBuffer()");
1071 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1073 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1074 context_set_draw_buffer(context, GL_NONE);
1075 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1077 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1079 glDepthMask(GL_TRUE);
1080 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
1082 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1084 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1086 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1087 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
1089 glStencilMask(~0U);
1090 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
1093 glDisable(GL_SCISSOR_TEST);
1094 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1096 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1097 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1098 checkGLcall("glBlitFramebuffer()");
1100 LEAVE_GL();
1102 if (wined3d_settings.strict_draw_ordering)
1103 wglFlush(); /* Flush to ensure ordering across contexts. */
1105 context_release(context);
1108 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1109 * Depth / stencil is not supported. */
1110 static void surface_blt_fbo(const struct wined3d_device *device, const WINED3DTEXTUREFILTERTYPE filter,
1111 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1112 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1114 const struct wined3d_gl_info *gl_info;
1115 struct wined3d_context *context;
1116 RECT src_rect, dst_rect;
1117 GLenum gl_filter;
1118 GLenum buffer;
1120 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1121 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1122 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1123 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1124 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1126 src_rect = *src_rect_in;
1127 dst_rect = *dst_rect_in;
1129 switch (filter)
1131 case WINED3DTEXF_LINEAR:
1132 gl_filter = GL_LINEAR;
1133 break;
1135 default:
1136 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1137 case WINED3DTEXF_NONE:
1138 case WINED3DTEXF_POINT:
1139 gl_filter = GL_NEAREST;
1140 break;
1143 /* Resolve the source surface first if needed. */
1144 if (src_location == SFLAG_INRB_MULTISAMPLE
1145 && (src_surface->resource.format->id != dst_surface->resource.format->id
1146 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1147 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1148 src_location = SFLAG_INRB_RESOLVED;
1150 /* Make sure the locations are up-to-date. Loading the destination
1151 * surface isn't required if the entire surface is overwritten. (And is
1152 * in fact harmful if we're being called by surface_load_location() with
1153 * the purpose of loading the destination surface.) */
1154 surface_load_location(src_surface, src_location, NULL);
1155 if (!surface_is_full_rect(dst_surface, &dst_rect))
1156 surface_load_location(dst_surface, dst_location, NULL);
1158 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1159 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1160 else context = context_acquire(device, NULL);
1162 if (!context->valid)
1164 context_release(context);
1165 WARN("Invalid context, skipping blit.\n");
1166 return;
1169 gl_info = context->gl_info;
1171 if (src_location == SFLAG_INDRAWABLE)
1173 TRACE("Source surface %p is onscreen.\n", src_surface);
1174 buffer = surface_get_gl_buffer(src_surface);
1175 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1177 else
1179 TRACE("Source surface %p is offscreen.\n", src_surface);
1180 buffer = GL_COLOR_ATTACHMENT0;
1183 ENTER_GL();
1184 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1185 glReadBuffer(buffer);
1186 checkGLcall("glReadBuffer()");
1187 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1188 LEAVE_GL();
1190 if (dst_location == SFLAG_INDRAWABLE)
1192 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1193 buffer = surface_get_gl_buffer(dst_surface);
1194 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1196 else
1198 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1199 buffer = GL_COLOR_ATTACHMENT0;
1202 ENTER_GL();
1203 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1204 context_set_draw_buffer(context, buffer);
1205 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1206 context_invalidate_state(context, STATE_FRAMEBUFFER);
1208 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1209 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
1210 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
1211 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
1212 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
1214 glDisable(GL_SCISSOR_TEST);
1215 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
1217 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1218 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1219 checkGLcall("glBlitFramebuffer()");
1221 LEAVE_GL();
1223 if (wined3d_settings.strict_draw_ordering
1224 || (dst_location == SFLAG_INDRAWABLE
1225 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1226 wglFlush();
1228 context_release(context);
1231 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1232 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
1233 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
1235 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1236 return FALSE;
1238 /* Source and/or destination need to be on the GL side */
1239 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
1240 return FALSE;
1242 switch (blit_op)
1244 case WINED3D_BLIT_OP_COLOR_BLIT:
1245 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1246 return FALSE;
1247 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1248 return FALSE;
1249 break;
1251 case WINED3D_BLIT_OP_DEPTH_BLIT:
1252 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1253 return FALSE;
1254 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1255 return FALSE;
1256 break;
1258 default:
1259 return FALSE;
1262 if (!(src_format->id == dst_format->id
1263 || (is_identity_fixup(src_format->color_fixup)
1264 && is_identity_fixup(dst_format->color_fixup))))
1265 return FALSE;
1267 return TRUE;
1270 /* This function checks if the primary render target uses the 8bit paletted format. */
1271 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1273 if (device->fb.render_targets && device->fb.render_targets[0])
1275 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1276 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1277 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1278 return TRUE;
1280 return FALSE;
1283 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1284 DWORD color, struct wined3d_color *float_color)
1286 const struct wined3d_format *format = surface->resource.format;
1287 const struct wined3d_device *device = surface->resource.device;
1289 switch (format->id)
1291 case WINED3DFMT_P8_UINT:
1292 if (surface->palette)
1294 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1295 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1296 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1298 else
1300 float_color->r = 0.0f;
1301 float_color->g = 0.0f;
1302 float_color->b = 0.0f;
1304 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1305 break;
1307 case WINED3DFMT_B5G6R5_UNORM:
1308 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1309 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1310 float_color->b = (color & 0x1f) / 31.0f;
1311 float_color->a = 1.0f;
1312 break;
1314 case WINED3DFMT_B8G8R8_UNORM:
1315 case WINED3DFMT_B8G8R8X8_UNORM:
1316 float_color->r = D3DCOLOR_R(color);
1317 float_color->g = D3DCOLOR_G(color);
1318 float_color->b = D3DCOLOR_B(color);
1319 float_color->a = 1.0f;
1320 break;
1322 case WINED3DFMT_B8G8R8A8_UNORM:
1323 float_color->r = D3DCOLOR_R(color);
1324 float_color->g = D3DCOLOR_G(color);
1325 float_color->b = D3DCOLOR_B(color);
1326 float_color->a = D3DCOLOR_A(color);
1327 break;
1329 default:
1330 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1331 return FALSE;
1334 return TRUE;
1337 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1339 const struct wined3d_format *format = surface->resource.format;
1341 switch (format->id)
1343 case WINED3DFMT_S1_UINT_D15_UNORM:
1344 *float_depth = depth / (float)0x00007fff;
1345 break;
1347 case WINED3DFMT_D16_UNORM:
1348 *float_depth = depth / (float)0x0000ffff;
1349 break;
1351 case WINED3DFMT_D24_UNORM_S8_UINT:
1352 case WINED3DFMT_X8D24_UNORM:
1353 *float_depth = depth / (float)0x00ffffff;
1354 break;
1356 case WINED3DFMT_D32_UNORM:
1357 *float_depth = depth / (float)0xffffffff;
1358 break;
1360 default:
1361 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1362 return FALSE;
1365 return TRUE;
1368 /* Do not call while under the GL lock. */
1369 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1371 const struct wined3d_resource *resource = &surface->resource;
1372 struct wined3d_device *device = resource->device;
1373 const struct blit_shader *blitter;
1375 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1376 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1377 if (!blitter)
1379 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1380 return WINED3DERR_INVALIDCALL;
1383 return blitter->depth_fill(device, surface, rect, depth);
1386 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1387 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1389 struct wined3d_device *device = src_surface->resource.device;
1391 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1392 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1393 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1394 return WINED3DERR_INVALIDCALL;
1396 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1398 surface_modify_ds_location(dst_surface, SFLAG_DS_OFFSCREEN,
1399 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1400 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
1402 return WINED3D_OK;
1405 /* Do not call while under the GL lock. */
1406 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1407 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1408 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1410 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1411 struct wined3d_device *device = dst_surface->resource.device;
1412 DWORD src_ds_flags, dst_ds_flags;
1413 RECT src_rect, dst_rect;
1414 BOOL scale, convert;
1416 static const DWORD simple_blit = WINEDDBLT_ASYNC
1417 | WINEDDBLT_COLORFILL
1418 | WINEDDBLT_WAIT
1419 | WINEDDBLT_DEPTHFILL
1420 | WINEDDBLT_DONOTWAIT;
1422 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1423 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1424 flags, fx, debug_d3dtexturefiltertype(filter));
1425 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1427 if (fx)
1429 TRACE("dwSize %#x.\n", fx->dwSize);
1430 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
1431 TRACE("dwROP %#x.\n", fx->dwROP);
1432 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
1433 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
1434 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
1435 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
1436 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
1437 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
1438 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
1439 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
1440 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
1441 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
1442 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
1443 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
1444 TRACE("dwReserved %#x.\n", fx->dwReserved);
1445 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
1446 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
1447 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
1448 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
1449 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
1450 TRACE("ddckDestColorkey {%#x, %#x}.\n",
1451 fx->ddckDestColorkey.color_space_low_value,
1452 fx->ddckDestColorkey.color_space_high_value);
1453 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
1454 fx->ddckSrcColorkey.color_space_low_value,
1455 fx->ddckSrcColorkey.color_space_high_value);
1458 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1460 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1461 return WINEDDERR_SURFACEBUSY;
1464 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1466 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1467 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1468 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1469 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1470 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1472 WARN("The application gave us a bad destination rectangle.\n");
1473 return WINEDDERR_INVALIDRECT;
1476 if (src_surface)
1478 surface_get_rect(src_surface, src_rect_in, &src_rect);
1480 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1481 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1482 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1483 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1484 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1486 WARN("Application gave us bad source rectangle for Blt.\n");
1487 return WINEDDERR_INVALIDRECT;
1490 else
1492 memset(&src_rect, 0, sizeof(src_rect));
1495 if (!fx || !(fx->dwDDFX))
1496 flags &= ~WINEDDBLT_DDFX;
1498 if (flags & WINEDDBLT_WAIT)
1499 flags &= ~WINEDDBLT_WAIT;
1501 if (flags & WINEDDBLT_ASYNC)
1503 static unsigned int once;
1505 if (!once++)
1506 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1507 flags &= ~WINEDDBLT_ASYNC;
1510 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1511 if (flags & WINEDDBLT_DONOTWAIT)
1513 static unsigned int once;
1515 if (!once++)
1516 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1517 flags &= ~WINEDDBLT_DONOTWAIT;
1520 if (!device->d3d_initialized)
1522 WARN("D3D not initialized, using fallback.\n");
1523 goto cpu;
1526 /* We want to avoid invalidating the sysmem location for converted
1527 * surfaces, since otherwise we'd have to convert the data back when
1528 * locking them. */
1529 if (dst_surface->flags & SFLAG_CONVERTED)
1531 WARN("Converted surface, using CPU blit.\n");
1532 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1535 if (flags & ~simple_blit)
1537 WARN("Using fallback for complex blit (%#x).\n", flags);
1538 goto fallback;
1541 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1542 src_swapchain = src_surface->container.u.swapchain;
1543 else
1544 src_swapchain = NULL;
1546 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1547 dst_swapchain = dst_surface->container.u.swapchain;
1548 else
1549 dst_swapchain = NULL;
1551 /* This isn't strictly needed. FBO blits for example could deal with
1552 * cross-swapchain blits by first downloading the source to a texture
1553 * before switching to the destination context. We just have this here to
1554 * not have to deal with the issue, since cross-swapchain blits should be
1555 * rare. */
1556 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1558 FIXME("Using fallback for cross-swapchain blit.\n");
1559 goto fallback;
1562 scale = src_surface
1563 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
1564 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
1565 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
1567 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1568 if (src_surface)
1569 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1570 else
1571 src_ds_flags = 0;
1573 if (src_ds_flags || dst_ds_flags)
1575 if (flags & WINEDDBLT_DEPTHFILL)
1577 float depth;
1579 TRACE("Depth fill.\n");
1581 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1582 return WINED3DERR_INVALIDCALL;
1584 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1585 return WINED3D_OK;
1587 else
1589 /* Accessing depth / stencil surfaces is supposed to fail while in
1590 * a scene, except for fills, which seem to work. */
1591 if (device->inScene)
1593 WARN("Rejecting depth / stencil access while in scene.\n");
1594 return WINED3DERR_INVALIDCALL;
1597 if (src_ds_flags != dst_ds_flags)
1599 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1600 return WINED3DERR_INVALIDCALL;
1603 if (src_rect.top || src_rect.left
1604 || src_rect.bottom != src_surface->resource.height
1605 || src_rect.right != src_surface->resource.width)
1607 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1608 wine_dbgstr_rect(&src_rect));
1609 return WINED3DERR_INVALIDCALL;
1612 if (dst_rect.top || dst_rect.left
1613 || dst_rect.bottom != dst_surface->resource.height
1614 || dst_rect.right != dst_surface->resource.width)
1616 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1617 wine_dbgstr_rect(&src_rect));
1618 return WINED3DERR_INVALIDCALL;
1621 if (scale)
1623 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1624 return WINED3DERR_INVALIDCALL;
1627 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1628 return WINED3D_OK;
1631 else
1633 /* In principle this would apply to depth blits as well, but we don't
1634 * implement those in the CPU blitter at the moment. */
1635 if ((dst_surface->flags & SFLAG_INSYSMEM)
1636 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
1638 if (scale)
1639 TRACE("Not doing sysmem blit because of scaling.\n");
1640 else if (convert)
1641 TRACE("Not doing sysmem blit because of format conversion.\n");
1642 else
1643 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1646 if (flags & WINEDDBLT_COLORFILL)
1648 struct wined3d_color color;
1650 TRACE("Color fill.\n");
1652 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1653 goto fallback;
1655 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1656 return WINED3D_OK;
1658 else
1660 TRACE("Color blit.\n");
1662 /* Upload */
1663 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
1665 if (scale)
1666 TRACE("Not doing upload because of scaling.\n");
1667 else if (convert)
1668 TRACE("Not doing upload because of format conversion.\n");
1669 else
1671 POINT dst_point = {dst_rect.left, dst_rect.top};
1673 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
1675 if (!surface_is_offscreen(dst_surface))
1676 surface_load_location(dst_surface, dst_surface->draw_binding, NULL);
1677 return WINED3D_OK;
1682 /* Use present for back -> front blits. The idea behind this is
1683 * that present is potentially faster than a blit, in particular
1684 * when FBO blits aren't available. Some ddraw applications like
1685 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
1686 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
1687 * applications can't blit directly to the frontbuffer. */
1688 if (dst_swapchain && dst_swapchain->back_buffers
1689 && dst_surface == dst_swapchain->front_buffer
1690 && src_surface == dst_swapchain->back_buffers[0])
1692 WINED3DSWAPEFFECT swap_effect = dst_swapchain->desc.swap_effect;
1694 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1696 /* Set the swap effect to COPY, we don't want the backbuffer
1697 * to become undefined. */
1698 dst_swapchain->desc.swap_effect = WINED3DSWAPEFFECT_COPY;
1699 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
1700 dst_swapchain->desc.swap_effect = swap_effect;
1702 return WINED3D_OK;
1705 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1706 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1707 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1709 TRACE("Using FBO blit.\n");
1711 surface_blt_fbo(device, filter,
1712 src_surface, src_surface->draw_binding, &src_rect,
1713 dst_surface, dst_surface->draw_binding, &dst_rect);
1714 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1715 return WINED3D_OK;
1718 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1719 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1720 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1722 TRACE("Using arbfp blit.\n");
1724 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1725 return WINED3D_OK;
1730 fallback:
1732 /* Special cases for render targets. */
1733 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1734 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1736 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1737 src_surface, &src_rect, flags, fx, filter)))
1738 return WINED3D_OK;
1741 cpu:
1743 /* For the rest call the X11 surface implementation. For render targets
1744 * this should be implemented OpenGL accelerated in BltOverride, other
1745 * blits are rather rare. */
1746 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1749 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1750 struct wined3d_surface *render_target)
1752 TRACE("surface %p, render_target %p.\n", surface, render_target);
1754 /* TODO: Check surface sizes, pools, etc. */
1756 if (render_target->resource.multisample_type)
1757 return WINED3DERR_INVALIDCALL;
1759 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3DTEXF_POINT);
1762 /* Context activation is done by the caller. */
1763 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1765 if (surface->flags & SFLAG_DIBSECTION)
1767 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1769 else if (!surface->resource.heapMemory)
1771 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1772 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1773 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1776 ENTER_GL();
1777 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1778 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1779 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1780 surface->resource.size, surface->resource.allocatedMemory));
1781 checkGLcall("glGetBufferSubDataARB");
1782 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1783 checkGLcall("glDeleteBuffersARB");
1784 LEAVE_GL();
1786 surface->pbo = 0;
1787 surface->flags &= ~SFLAG_PBO;
1790 /* Do not call while under the GL lock. */
1791 static void surface_unload(struct wined3d_resource *resource)
1793 struct wined3d_surface *surface = surface_from_resource(resource);
1794 struct wined3d_renderbuffer_entry *entry, *entry2;
1795 struct wined3d_device *device = resource->device;
1796 const struct wined3d_gl_info *gl_info;
1797 struct wined3d_context *context;
1799 TRACE("surface %p.\n", surface);
1801 if (resource->pool == WINED3DPOOL_DEFAULT)
1803 /* Default pool resources are supposed to be destroyed before Reset is called.
1804 * Implicit resources stay however. So this means we have an implicit render target
1805 * or depth stencil. The content may be destroyed, but we still have to tear down
1806 * opengl resources, so we cannot leave early.
1808 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1809 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1810 * or the depth stencil into an FBO the texture or render buffer will be removed
1811 * and all flags get lost
1813 if (!(surface->flags & SFLAG_PBO))
1814 surface_init_sysmem(surface);
1815 /* We also get here when the ddraw swapchain is destroyed, for example
1816 * for a mode switch. In this case this surface won't necessarily be
1817 * an implicit surface. We have to mark it lost so that the
1818 * application can restore it after the mode switch. */
1819 surface->flags |= SFLAG_LOST;
1821 else
1823 /* Load the surface into system memory */
1824 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1825 surface_modify_location(surface, surface->draw_binding, FALSE);
1827 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1828 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1829 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1831 context = context_acquire(device, NULL);
1832 gl_info = context->gl_info;
1834 /* Destroy PBOs, but load them into real sysmem before */
1835 if (surface->flags & SFLAG_PBO)
1836 surface_remove_pbo(surface, gl_info);
1838 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1839 * all application-created targets the application has to release the surface
1840 * before calling _Reset
1842 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1844 ENTER_GL();
1845 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1846 LEAVE_GL();
1847 list_remove(&entry->entry);
1848 HeapFree(GetProcessHeap(), 0, entry);
1850 list_init(&surface->renderbuffers);
1851 surface->current_renderbuffer = NULL;
1853 ENTER_GL();
1855 /* If we're in a texture, the texture name belongs to the texture.
1856 * Otherwise, destroy it. */
1857 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1859 glDeleteTextures(1, &surface->texture_name);
1860 surface->texture_name = 0;
1861 glDeleteTextures(1, &surface->texture_name_srgb);
1862 surface->texture_name_srgb = 0;
1864 if (surface->rb_multisample)
1866 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1867 surface->rb_multisample = 0;
1869 if (surface->rb_resolved)
1871 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1872 surface->rb_resolved = 0;
1875 LEAVE_GL();
1877 context_release(context);
1879 resource_unload(resource);
1882 static const struct wined3d_resource_ops surface_resource_ops =
1884 surface_unload,
1887 static const struct wined3d_surface_ops surface_ops =
1889 surface_private_setup,
1890 surface_realize_palette,
1891 surface_map,
1892 surface_unmap,
1895 /*****************************************************************************
1896 * Initializes the GDI surface, aka creates the DIB section we render to
1897 * The DIB section creation is done by calling GetDC, which will create the
1898 * section and releasing the dc to allow the app to use it. The dib section
1899 * will stay until the surface is released
1901 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1902 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1903 * avoid confusion in the shared surface code.
1905 * Returns:
1906 * WINED3D_OK on success
1907 * The return values of called methods on failure
1909 *****************************************************************************/
1910 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1912 HRESULT hr;
1914 TRACE("surface %p.\n", surface);
1916 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1918 ERR("Overlays not yet supported by GDI surfaces.\n");
1919 return WINED3DERR_INVALIDCALL;
1922 /* Sysmem textures have memory already allocated - release it,
1923 * this avoids an unnecessary memcpy. */
1924 hr = surface_create_dib_section(surface);
1925 if (SUCCEEDED(hr))
1927 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1928 surface->resource.heapMemory = NULL;
1929 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1932 /* We don't mind the nonpow2 stuff in GDI. */
1933 surface->pow2Width = surface->resource.width;
1934 surface->pow2Height = surface->resource.height;
1936 return WINED3D_OK;
1939 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1941 struct wined3d_palette *palette = surface->palette;
1943 TRACE("surface %p.\n", surface);
1945 if (!palette) return;
1947 if (surface->flags & SFLAG_DIBSECTION)
1949 RGBQUAD col[256];
1950 unsigned int i;
1952 TRACE("Updating the DC's palette.\n");
1954 for (i = 0; i < 256; ++i)
1956 col[i].rgbRed = palette->palents[i].peRed;
1957 col[i].rgbGreen = palette->palents[i].peGreen;
1958 col[i].rgbBlue = palette->palents[i].peBlue;
1959 col[i].rgbReserved = 0;
1961 SetDIBColorTable(surface->hDC, 0, 256, col);
1964 /* Update the image because of the palette change. Some games like e.g.
1965 * Red Alert call SetEntries a lot to implement fading. */
1966 /* Tell the swapchain to update the screen. */
1967 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1969 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1970 if (surface == swapchain->front_buffer)
1972 x11_copy_to_screen(swapchain, NULL);
1977 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
1979 TRACE("surface %p, rect %s, flags %#x.\n",
1980 surface, wine_dbgstr_rect(rect), flags);
1982 if (!(surface->flags & SFLAG_DIBSECTION))
1984 /* This happens on gdi surfaces if the application set a user pointer
1985 * and resets it. Recreate the DIB section. */
1986 surface_create_dib_section(surface);
1987 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1991 static void gdi_surface_unmap(struct wined3d_surface *surface)
1993 TRACE("surface %p.\n", surface);
1995 /* Tell the swapchain to update the screen. */
1996 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1998 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1999 if (surface == swapchain->front_buffer)
2001 x11_copy_to_screen(swapchain, &surface->lockedRect);
2005 memset(&surface->lockedRect, 0, sizeof(RECT));
2008 static const struct wined3d_surface_ops gdi_surface_ops =
2010 gdi_surface_private_setup,
2011 gdi_surface_realize_palette,
2012 gdi_surface_map,
2013 gdi_surface_unmap,
2016 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2018 GLuint *name;
2019 DWORD flag;
2021 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2023 if(srgb)
2025 name = &surface->texture_name_srgb;
2026 flag = SFLAG_INSRGBTEX;
2028 else
2030 name = &surface->texture_name;
2031 flag = SFLAG_INTEXTURE;
2034 if (!*name && new_name)
2036 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2037 * surface has no texture name yet. See if we can get rid of this. */
2038 if (surface->flags & flag)
2039 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2040 surface_modify_location(surface, flag, FALSE);
2043 *name = new_name;
2044 surface_force_reload(surface);
2047 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2049 TRACE("surface %p, target %#x.\n", surface, target);
2051 if (surface->texture_target != target)
2053 if (target == GL_TEXTURE_RECTANGLE_ARB)
2055 surface->flags &= ~SFLAG_NORMCOORD;
2057 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2059 surface->flags |= SFLAG_NORMCOORD;
2062 surface->texture_target = target;
2063 surface_force_reload(surface);
2066 /* Context activation is done by the caller. */
2067 void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
2069 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
2071 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2073 struct wined3d_texture *texture = surface->container.u.texture;
2075 TRACE("Passing to container (%p).\n", texture);
2076 texture->texture_ops->texture_bind(texture, context, srgb);
2078 else
2080 if (surface->texture_level)
2082 ERR("Standalone surface %p is non-zero texture level %u.\n",
2083 surface, surface->texture_level);
2086 if (srgb)
2087 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2089 ENTER_GL();
2091 if (!surface->texture_name)
2093 glGenTextures(1, &surface->texture_name);
2094 checkGLcall("glGenTextures");
2096 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2098 context_bind_texture(context, surface->texture_target, surface->texture_name);
2099 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2100 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2101 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2102 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2103 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2104 checkGLcall("glTexParameteri");
2106 else
2108 context_bind_texture(context, surface->texture_target, surface->texture_name);
2111 LEAVE_GL();
2115 /* This call just downloads data, the caller is responsible for binding the
2116 * correct texture. */
2117 /* Context activation is done by the caller. */
2118 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2120 const struct wined3d_format *format = surface->resource.format;
2122 /* Only support read back of converted P8 surfaces. */
2123 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2125 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
2126 return;
2129 ENTER_GL();
2131 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2133 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2134 surface, surface->texture_level, format->glFormat, format->glType,
2135 surface->resource.allocatedMemory);
2137 if (surface->flags & SFLAG_PBO)
2139 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2140 checkGLcall("glBindBufferARB");
2141 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2142 checkGLcall("glGetCompressedTexImageARB");
2143 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2144 checkGLcall("glBindBufferARB");
2146 else
2148 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2149 surface->texture_level, surface->resource.allocatedMemory));
2150 checkGLcall("glGetCompressedTexImageARB");
2153 LEAVE_GL();
2155 else
2157 void *mem;
2158 GLenum gl_format = format->glFormat;
2159 GLenum gl_type = format->glType;
2160 int src_pitch = 0;
2161 int dst_pitch = 0;
2163 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2164 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2166 gl_format = GL_ALPHA;
2167 gl_type = GL_UNSIGNED_BYTE;
2170 if (surface->flags & SFLAG_NONPOW2)
2172 unsigned char alignment = surface->resource.device->surface_alignment;
2173 src_pitch = format->byte_count * surface->pow2Width;
2174 dst_pitch = wined3d_surface_get_pitch(surface);
2175 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2176 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2178 else
2180 mem = surface->resource.allocatedMemory;
2183 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2184 surface, surface->texture_level, gl_format, gl_type, mem);
2186 if (surface->flags & SFLAG_PBO)
2188 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2189 checkGLcall("glBindBufferARB");
2191 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2192 checkGLcall("glGetTexImage");
2194 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2195 checkGLcall("glBindBufferARB");
2197 else
2199 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2200 checkGLcall("glGetTexImage");
2202 LEAVE_GL();
2204 if (surface->flags & SFLAG_NONPOW2)
2206 const BYTE *src_data;
2207 BYTE *dst_data;
2208 UINT y;
2210 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2211 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2212 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2214 * We're doing this...
2216 * instead of boxing the texture :
2217 * |<-texture width ->| -->pow2width| /\
2218 * |111111111111111111| | |
2219 * |222 Texture 222222| boxed empty | texture height
2220 * |3333 Data 33333333| | |
2221 * |444444444444444444| | \/
2222 * ----------------------------------- |
2223 * | boxed empty | boxed empty | pow2height
2224 * | | | \/
2225 * -----------------------------------
2228 * we're repacking the data to the expected texture width
2230 * |<-texture width ->| -->pow2width| /\
2231 * |111111111111111111222222222222222| |
2232 * |222333333333333333333444444444444| texture height
2233 * |444444 | |
2234 * | | \/
2235 * | | |
2236 * | empty | pow2height
2237 * | | \/
2238 * -----------------------------------
2240 * == is the same as
2242 * |<-texture width ->| /\
2243 * |111111111111111111|
2244 * |222222222222222222|texture height
2245 * |333333333333333333|
2246 * |444444444444444444| \/
2247 * --------------------
2249 * this also means that any references to allocatedMemory should work with the data as if were a
2250 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2252 * internally the texture is still stored in a boxed format so any references to textureName will
2253 * get a boxed texture with width pow2width and not a texture of width resource.width.
2255 * Performance should not be an issue, because applications normally do not lock the surfaces when
2256 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2257 * and doesn't have to be re-read. */
2258 src_data = mem;
2259 dst_data = surface->resource.allocatedMemory;
2260 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2261 for (y = 1; y < surface->resource.height; ++y)
2263 /* skip the first row */
2264 src_data += src_pitch;
2265 dst_data += dst_pitch;
2266 memcpy(dst_data, src_data, dst_pitch);
2269 HeapFree(GetProcessHeap(), 0, mem);
2273 /* Surface has now been downloaded */
2274 surface->flags |= SFLAG_INSYSMEM;
2277 /* This call just uploads data, the caller is responsible for binding the
2278 * correct texture. */
2279 /* Context activation is done by the caller. */
2280 static void surface_upload_data(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2281 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
2282 BOOL srgb, const struct wined3d_bo_address *data)
2284 UINT update_w = src_rect->right - src_rect->left;
2285 UINT update_h = src_rect->bottom - src_rect->top;
2287 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
2288 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
2289 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2291 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2292 update_h *= format->heightscale;
2294 ENTER_GL();
2296 if (data->buffer_object)
2298 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2299 checkGLcall("glBindBufferARB");
2302 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2304 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2305 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2306 const BYTE *addr = data->addr;
2307 GLenum internal;
2309 addr += (src_rect->top / format->block_height) * src_pitch;
2310 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2312 if (srgb)
2313 internal = format->glGammaInternal;
2314 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2315 internal = format->rtInternal;
2316 else
2317 internal = format->glInternal;
2319 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2320 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2321 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2323 if (row_length == src_pitch)
2325 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2326 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2328 else
2330 UINT row, y;
2332 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2333 * can't use the unpack row length like below. */
2334 for (row = 0, y = dst_point->y; row < row_count; ++row)
2336 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2337 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2338 y += format->block_height;
2339 addr += src_pitch;
2342 checkGLcall("glCompressedTexSubImage2DARB");
2344 else
2346 const BYTE *addr = data->addr;
2348 addr += src_rect->top * src_pitch;
2349 addr += src_rect->left * format->byte_count;
2351 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2352 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2353 update_w, update_h, format->glFormat, format->glType, addr);
2355 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
2356 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2357 update_w, update_h, format->glFormat, format->glType, addr);
2358 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2359 checkGLcall("glTexSubImage2D");
2362 if (data->buffer_object)
2364 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2365 checkGLcall("glBindBufferARB");
2368 LEAVE_GL();
2370 if (wined3d_settings.strict_draw_ordering)
2371 wglFlush();
2373 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2375 struct wined3d_device *device = surface->resource.device;
2376 unsigned int i;
2378 for (i = 0; i < device->context_count; ++i)
2380 context_surface_update(device->contexts[i], surface);
2385 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
2386 struct wined3d_surface *src_surface, const RECT *src_rect)
2388 const struct wined3d_format *src_format;
2389 const struct wined3d_format *dst_format;
2390 const struct wined3d_gl_info *gl_info;
2391 struct wined3d_context *context;
2392 struct wined3d_bo_address data;
2393 struct wined3d_format format;
2394 UINT update_w, update_h;
2395 CONVERT_TYPES convert;
2396 UINT dst_w, dst_h;
2397 UINT src_w, src_h;
2398 UINT src_pitch;
2399 POINT p;
2400 RECT r;
2402 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
2403 dst_surface, wine_dbgstr_point(dst_point),
2404 src_surface, wine_dbgstr_rect(src_rect));
2406 src_format = src_surface->resource.format;
2407 dst_format = dst_surface->resource.format;
2409 if (src_format->id != dst_format->id)
2411 WARN("Source and destination surfaces should have the same format.\n");
2412 return WINED3DERR_INVALIDCALL;
2415 if (!dst_point)
2417 p.x = 0;
2418 p.y = 0;
2419 dst_point = &p;
2421 else if (dst_point->x < 0 || dst_point->y < 0)
2423 WARN("Invalid destination point.\n");
2424 return WINED3DERR_INVALIDCALL;
2427 if (!src_rect)
2429 r.left = 0;
2430 r.top = 0;
2431 r.right = src_surface->resource.width;
2432 r.bottom = src_surface->resource.height;
2433 src_rect = &r;
2435 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
2436 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
2438 WARN("Invalid source rectangle.\n");
2439 return WINED3DERR_INVALIDCALL;
2442 src_w = src_surface->resource.width;
2443 src_h = src_surface->resource.height;
2445 dst_w = dst_surface->resource.width;
2446 dst_h = dst_surface->resource.height;
2448 update_w = src_rect->right - src_rect->left;
2449 update_h = src_rect->bottom - src_rect->top;
2451 if (update_w > dst_w || dst_point->x > dst_w - update_w
2452 || update_h > dst_h || dst_point->y > dst_h - update_h)
2454 WARN("Destination out of bounds.\n");
2455 return WINED3DERR_INVALIDCALL;
2458 /* NPOT block sizes would be silly. */
2459 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS)
2460 && ((update_w & (src_format->block_width - 1) || update_h & (src_format->block_height - 1))
2461 && (src_w != update_w || dst_w != update_w || src_h != update_h || dst_h != update_h)))
2463 WARN("Update rect not block-aligned.\n");
2464 return WINED3DERR_INVALIDCALL;
2467 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
2468 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
2469 if (convert != NO_CONVERSION || format.convert)
2471 RECT dst_rect = {dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h};
2472 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3DTEXF_POINT);
2475 context = context_acquire(dst_surface->resource.device, NULL);
2476 gl_info = context->gl_info;
2478 /* Only load the surface for partial updates. For newly allocated texture
2479 * the texture wouldn't be the current location, and we'd upload zeroes
2480 * just to overwrite them again. */
2481 if (update_w == dst_w && update_h == dst_h)
2482 surface_prepare_texture(dst_surface, context, FALSE);
2483 else
2484 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
2485 surface_bind(dst_surface, context, FALSE);
2487 data.buffer_object = src_surface->pbo;
2488 data.addr = src_surface->resource.allocatedMemory;
2489 src_pitch = wined3d_surface_get_pitch(src_surface);
2491 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
2493 invalidate_active_texture(dst_surface->resource.device, context);
2495 context_release(context);
2497 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
2498 return WINED3D_OK;
2501 /* This call just allocates the texture, the caller is responsible for binding
2502 * the correct texture. */
2503 /* Context activation is done by the caller. */
2504 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2505 const struct wined3d_format *format, BOOL srgb)
2507 BOOL enable_client_storage = FALSE;
2508 GLsizei width = surface->pow2Width;
2509 GLsizei height = surface->pow2Height;
2510 const BYTE *mem = NULL;
2511 GLenum internal;
2513 if (srgb)
2515 internal = format->glGammaInternal;
2517 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2519 internal = format->rtInternal;
2521 else
2523 internal = format->glInternal;
2526 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2528 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",
2529 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2530 internal, width, height, format->glFormat, format->glType);
2532 ENTER_GL();
2534 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2536 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2537 || !surface->resource.allocatedMemory)
2539 /* In some cases we want to disable client storage.
2540 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2541 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2542 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2543 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2545 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2546 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2547 surface->flags &= ~SFLAG_CLIENT;
2548 enable_client_storage = TRUE;
2550 else
2552 surface->flags |= SFLAG_CLIENT;
2554 /* Point OpenGL to our allocated texture memory. Do not use
2555 * resource.allocatedMemory here because it might point into a
2556 * PBO. Instead use heapMemory, but get the alignment right. */
2557 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2558 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2562 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2564 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2565 internal, width, height, 0, surface->resource.size, mem));
2566 checkGLcall("glCompressedTexImage2DARB");
2568 else
2570 glTexImage2D(surface->texture_target, surface->texture_level,
2571 internal, width, height, 0, format->glFormat, format->glType, mem);
2572 checkGLcall("glTexImage2D");
2575 if(enable_client_storage) {
2576 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2577 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2579 LEAVE_GL();
2582 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2583 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2584 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2585 /* GL locking is done by the caller */
2586 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2588 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2589 struct wined3d_renderbuffer_entry *entry;
2590 GLuint renderbuffer = 0;
2591 unsigned int src_width, src_height;
2592 unsigned int width, height;
2594 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2596 width = rt->pow2Width;
2597 height = rt->pow2Height;
2599 else
2601 width = surface->pow2Width;
2602 height = surface->pow2Height;
2605 src_width = surface->pow2Width;
2606 src_height = surface->pow2Height;
2608 /* A depth stencil smaller than the render target is not valid */
2609 if (width > src_width || height > src_height) return;
2611 /* Remove any renderbuffer set if the sizes match */
2612 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2613 || (width == src_width && height == src_height))
2615 surface->current_renderbuffer = NULL;
2616 return;
2619 /* Look if we've already got a renderbuffer of the correct dimensions */
2620 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2622 if (entry->width == width && entry->height == height)
2624 renderbuffer = entry->id;
2625 surface->current_renderbuffer = entry;
2626 break;
2630 if (!renderbuffer)
2632 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2633 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2634 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2635 surface->resource.format->glInternal, width, height);
2637 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2638 entry->width = width;
2639 entry->height = height;
2640 entry->id = renderbuffer;
2641 list_add_head(&surface->renderbuffers, &entry->entry);
2643 surface->current_renderbuffer = entry;
2646 checkGLcall("set_compatible_renderbuffer");
2649 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2651 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2653 TRACE("surface %p.\n", surface);
2655 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2657 ERR("Surface %p is not on a swapchain.\n", surface);
2658 return GL_NONE;
2661 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2663 if (swapchain->render_to_fbo)
2665 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2666 return GL_COLOR_ATTACHMENT0;
2668 TRACE("Returning GL_BACK\n");
2669 return GL_BACK;
2671 else if (surface == swapchain->front_buffer)
2673 TRACE("Returning GL_FRONT\n");
2674 return GL_FRONT;
2677 FIXME("Higher back buffer, returning GL_BACK\n");
2678 return GL_BACK;
2681 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2682 void surface_add_dirty_rect(struct wined3d_surface *surface, const struct wined3d_box *dirty_rect)
2684 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2686 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2687 /* No partial locking for textures yet. */
2688 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2690 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2691 if (dirty_rect)
2693 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->left);
2694 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->top);
2695 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->right);
2696 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->bottom);
2698 else
2700 surface->dirtyRect.left = 0;
2701 surface->dirtyRect.top = 0;
2702 surface->dirtyRect.right = surface->resource.width;
2703 surface->dirtyRect.bottom = surface->resource.height;
2706 /* if the container is a texture then mark it dirty. */
2707 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2709 TRACE("Passing to container.\n");
2710 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2714 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2716 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2717 BOOL ck_changed;
2719 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2721 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2723 ERR("Not supported on scratch surfaces.\n");
2724 return WINED3DERR_INVALIDCALL;
2727 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2729 /* Reload if either the texture and sysmem have different ideas about the
2730 * color key, or the actual key values changed. */
2731 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2732 && (surface->gl_color_key.color_space_low_value != surface->src_blt_color_key.color_space_low_value
2733 || surface->gl_color_key.color_space_high_value != surface->src_blt_color_key.color_space_high_value)))
2735 TRACE("Reloading because of color keying\n");
2736 /* To perform the color key conversion we need a sysmem copy of
2737 * the surface. Make sure we have it. */
2739 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2740 /* Make sure the texture is reloaded because of the color key change,
2741 * this kills performance though :( */
2742 /* TODO: This is not necessarily needed with hw palettized texture support. */
2743 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2744 /* Switching color keying on / off may change the internal format. */
2745 if (ck_changed)
2746 surface_force_reload(surface);
2748 else if (!(surface->flags & flag))
2750 TRACE("Reloading because surface is dirty.\n");
2752 else
2754 TRACE("surface is already in texture\n");
2755 return WINED3D_OK;
2758 /* No partial locking for textures yet. */
2759 surface_load_location(surface, flag, NULL);
2760 surface_evict_sysmem(surface);
2762 return WINED3D_OK;
2765 /* See also float_16_to_32() in wined3d_private.h */
2766 static inline unsigned short float_32_to_16(const float *in)
2768 int exp = 0;
2769 float tmp = fabsf(*in);
2770 unsigned int mantissa;
2771 unsigned short ret;
2773 /* Deal with special numbers */
2774 if (*in == 0.0f)
2775 return 0x0000;
2776 if (isnan(*in))
2777 return 0x7c01;
2778 if (isinf(*in))
2779 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2781 if (tmp < powf(2, 10))
2785 tmp = tmp * 2.0f;
2786 exp--;
2787 } while (tmp < powf(2, 10));
2789 else if (tmp >= powf(2, 11))
2793 tmp /= 2.0f;
2794 exp++;
2795 } while (tmp >= powf(2, 11));
2798 mantissa = (unsigned int)tmp;
2799 if (tmp - mantissa >= 0.5f)
2800 ++mantissa; /* Round to nearest, away from zero. */
2802 exp += 10; /* Normalize the mantissa. */
2803 exp += 15; /* Exponent is encoded with excess 15. */
2805 if (exp > 30) /* too big */
2807 ret = 0x7c00; /* INF */
2809 else if (exp <= 0)
2811 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2812 while (exp <= 0)
2814 mantissa = mantissa >> 1;
2815 ++exp;
2817 ret = mantissa & 0x3ff;
2819 else
2821 ret = (exp << 10) | (mantissa & 0x3ff);
2824 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2825 return ret;
2828 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2830 ULONG refcount;
2832 TRACE("Surface %p, container %p of type %#x.\n",
2833 surface, surface->container.u.base, surface->container.type);
2835 switch (surface->container.type)
2837 case WINED3D_CONTAINER_TEXTURE:
2838 return wined3d_texture_incref(surface->container.u.texture);
2840 case WINED3D_CONTAINER_SWAPCHAIN:
2841 return wined3d_swapchain_incref(surface->container.u.swapchain);
2843 default:
2844 ERR("Unhandled container type %#x.\n", surface->container.type);
2845 case WINED3D_CONTAINER_NONE:
2846 break;
2849 refcount = InterlockedIncrement(&surface->resource.ref);
2850 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2852 return refcount;
2855 /* Do not call while under the GL lock. */
2856 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2858 ULONG refcount;
2860 TRACE("Surface %p, container %p of type %#x.\n",
2861 surface, surface->container.u.base, surface->container.type);
2863 switch (surface->container.type)
2865 case WINED3D_CONTAINER_TEXTURE:
2866 return wined3d_texture_decref(surface->container.u.texture);
2868 case WINED3D_CONTAINER_SWAPCHAIN:
2869 return wined3d_swapchain_decref(surface->container.u.swapchain);
2871 default:
2872 ERR("Unhandled container type %#x.\n", surface->container.type);
2873 case WINED3D_CONTAINER_NONE:
2874 break;
2877 refcount = InterlockedDecrement(&surface->resource.ref);
2878 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2880 if (!refcount)
2882 surface_cleanup(surface);
2883 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2885 TRACE("Destroyed surface %p.\n", surface);
2886 HeapFree(GetProcessHeap(), 0, surface);
2889 return refcount;
2892 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2894 return resource_set_priority(&surface->resource, priority);
2897 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2899 return resource_get_priority(&surface->resource);
2902 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2904 TRACE("surface %p.\n", surface);
2906 if (!surface->resource.device->d3d_initialized)
2908 ERR("D3D not initialized.\n");
2909 return;
2912 surface_internal_preload(surface, SRGB_ANY);
2915 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2917 TRACE("surface %p.\n", surface);
2919 return surface->resource.parent;
2922 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2924 TRACE("surface %p.\n", surface);
2926 return &surface->resource;
2929 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2931 TRACE("surface %p, flags %#x.\n", surface, flags);
2933 switch (flags)
2935 case WINEDDGBS_CANBLT:
2936 case WINEDDGBS_ISBLTDONE:
2937 return WINED3D_OK;
2939 default:
2940 return WINED3DERR_INVALIDCALL;
2944 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2946 TRACE("surface %p, flags %#x.\n", surface, flags);
2948 /* XXX: DDERR_INVALIDSURFACETYPE */
2950 switch (flags)
2952 case WINEDDGFS_CANFLIP:
2953 case WINEDDGFS_ISFLIPDONE:
2954 return WINED3D_OK;
2956 default:
2957 return WINED3DERR_INVALIDCALL;
2961 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2963 TRACE("surface %p.\n", surface);
2965 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2966 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2969 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2971 TRACE("surface %p.\n", surface);
2973 surface->flags &= ~SFLAG_LOST;
2974 return WINED3D_OK;
2977 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2979 TRACE("surface %p, palette %p.\n", surface, palette);
2981 if (surface->palette == palette)
2983 TRACE("Nop palette change.\n");
2984 return WINED3D_OK;
2987 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2988 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2990 surface->palette = palette;
2992 if (palette)
2994 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2995 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
2997 surface->surface_ops->surface_realize_palette(surface);
3000 return WINED3D_OK;
3003 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
3004 DWORD flags, const struct wined3d_color_key *color_key)
3006 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3008 if (flags & WINEDDCKEY_COLORSPACE)
3010 FIXME(" colorkey value not supported (%08x) !\n", flags);
3011 return WINED3DERR_INVALIDCALL;
3014 /* Dirtify the surface, but only if a key was changed. */
3015 if (color_key)
3017 switch (flags & ~WINEDDCKEY_COLORSPACE)
3019 case WINEDDCKEY_DESTBLT:
3020 surface->dst_blt_color_key = *color_key;
3021 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3022 break;
3024 case WINEDDCKEY_DESTOVERLAY:
3025 surface->dst_overlay_color_key = *color_key;
3026 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3027 break;
3029 case WINEDDCKEY_SRCOVERLAY:
3030 surface->src_overlay_color_key = *color_key;
3031 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3032 break;
3034 case WINEDDCKEY_SRCBLT:
3035 surface->src_blt_color_key = *color_key;
3036 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3037 break;
3040 else
3042 switch (flags & ~WINEDDCKEY_COLORSPACE)
3044 case WINEDDCKEY_DESTBLT:
3045 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3046 break;
3048 case WINEDDCKEY_DESTOVERLAY:
3049 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3050 break;
3052 case WINEDDCKEY_SRCOVERLAY:
3053 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3054 break;
3056 case WINEDDCKEY_SRCBLT:
3057 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3058 break;
3062 return WINED3D_OK;
3065 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3067 TRACE("surface %p.\n", surface);
3069 return surface->palette;
3072 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3074 const struct wined3d_format *format = surface->resource.format;
3075 DWORD pitch;
3077 TRACE("surface %p.\n", surface);
3079 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
3081 /* Since compressed formats are block based, pitch means the amount of
3082 * bytes to the next row of block rather than the next row of pixels. */
3083 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3084 pitch = row_block_count * format->block_byte_count;
3086 else
3088 unsigned char alignment = surface->resource.device->surface_alignment;
3089 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3090 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3093 TRACE("Returning %u.\n", pitch);
3095 return pitch;
3098 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3100 TRACE("surface %p, mem %p.\n", surface, mem);
3102 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3104 WARN("Surface is locked or the DC is in use.\n");
3105 return WINED3DERR_INVALIDCALL;
3108 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
3109 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3111 ERR("Not supported on render targets.\n");
3112 return WINED3DERR_INVALIDCALL;
3115 if (mem && mem != surface->resource.allocatedMemory)
3117 void *release = NULL;
3119 /* Do I have to copy the old surface content? */
3120 if (surface->flags & SFLAG_DIBSECTION)
3122 DeleteDC(surface->hDC);
3123 DeleteObject(surface->dib.DIBsection);
3124 surface->dib.bitmap_data = NULL;
3125 surface->resource.allocatedMemory = NULL;
3126 surface->hDC = NULL;
3127 surface->flags &= ~SFLAG_DIBSECTION;
3129 else if (!(surface->flags & SFLAG_USERPTR))
3131 release = surface->resource.heapMemory;
3132 surface->resource.heapMemory = NULL;
3134 surface->resource.allocatedMemory = mem;
3135 surface->flags |= SFLAG_USERPTR;
3137 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
3138 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3140 /* For client textures OpenGL has to be notified. */
3141 if (surface->flags & SFLAG_CLIENT)
3142 surface_release_client_storage(surface);
3144 /* Now free the old memory if any. */
3145 HeapFree(GetProcessHeap(), 0, release);
3147 else if (surface->flags & SFLAG_USERPTR)
3149 /* HeapMemory should be NULL already. */
3150 if (surface->resource.heapMemory)
3151 ERR("User pointer surface has heap memory allocated.\n");
3153 if (!mem)
3155 surface->resource.allocatedMemory = NULL;
3156 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
3158 if (surface->flags & SFLAG_CLIENT)
3159 surface_release_client_storage(surface);
3161 surface_prepare_system_memory(surface);
3164 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3167 return WINED3D_OK;
3170 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3172 LONG w, h;
3174 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3176 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3178 WARN("Not an overlay surface.\n");
3179 return WINEDDERR_NOTAOVERLAYSURFACE;
3182 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3183 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3184 surface->overlay_destrect.left = x;
3185 surface->overlay_destrect.top = y;
3186 surface->overlay_destrect.right = x + w;
3187 surface->overlay_destrect.bottom = y + h;
3189 surface_draw_overlay(surface);
3191 return WINED3D_OK;
3194 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3196 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3198 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3200 TRACE("Not an overlay surface.\n");
3201 return WINEDDERR_NOTAOVERLAYSURFACE;
3204 if (!surface->overlay_dest)
3206 TRACE("Overlay not visible.\n");
3207 *x = 0;
3208 *y = 0;
3209 return WINEDDERR_OVERLAYNOTVISIBLE;
3212 *x = surface->overlay_destrect.left;
3213 *y = surface->overlay_destrect.top;
3215 TRACE("Returning position %d, %d.\n", *x, *y);
3217 return WINED3D_OK;
3220 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3221 DWORD flags, struct wined3d_surface *ref)
3223 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3225 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3227 TRACE("Not an overlay surface.\n");
3228 return WINEDDERR_NOTAOVERLAYSURFACE;
3231 return WINED3D_OK;
3234 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3235 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3237 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3238 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3240 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3242 WARN("Not an overlay surface.\n");
3243 return WINEDDERR_NOTAOVERLAYSURFACE;
3245 else if (!dst_surface)
3247 WARN("Dest surface is NULL.\n");
3248 return WINED3DERR_INVALIDCALL;
3251 if (src_rect)
3253 surface->overlay_srcrect = *src_rect;
3255 else
3257 surface->overlay_srcrect.left = 0;
3258 surface->overlay_srcrect.top = 0;
3259 surface->overlay_srcrect.right = surface->resource.width;
3260 surface->overlay_srcrect.bottom = surface->resource.height;
3263 if (dst_rect)
3265 surface->overlay_destrect = *dst_rect;
3267 else
3269 surface->overlay_destrect.left = 0;
3270 surface->overlay_destrect.top = 0;
3271 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3272 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3275 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3277 surface->overlay_dest = NULL;
3278 list_remove(&surface->overlay_entry);
3281 if (flags & WINEDDOVER_SHOW)
3283 if (surface->overlay_dest != dst_surface)
3285 surface->overlay_dest = dst_surface;
3286 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3289 else if (flags & WINEDDOVER_HIDE)
3291 /* tests show that the rectangles are erased on hide */
3292 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3293 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3294 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3295 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3296 surface->overlay_dest = NULL;
3299 surface_draw_overlay(surface);
3301 return WINED3D_OK;
3304 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3306 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3308 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3310 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3312 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3313 return WINED3DERR_INVALIDCALL;
3316 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3317 surface->pow2Width, surface->pow2Height);
3318 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3319 surface->resource.format = format;
3321 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3322 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3323 format->glFormat, format->glInternal, format->glType);
3325 return WINED3D_OK;
3328 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3329 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3331 unsigned short *dst_s;
3332 const float *src_f;
3333 unsigned int x, y;
3335 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3337 for (y = 0; y < h; ++y)
3339 src_f = (const float *)(src + y * pitch_in);
3340 dst_s = (unsigned short *) (dst + y * pitch_out);
3341 for (x = 0; x < w; ++x)
3343 dst_s[x] = float_32_to_16(src_f + x);
3348 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3349 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3351 static const unsigned char convert_5to8[] =
3353 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3354 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3355 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3356 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3358 static const unsigned char convert_6to8[] =
3360 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3361 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3362 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3363 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3364 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3365 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3366 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3367 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3369 unsigned int x, y;
3371 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3373 for (y = 0; y < h; ++y)
3375 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3376 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3377 for (x = 0; x < w; ++x)
3379 WORD pixel = src_line[x];
3380 dst_line[x] = 0xff000000
3381 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3382 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3383 | convert_5to8[(pixel & 0x001f)];
3388 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3389 * in both cases we're just setting the X / Alpha channel to 0xff. */
3390 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3391 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3393 unsigned int x, y;
3395 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3397 for (y = 0; y < h; ++y)
3399 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3400 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3402 for (x = 0; x < w; ++x)
3404 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3409 static inline BYTE cliptobyte(int x)
3411 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3414 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3415 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3417 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3418 unsigned int x, y;
3420 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3422 for (y = 0; y < h; ++y)
3424 const BYTE *src_line = src + y * pitch_in;
3425 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3426 for (x = 0; x < w; ++x)
3428 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3429 * C = Y - 16; D = U - 128; E = V - 128;
3430 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3431 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3432 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3433 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3434 * U and V are shared between the pixels. */
3435 if (!(x & 1)) /* For every even pixel, read new U and V. */
3437 d = (int) src_line[1] - 128;
3438 e = (int) src_line[3] - 128;
3439 r2 = 409 * e + 128;
3440 g2 = - 100 * d - 208 * e + 128;
3441 b2 = 516 * d + 128;
3443 c2 = 298 * ((int) src_line[0] - 16);
3444 dst_line[x] = 0xff000000
3445 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3446 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3447 | cliptobyte((c2 + b2) >> 8); /* blue */
3448 /* Scale RGB values to 0..255 range,
3449 * then clip them if still not in range (may be negative),
3450 * then shift them within DWORD if necessary. */
3451 src_line += 2;
3456 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3457 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3459 unsigned int x, y;
3460 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3462 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3464 for (y = 0; y < h; ++y)
3466 const BYTE *src_line = src + y * pitch_in;
3467 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3468 for (x = 0; x < w; ++x)
3470 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3471 * C = Y - 16; D = U - 128; E = V - 128;
3472 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3473 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3474 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3475 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3476 * U and V are shared between the pixels. */
3477 if (!(x & 1)) /* For every even pixel, read new U and V. */
3479 d = (int) src_line[1] - 128;
3480 e = (int) src_line[3] - 128;
3481 r2 = 409 * e + 128;
3482 g2 = - 100 * d - 208 * e + 128;
3483 b2 = 516 * d + 128;
3485 c2 = 298 * ((int) src_line[0] - 16);
3486 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3487 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3488 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3489 /* Scale RGB values to 0..255 range,
3490 * then clip them if still not in range (may be negative),
3491 * then shift them within DWORD if necessary. */
3492 src_line += 2;
3497 struct d3dfmt_convertor_desc
3499 enum wined3d_format_id from, to;
3500 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3503 static const struct d3dfmt_convertor_desc convertors[] =
3505 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3506 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3507 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3508 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3509 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3510 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3513 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3514 enum wined3d_format_id to)
3516 unsigned int i;
3518 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3520 if (convertors[i].from == from && convertors[i].to == to)
3521 return &convertors[i];
3524 return NULL;
3527 /*****************************************************************************
3528 * surface_convert_format
3530 * Creates a duplicate of a surface in a different format. Is used by Blt to
3531 * blit between surfaces with different formats.
3533 * Parameters
3534 * source: Source surface
3535 * fmt: Requested destination format
3537 *****************************************************************************/
3538 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3540 struct wined3d_mapped_rect src_map, dst_map;
3541 const struct d3dfmt_convertor_desc *conv;
3542 struct wined3d_surface *ret = NULL;
3543 HRESULT hr;
3545 conv = find_convertor(source->resource.format->id, to_fmt);
3546 if (!conv)
3548 FIXME("Cannot find a conversion function from format %s to %s.\n",
3549 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3550 return NULL;
3553 wined3d_surface_create(source->resource.device, source->resource.width,
3554 source->resource.height, to_fmt, 0 /* level */, 0 /* usage */, WINED3DPOOL_SCRATCH,
3555 WINED3DMULTISAMPLE_NONE /* TODO: Multisampled conversion */, 0 /* MultiSampleQuality */,
3556 source->surface_type, WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD,
3557 NULL /* parent */, &wined3d_null_parent_ops, &ret);
3558 if (!ret)
3560 ERR("Failed to create a destination surface for conversion.\n");
3561 return NULL;
3564 memset(&src_map, 0, sizeof(src_map));
3565 memset(&dst_map, 0, sizeof(dst_map));
3567 hr = wined3d_surface_map(source, &src_map, NULL, WINED3DLOCK_READONLY);
3568 if (FAILED(hr))
3570 ERR("Failed to lock the source surface.\n");
3571 wined3d_surface_decref(ret);
3572 return NULL;
3574 hr = wined3d_surface_map(ret, &dst_map, NULL, WINED3DLOCK_READONLY);
3575 if (FAILED(hr))
3577 ERR("Failed to lock the destination surface.\n");
3578 wined3d_surface_unmap(source);
3579 wined3d_surface_decref(ret);
3580 return NULL;
3583 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
3584 source->resource.width, source->resource.height);
3586 wined3d_surface_unmap(ret);
3587 wined3d_surface_unmap(source);
3589 return ret;
3592 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3593 unsigned int bpp, UINT pitch, DWORD color)
3595 BYTE *first;
3596 int x, y;
3598 /* Do first row */
3600 #define COLORFILL_ROW(type) \
3601 do { \
3602 type *d = (type *)buf; \
3603 for (x = 0; x < width; ++x) \
3604 d[x] = (type)color; \
3605 } while(0)
3607 switch (bpp)
3609 case 1:
3610 COLORFILL_ROW(BYTE);
3611 break;
3613 case 2:
3614 COLORFILL_ROW(WORD);
3615 break;
3617 case 3:
3619 BYTE *d = buf;
3620 for (x = 0; x < width; ++x, d += 3)
3622 d[0] = (color ) & 0xFF;
3623 d[1] = (color >> 8) & 0xFF;
3624 d[2] = (color >> 16) & 0xFF;
3626 break;
3628 case 4:
3629 COLORFILL_ROW(DWORD);
3630 break;
3632 default:
3633 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3634 return WINED3DERR_NOTAVAILABLE;
3637 #undef COLORFILL_ROW
3639 /* Now copy first row. */
3640 first = buf;
3641 for (y = 1; y < height; ++y)
3643 buf += pitch;
3644 memcpy(buf, first, width * bpp);
3647 return WINED3D_OK;
3650 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3652 TRACE("surface %p.\n", surface);
3654 if (!(surface->flags & SFLAG_LOCKED))
3656 WARN("Trying to unmap unmapped surface.\n");
3657 return WINEDDERR_NOTLOCKED;
3659 surface->flags &= ~SFLAG_LOCKED;
3661 surface->surface_ops->surface_unmap(surface);
3663 return WINED3D_OK;
3666 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3667 struct wined3d_mapped_rect *mapped_rect, const RECT *rect, DWORD flags)
3669 const struct wined3d_format *format = surface->resource.format;
3671 TRACE("surface %p, mapped_rect %p, rect %s, flags %#x.\n",
3672 surface, mapped_rect, wine_dbgstr_rect(rect), flags);
3674 if (surface->flags & SFLAG_LOCKED)
3676 WARN("Surface is already mapped.\n");
3677 return WINED3DERR_INVALIDCALL;
3679 if ((format->flags & WINED3DFMT_FLAG_BLOCKS)
3680 && rect && (rect->left || rect->top
3681 || rect->right != surface->resource.width
3682 || rect->bottom != surface->resource.height))
3684 UINT width_mask = format->block_width - 1;
3685 UINT height_mask = format->block_height - 1;
3687 if ((rect->left & width_mask) || (rect->right & width_mask)
3688 || (rect->top & height_mask) || (rect->bottom & height_mask))
3690 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3691 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3693 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3694 return WINED3DERR_INVALIDCALL;
3698 surface->flags |= SFLAG_LOCKED;
3700 if (!(surface->flags & SFLAG_LOCKABLE))
3701 WARN("Trying to lock unlockable surface.\n");
3703 /* Performance optimization: Count how often a surface is mapped, if it is
3704 * mapped regularly do not throw away the system memory copy. This avoids
3705 * the need to download the surface from OpenGL all the time. The surface
3706 * is still downloaded if the OpenGL texture is changed. */
3707 if (!(surface->flags & SFLAG_DYNLOCK))
3709 if (++surface->lockCount > MAXLOCKCOUNT)
3711 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3712 surface->flags |= SFLAG_DYNLOCK;
3716 surface->surface_ops->surface_map(surface, rect, flags);
3718 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3719 mapped_rect->row_pitch = surface->resource.width * format->byte_count;
3720 else
3721 mapped_rect->row_pitch = wined3d_surface_get_pitch(surface);
3723 if (!rect)
3725 mapped_rect->data = surface->resource.allocatedMemory;
3726 surface->lockedRect.left = 0;
3727 surface->lockedRect.top = 0;
3728 surface->lockedRect.right = surface->resource.width;
3729 surface->lockedRect.bottom = surface->resource.height;
3731 else
3733 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3735 /* Compressed textures are block based, so calculate the offset of
3736 * the block that contains the top-left pixel of the locked rectangle. */
3737 mapped_rect->data = surface->resource.allocatedMemory
3738 + ((rect->top / format->block_height) * mapped_rect->row_pitch)
3739 + ((rect->left / format->block_width) * format->block_byte_count);
3741 else
3743 mapped_rect->data = surface->resource.allocatedMemory
3744 + (mapped_rect->row_pitch * rect->top)
3745 + (rect->left * format->byte_count);
3747 surface->lockedRect.left = rect->left;
3748 surface->lockedRect.top = rect->top;
3749 surface->lockedRect.right = rect->right;
3750 surface->lockedRect.bottom = rect->bottom;
3753 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3754 TRACE("Returning memory %p, pitch %u.\n", mapped_rect->data, mapped_rect->row_pitch);
3756 return WINED3D_OK;
3759 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3761 struct wined3d_mapped_rect map;
3762 HRESULT hr;
3764 TRACE("surface %p, dc %p.\n", surface, dc);
3766 if (surface->flags & SFLAG_USERPTR)
3768 ERR("Not supported on surfaces with application-provided memory.\n");
3769 return WINEDDERR_NODC;
3772 /* Give more detailed info for ddraw. */
3773 if (surface->flags & SFLAG_DCINUSE)
3774 return WINEDDERR_DCALREADYCREATED;
3776 /* Can't GetDC if the surface is locked. */
3777 if (surface->flags & SFLAG_LOCKED)
3778 return WINED3DERR_INVALIDCALL;
3780 /* Create a DIB section if there isn't a dc yet. */
3781 if (!surface->hDC)
3783 if (surface->flags & SFLAG_CLIENT)
3785 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3786 surface_release_client_storage(surface);
3788 hr = surface_create_dib_section(surface);
3789 if (FAILED(hr))
3790 return WINED3DERR_INVALIDCALL;
3792 /* Use the DIB section from now on if we are not using a PBO. */
3793 if (!(surface->flags & SFLAG_PBO))
3794 surface->resource.allocatedMemory = surface->dib.bitmap_data;
3797 /* Map the surface. */
3798 hr = wined3d_surface_map(surface, &map, NULL, 0);
3799 if (FAILED(hr))
3801 ERR("Map failed, hr %#x.\n", hr);
3802 return hr;
3805 /* Sync the DIB with the PBO. This can't be done earlier because Map()
3806 * activates the allocatedMemory. */
3807 if (surface->flags & SFLAG_PBO)
3808 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
3810 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3811 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3813 /* GetDC on palettized formats is unsupported in D3D9, and the method
3814 * is missing in D3D8, so this should only be used for DX <=7
3815 * surfaces (with non-device palettes). */
3816 const PALETTEENTRY *pal = NULL;
3818 if (surface->palette)
3820 pal = surface->palette->palents;
3822 else
3824 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3825 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3827 if (dds_primary && dds_primary->palette)
3828 pal = dds_primary->palette->palents;
3831 if (pal)
3833 RGBQUAD col[256];
3834 unsigned int i;
3836 for (i = 0; i < 256; ++i)
3838 col[i].rgbRed = pal[i].peRed;
3839 col[i].rgbGreen = pal[i].peGreen;
3840 col[i].rgbBlue = pal[i].peBlue;
3841 col[i].rgbReserved = 0;
3843 SetDIBColorTable(surface->hDC, 0, 256, col);
3847 surface->flags |= SFLAG_DCINUSE;
3849 *dc = surface->hDC;
3850 TRACE("Returning dc %p.\n", *dc);
3852 return WINED3D_OK;
3855 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3857 TRACE("surface %p, dc %p.\n", surface, dc);
3859 if (!(surface->flags & SFLAG_DCINUSE))
3860 return WINEDDERR_NODC;
3862 if (surface->hDC != dc)
3864 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3865 dc, surface->hDC);
3866 return WINEDDERR_NODC;
3869 /* Copy the contents of the DIB over to the PBO. */
3870 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3871 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3873 /* We locked first, so unlock now. */
3874 wined3d_surface_unmap(surface);
3876 surface->flags &= ~SFLAG_DCINUSE;
3878 return WINED3D_OK;
3881 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3883 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3885 if (flags)
3887 static UINT once;
3888 if (!once++)
3889 FIXME("Ignoring flags %#x.\n", flags);
3890 else
3891 WARN("Ignoring flags %#x.\n", flags);
3894 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
3896 ERR("Not supported on swapchain surfaces.\n");
3897 return WINEDDERR_NOTFLIPPABLE;
3900 /* Flipping is only supported on render targets and overlays. */
3901 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
3903 WARN("Tried to flip a non-render target, non-overlay surface.\n");
3904 return WINEDDERR_NOTFLIPPABLE;
3907 flip_surface(surface, override);
3909 /* Update overlays if they're visible. */
3910 if ((surface->resource.usage & WINED3DUSAGE_OVERLAY) && surface->overlay_dest)
3911 return surface_draw_overlay(surface);
3913 return WINED3D_OK;
3916 /* Do not call while under the GL lock. */
3917 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3919 struct wined3d_device *device = surface->resource.device;
3921 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3923 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3925 struct wined3d_texture *texture = surface->container.u.texture;
3927 TRACE("Passing to container (%p).\n", texture);
3928 texture->texture_ops->texture_preload(texture, srgb);
3930 else
3932 struct wined3d_context *context;
3934 TRACE("(%p) : About to load surface\n", surface);
3936 /* TODO: Use already acquired context when possible. */
3937 context = context_acquire(device, NULL);
3939 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3941 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3943 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3944 GLclampf tmp;
3945 tmp = 0.9f;
3946 ENTER_GL();
3947 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3948 LEAVE_GL();
3951 context_release(context);
3955 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3957 if (!surface->resource.allocatedMemory)
3959 if (!surface->resource.heapMemory)
3961 if (!(surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3962 surface->resource.size + RESOURCE_ALIGNMENT)))
3964 ERR("Failed to allocate memory.\n");
3965 return FALSE;
3968 else if (!(surface->flags & SFLAG_CLIENT))
3970 ERR("Surface %p has heapMemory %p and flags %#x.\n",
3971 surface, surface->resource.heapMemory, surface->flags);
3974 surface->resource.allocatedMemory =
3975 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
3977 else
3979 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
3982 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3984 return TRUE;
3987 /* Read the framebuffer back into the surface */
3988 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
3990 struct wined3d_device *device = surface->resource.device;
3991 const struct wined3d_gl_info *gl_info;
3992 struct wined3d_context *context;
3993 BYTE *mem;
3994 GLint fmt;
3995 GLint type;
3996 BYTE *row, *top, *bottom;
3997 int i;
3998 BOOL bpp;
3999 RECT local_rect;
4000 BOOL srcIsUpsideDown;
4001 GLint rowLen = 0;
4002 GLint skipPix = 0;
4003 GLint skipRow = 0;
4005 context = context_acquire(device, surface);
4006 context_apply_blit_state(context, device);
4007 gl_info = context->gl_info;
4009 ENTER_GL();
4011 /* Select the correct read buffer, and give some debug output.
4012 * There is no need to keep track of the current read buffer or reset it, every part of the code
4013 * that reads sets the read buffer as desired.
4015 if (surface_is_offscreen(surface))
4017 /* Mapping the primary render target which is not on a swapchain.
4018 * Read from the back buffer. */
4019 TRACE("Mapping offscreen render target.\n");
4020 glReadBuffer(device->offscreenBuffer);
4021 srcIsUpsideDown = TRUE;
4023 else
4025 /* Onscreen surfaces are always part of a swapchain */
4026 GLenum buffer = surface_get_gl_buffer(surface);
4027 TRACE("Mapping %#x buffer.\n", buffer);
4028 glReadBuffer(buffer);
4029 checkGLcall("glReadBuffer");
4030 srcIsUpsideDown = FALSE;
4033 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
4034 if (!rect)
4036 local_rect.left = 0;
4037 local_rect.top = 0;
4038 local_rect.right = surface->resource.width;
4039 local_rect.bottom = surface->resource.height;
4041 else
4043 local_rect = *rect;
4045 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
4047 switch (surface->resource.format->id)
4049 case WINED3DFMT_P8_UINT:
4051 if (primary_render_target_is_p8(device))
4053 /* In case of P8 render targets the index is stored in the alpha component */
4054 fmt = GL_ALPHA;
4055 type = GL_UNSIGNED_BYTE;
4056 mem = dest;
4057 bpp = surface->resource.format->byte_count;
4059 else
4061 /* GL can't return palettized data, so read ARGB pixels into a
4062 * separate block of memory and convert them into palettized format
4063 * in software. Slow, but if the app means to use palettized render
4064 * targets and locks it...
4066 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4067 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4068 * for the color channels when palettizing the colors.
4070 fmt = GL_RGB;
4071 type = GL_UNSIGNED_BYTE;
4072 pitch *= 3;
4073 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4074 if (!mem)
4076 ERR("Out of memory\n");
4077 LEAVE_GL();
4078 return;
4080 bpp = surface->resource.format->byte_count * 3;
4083 break;
4085 default:
4086 mem = dest;
4087 fmt = surface->resource.format->glFormat;
4088 type = surface->resource.format->glType;
4089 bpp = surface->resource.format->byte_count;
4092 if (surface->flags & SFLAG_PBO)
4094 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4095 checkGLcall("glBindBufferARB");
4096 if (mem)
4098 ERR("mem not null for pbo -- unexpected\n");
4099 mem = NULL;
4103 /* Save old pixel store pack state */
4104 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4105 checkGLcall("glGetIntegerv");
4106 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4107 checkGLcall("glGetIntegerv");
4108 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4109 checkGLcall("glGetIntegerv");
4111 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4112 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4113 checkGLcall("glPixelStorei");
4114 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4115 checkGLcall("glPixelStorei");
4116 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4117 checkGLcall("glPixelStorei");
4119 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4120 local_rect.right - local_rect.left,
4121 local_rect.bottom - local_rect.top,
4122 fmt, type, mem);
4123 checkGLcall("glReadPixels");
4125 /* Reset previous pixel store pack state */
4126 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4127 checkGLcall("glPixelStorei");
4128 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4129 checkGLcall("glPixelStorei");
4130 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4131 checkGLcall("glPixelStorei");
4133 if (surface->flags & SFLAG_PBO)
4135 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4136 checkGLcall("glBindBufferARB");
4138 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4139 * to get a pointer to it and perform the flipping in software. This is a lot
4140 * faster than calling glReadPixels for each line. In case we want more speed
4141 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4142 if (!srcIsUpsideDown)
4144 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4145 checkGLcall("glBindBufferARB");
4147 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4148 checkGLcall("glMapBufferARB");
4152 /* TODO: Merge this with the palettization loop below for P8 targets */
4153 if(!srcIsUpsideDown) {
4154 UINT len, off;
4155 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4156 Flip the lines in software */
4157 len = (local_rect.right - local_rect.left) * bpp;
4158 off = local_rect.left * bpp;
4160 row = HeapAlloc(GetProcessHeap(), 0, len);
4161 if(!row) {
4162 ERR("Out of memory\n");
4163 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4164 HeapFree(GetProcessHeap(), 0, mem);
4165 LEAVE_GL();
4166 return;
4169 top = mem + pitch * local_rect.top;
4170 bottom = mem + pitch * (local_rect.bottom - 1);
4171 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4172 memcpy(row, top + off, len);
4173 memcpy(top + off, bottom + off, len);
4174 memcpy(bottom + off, row, len);
4175 top += pitch;
4176 bottom -= pitch;
4178 HeapFree(GetProcessHeap(), 0, row);
4180 /* Unmap the temp PBO buffer */
4181 if (surface->flags & SFLAG_PBO)
4183 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4184 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4188 LEAVE_GL();
4189 context_release(context);
4191 /* For P8 textures we need to perform an inverse palette lookup. This is
4192 * done by searching for a palette index which matches the RGB value.
4193 * Note this isn't guaranteed to work when there are multiple entries for
4194 * the same color but we have no choice. In case of P8 render targets,
4195 * the index is stored in the alpha component so no conversion is needed. */
4196 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4198 const PALETTEENTRY *pal = NULL;
4199 DWORD width = pitch / 3;
4200 int x, y, c;
4202 if (surface->palette)
4204 pal = surface->palette->palents;
4206 else
4208 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4209 HeapFree(GetProcessHeap(), 0, mem);
4210 return;
4213 for(y = local_rect.top; y < local_rect.bottom; y++) {
4214 for(x = local_rect.left; x < local_rect.right; x++) {
4215 /* start lines pixels */
4216 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4217 const BYTE *green = blue + 1;
4218 const BYTE *red = green + 1;
4220 for(c = 0; c < 256; c++) {
4221 if(*red == pal[c].peRed &&
4222 *green == pal[c].peGreen &&
4223 *blue == pal[c].peBlue)
4225 *((BYTE *) dest + y * width + x) = c;
4226 break;
4231 HeapFree(GetProcessHeap(), 0, mem);
4235 /* Read the framebuffer contents into a texture. Note that this function
4236 * doesn't do any kind of flipping. Using this on an onscreen surface will
4237 * result in a flipped D3D texture. */
4238 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
4240 struct wined3d_device *device = surface->resource.device;
4241 struct wined3d_context *context;
4243 context = context_acquire(device, surface);
4244 device_invalidate_state(device, STATE_FRAMEBUFFER);
4246 surface_prepare_texture(surface, context, srgb);
4247 surface_bind_and_dirtify(surface, context, srgb);
4249 TRACE("Reading back offscreen render target %p.\n", surface);
4251 ENTER_GL();
4253 if (surface_is_offscreen(surface))
4254 glReadBuffer(device->offscreenBuffer);
4255 else
4256 glReadBuffer(surface_get_gl_buffer(surface));
4257 checkGLcall("glReadBuffer");
4259 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4260 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4261 checkGLcall("glCopyTexSubImage2D");
4263 LEAVE_GL();
4265 context_release(context);
4268 /* Context activation is done by the caller. */
4269 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4270 struct wined3d_context *context, BOOL srgb)
4272 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4273 CONVERT_TYPES convert;
4274 struct wined3d_format format;
4276 if (surface->flags & alloc_flag) return;
4278 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4279 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4280 else surface->flags &= ~SFLAG_CONVERTED;
4282 surface_bind_and_dirtify(surface, context, srgb);
4283 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4284 surface->flags |= alloc_flag;
4287 /* Context activation is done by the caller. */
4288 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4290 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4292 struct wined3d_texture *texture = surface->container.u.texture;
4293 UINT sub_count = texture->level_count * texture->layer_count;
4294 UINT i;
4296 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4298 for (i = 0; i < sub_count; ++i)
4300 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4301 surface_prepare_texture_internal(s, context, srgb);
4304 return;
4307 surface_prepare_texture_internal(surface, context, srgb);
4310 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4312 if (multisample)
4314 if (surface->rb_multisample)
4315 return;
4317 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4318 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4319 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4320 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4321 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4323 else
4325 if (surface->rb_resolved)
4326 return;
4328 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4329 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4330 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4331 surface->pow2Width, surface->pow2Height);
4332 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4336 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4337 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4339 struct wined3d_device *device = surface->resource.device;
4340 UINT pitch = wined3d_surface_get_pitch(surface);
4341 const struct wined3d_gl_info *gl_info;
4342 struct wined3d_context *context;
4343 RECT local_rect;
4344 UINT w, h;
4346 surface_get_rect(surface, rect, &local_rect);
4348 mem += local_rect.top * pitch + local_rect.left * bpp;
4349 w = local_rect.right - local_rect.left;
4350 h = local_rect.bottom - local_rect.top;
4352 /* Activate the correct context for the render target */
4353 context = context_acquire(device, surface);
4354 context_apply_blit_state(context, device);
4355 gl_info = context->gl_info;
4357 ENTER_GL();
4359 if (!surface_is_offscreen(surface))
4361 GLenum buffer = surface_get_gl_buffer(surface);
4362 TRACE("Unlocking %#x buffer.\n", buffer);
4363 context_set_draw_buffer(context, buffer);
4365 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4366 glPixelZoom(1.0f, -1.0f);
4368 else
4370 /* Primary offscreen render target */
4371 TRACE("Offscreen render target.\n");
4372 context_set_draw_buffer(context, device->offscreenBuffer);
4374 glPixelZoom(1.0f, 1.0f);
4377 glRasterPos3i(local_rect.left, local_rect.top, 1);
4378 checkGLcall("glRasterPos3i");
4380 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4381 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4383 if (surface->flags & SFLAG_PBO)
4385 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4386 checkGLcall("glBindBufferARB");
4389 glDrawPixels(w, h, fmt, type, mem);
4390 checkGLcall("glDrawPixels");
4392 if (surface->flags & SFLAG_PBO)
4394 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4395 checkGLcall("glBindBufferARB");
4398 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4399 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4401 LEAVE_GL();
4403 if (wined3d_settings.strict_draw_ordering
4404 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4405 && surface->container.u.swapchain->front_buffer == surface))
4406 wglFlush();
4408 context_release(context);
4411 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4412 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4414 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4415 const struct wined3d_device *device = surface->resource.device;
4416 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4417 BOOL blit_supported = FALSE;
4419 /* Copy the default values from the surface. Below we might perform fixups */
4420 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4421 *format = *surface->resource.format;
4422 *convert = NO_CONVERSION;
4424 /* Ok, now look if we have to do any conversion */
4425 switch (surface->resource.format->id)
4427 case WINED3DFMT_P8_UINT:
4428 /* Below the call to blit_supported is disabled for Wine 1.2
4429 * because the function isn't operating correctly yet. At the
4430 * moment 8-bit blits are handled in software and if certain GL
4431 * extensions are around, surface conversion is performed at
4432 * upload time. The blit_supported call recognizes it as a
4433 * destination fixup. This type of upload 'fixup' and 8-bit to
4434 * 8-bit blits need to be handled by the blit_shader.
4435 * TODO: get rid of this #if 0. */
4436 #if 0
4437 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4438 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4439 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4440 #endif
4441 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4443 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4444 * texturing. Further also use conversion in case of color keying.
4445 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4446 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4447 * conflicts with this.
4449 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4450 || colorkey_active || !use_texturing)
4452 format->glFormat = GL_RGBA;
4453 format->glInternal = GL_RGBA;
4454 format->glType = GL_UNSIGNED_BYTE;
4455 format->conv_byte_count = 4;
4456 if (colorkey_active)
4457 *convert = CONVERT_PALETTED_CK;
4458 else
4459 *convert = CONVERT_PALETTED;
4461 break;
4463 case WINED3DFMT_B2G3R3_UNORM:
4464 /* **********************
4465 GL_UNSIGNED_BYTE_3_3_2
4466 ********************** */
4467 if (colorkey_active) {
4468 /* This texture format will never be used.. So do not care about color keying
4469 up until the point in time it will be needed :-) */
4470 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4472 break;
4474 case WINED3DFMT_B5G6R5_UNORM:
4475 if (colorkey_active)
4477 *convert = CONVERT_CK_565;
4478 format->glFormat = GL_RGBA;
4479 format->glInternal = GL_RGB5_A1;
4480 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4481 format->conv_byte_count = 2;
4483 break;
4485 case WINED3DFMT_B5G5R5X1_UNORM:
4486 if (colorkey_active)
4488 *convert = CONVERT_CK_5551;
4489 format->glFormat = GL_BGRA;
4490 format->glInternal = GL_RGB5_A1;
4491 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4492 format->conv_byte_count = 2;
4494 break;
4496 case WINED3DFMT_B8G8R8_UNORM:
4497 if (colorkey_active)
4499 *convert = CONVERT_CK_RGB24;
4500 format->glFormat = GL_RGBA;
4501 format->glInternal = GL_RGBA8;
4502 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4503 format->conv_byte_count = 4;
4505 break;
4507 case WINED3DFMT_B8G8R8X8_UNORM:
4508 if (colorkey_active)
4510 *convert = CONVERT_RGB32_888;
4511 format->glFormat = GL_RGBA;
4512 format->glInternal = GL_RGBA8;
4513 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4514 format->conv_byte_count = 4;
4516 break;
4518 default:
4519 break;
4522 return WINED3D_OK;
4525 static BOOL color_in_range(const struct wined3d_color_key *color_key, DWORD color)
4527 /* FIXME: Is this really how color keys are supposed to work? I think it
4528 * makes more sense to compare the individual channels. */
4529 return color >= color_key->color_space_low_value
4530 && color <= color_key->color_space_high_value;
4533 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4535 const struct wined3d_device *device = surface->resource.device;
4536 const struct wined3d_palette *pal = surface->palette;
4537 BOOL index_in_alpha = FALSE;
4538 unsigned int i;
4540 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4541 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4542 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4543 * duplicate entries. Store the color key in the unused alpha component to speed the
4544 * download up and to make conversion unneeded. */
4545 index_in_alpha = primary_render_target_is_p8(device);
4547 if (!pal)
4549 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4550 if (index_in_alpha)
4552 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4553 * there's no palette at this time. */
4554 for (i = 0; i < 256; i++) table[i][3] = i;
4557 else
4559 TRACE("Using surface palette %p\n", pal);
4560 /* Get the surface's palette */
4561 for (i = 0; i < 256; ++i)
4563 table[i][0] = pal->palents[i].peRed;
4564 table[i][1] = pal->palents[i].peGreen;
4565 table[i][2] = pal->palents[i].peBlue;
4567 /* When index_in_alpha is set the palette index is stored in the
4568 * alpha component. In case of a readback we can then read
4569 * GL_ALPHA. Color keying is handled in BltOverride using a
4570 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4571 * color key itself is passed to glAlphaFunc in other cases the
4572 * alpha component of pixels that should be masked away is set to 0. */
4573 if (index_in_alpha)
4574 table[i][3] = i;
4575 else if (colorkey && color_in_range(&surface->src_blt_color_key, i))
4576 table[i][3] = 0x00;
4577 else if (pal->flags & WINEDDPCAPS_ALPHA)
4578 table[i][3] = pal->palents[i].peFlags;
4579 else
4580 table[i][3] = 0xFF;
4585 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4586 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4588 const BYTE *source;
4589 BYTE *dest;
4590 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4592 switch (convert) {
4593 case NO_CONVERSION:
4595 memcpy(dst, src, pitch * height);
4596 break;
4598 case CONVERT_PALETTED:
4599 case CONVERT_PALETTED_CK:
4601 BYTE table[256][4];
4602 unsigned int x, y;
4604 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4606 for (y = 0; y < height; y++)
4608 source = src + pitch * y;
4609 dest = dst + outpitch * y;
4610 /* This is an 1 bpp format, using the width here is fine */
4611 for (x = 0; x < width; x++) {
4612 BYTE color = *source++;
4613 *dest++ = table[color][0];
4614 *dest++ = table[color][1];
4615 *dest++ = table[color][2];
4616 *dest++ = table[color][3];
4620 break;
4622 case CONVERT_CK_565:
4624 /* Converting the 565 format in 5551 packed to emulate color-keying.
4626 Note : in all these conversion, it would be best to average the averaging
4627 pixels to get the color of the pixel that will be color-keyed to
4628 prevent 'color bleeding'. This will be done later on if ever it is
4629 too visible.
4631 Note2: Nvidia documents say that their driver does not support alpha + color keying
4632 on the same surface and disables color keying in such a case
4634 unsigned int x, y;
4635 const WORD *Source;
4636 WORD *Dest;
4638 TRACE("Color keyed 565\n");
4640 for (y = 0; y < height; y++) {
4641 Source = (const WORD *)(src + y * pitch);
4642 Dest = (WORD *) (dst + y * outpitch);
4643 for (x = 0; x < width; x++ ) {
4644 WORD color = *Source++;
4645 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4646 if (!color_in_range(&surface->src_blt_color_key, color))
4647 *Dest |= 0x0001;
4648 Dest++;
4652 break;
4654 case CONVERT_CK_5551:
4656 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4657 unsigned int x, y;
4658 const WORD *Source;
4659 WORD *Dest;
4660 TRACE("Color keyed 5551\n");
4661 for (y = 0; y < height; y++) {
4662 Source = (const WORD *)(src + y * pitch);
4663 Dest = (WORD *) (dst + y * outpitch);
4664 for (x = 0; x < width; x++ ) {
4665 WORD color = *Source++;
4666 *Dest = color;
4667 if (!color_in_range(&surface->src_blt_color_key, color))
4668 *Dest |= (1 << 15);
4669 else
4670 *Dest &= ~(1 << 15);
4671 Dest++;
4675 break;
4677 case CONVERT_CK_RGB24:
4679 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4680 unsigned int x, y;
4681 for (y = 0; y < height; y++)
4683 source = src + pitch * y;
4684 dest = dst + outpitch * y;
4685 for (x = 0; x < width; x++) {
4686 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4687 DWORD dstcolor = color << 8;
4688 if (!color_in_range(&surface->src_blt_color_key, color))
4689 dstcolor |= 0xff;
4690 *(DWORD*)dest = dstcolor;
4691 source += 3;
4692 dest += 4;
4696 break;
4698 case CONVERT_RGB32_888:
4700 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4701 unsigned int x, y;
4702 for (y = 0; y < height; y++)
4704 source = src + pitch * y;
4705 dest = dst + outpitch * y;
4706 for (x = 0; x < width; x++) {
4707 DWORD color = 0xffffff & *(const DWORD*)source;
4708 DWORD dstcolor = color << 8;
4709 if (!color_in_range(&surface->src_blt_color_key, color))
4710 dstcolor |= 0xff;
4711 *(DWORD*)dest = dstcolor;
4712 source += 4;
4713 dest += 4;
4717 break;
4719 default:
4720 ERR("Unsupported conversion type %#x.\n", convert);
4722 return WINED3D_OK;
4725 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4727 /* Flip the surface contents */
4728 /* Flip the DC */
4730 HDC tmp;
4731 tmp = front->hDC;
4732 front->hDC = back->hDC;
4733 back->hDC = tmp;
4736 /* Flip the DIBsection */
4738 HBITMAP tmp;
4739 BOOL hasDib = front->flags & SFLAG_DIBSECTION;
4740 tmp = front->dib.DIBsection;
4741 front->dib.DIBsection = back->dib.DIBsection;
4742 back->dib.DIBsection = tmp;
4744 if (back->flags & SFLAG_DIBSECTION) front->flags |= SFLAG_DIBSECTION;
4745 else front->flags &= ~SFLAG_DIBSECTION;
4746 if (hasDib) back->flags |= SFLAG_DIBSECTION;
4747 else back->flags &= ~SFLAG_DIBSECTION;
4750 /* Flip the surface data */
4752 void* tmp;
4754 tmp = front->dib.bitmap_data;
4755 front->dib.bitmap_data = back->dib.bitmap_data;
4756 back->dib.bitmap_data = tmp;
4758 tmp = front->resource.allocatedMemory;
4759 front->resource.allocatedMemory = back->resource.allocatedMemory;
4760 back->resource.allocatedMemory = tmp;
4762 tmp = front->resource.heapMemory;
4763 front->resource.heapMemory = back->resource.heapMemory;
4764 back->resource.heapMemory = tmp;
4767 /* Flip the PBO */
4769 GLuint tmp_pbo = front->pbo;
4770 front->pbo = back->pbo;
4771 back->pbo = tmp_pbo;
4774 /* Flip the opengl texture */
4776 GLuint tmp;
4778 tmp = back->texture_name;
4779 back->texture_name = front->texture_name;
4780 front->texture_name = tmp;
4782 tmp = back->texture_name_srgb;
4783 back->texture_name_srgb = front->texture_name_srgb;
4784 front->texture_name_srgb = tmp;
4786 tmp = back->rb_multisample;
4787 back->rb_multisample = front->rb_multisample;
4788 front->rb_multisample = tmp;
4790 tmp = back->rb_resolved;
4791 back->rb_resolved = front->rb_resolved;
4792 front->rb_resolved = tmp;
4794 resource_unload(&back->resource);
4795 resource_unload(&front->resource);
4799 DWORD tmp_flags = back->flags;
4800 back->flags = front->flags;
4801 front->flags = tmp_flags;
4805 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4806 * pixel copy calls. */
4807 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4808 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4810 struct wined3d_device *device = dst_surface->resource.device;
4811 float xrel, yrel;
4812 UINT row;
4813 struct wined3d_context *context;
4814 BOOL upsidedown = FALSE;
4815 RECT dst_rect = *dst_rect_in;
4817 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4818 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4820 if(dst_rect.top > dst_rect.bottom) {
4821 UINT tmp = dst_rect.bottom;
4822 dst_rect.bottom = dst_rect.top;
4823 dst_rect.top = tmp;
4824 upsidedown = TRUE;
4827 context = context_acquire(device, src_surface);
4828 context_apply_blit_state(context, device);
4829 surface_internal_preload(dst_surface, SRGB_RGB);
4830 ENTER_GL();
4832 /* Bind the target texture */
4833 context_bind_texture(context, dst_surface->texture_target, dst_surface->texture_name);
4834 if (surface_is_offscreen(src_surface))
4836 TRACE("Reading from an offscreen target\n");
4837 upsidedown = !upsidedown;
4838 glReadBuffer(device->offscreenBuffer);
4840 else
4842 glReadBuffer(surface_get_gl_buffer(src_surface));
4844 checkGLcall("glReadBuffer");
4846 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4847 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4849 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4851 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4853 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4854 ERR("Texture filtering not supported in direct blit\n");
4857 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4858 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4860 ERR("Texture filtering not supported in direct blit\n");
4863 if (upsidedown
4864 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4865 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4867 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4869 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4870 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4871 src_rect->left, src_surface->resource.height - src_rect->bottom,
4872 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4874 else
4876 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4877 /* I have to process this row by row to swap the image,
4878 * otherwise it would be upside down, so stretching in y direction
4879 * doesn't cost extra time
4881 * However, stretching in x direction can be avoided if not necessary
4883 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4884 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4886 /* Well, that stuff works, but it's very slow.
4887 * find a better way instead
4889 UINT col;
4891 for (col = dst_rect.left; col < dst_rect.right; ++col)
4893 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4894 dst_rect.left + col /* x offset */, row /* y offset */,
4895 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4898 else
4900 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4901 dst_rect.left /* x offset */, row /* y offset */,
4902 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4906 checkGLcall("glCopyTexSubImage2D");
4908 LEAVE_GL();
4909 context_release(context);
4911 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4912 * path is never entered
4914 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4917 /* Uses the hardware to stretch and flip the image */
4918 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4919 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4921 struct wined3d_device *device = dst_surface->resource.device;
4922 struct wined3d_swapchain *src_swapchain = NULL;
4923 GLuint src, backup = 0;
4924 float left, right, top, bottom; /* Texture coordinates */
4925 UINT fbwidth = src_surface->resource.width;
4926 UINT fbheight = src_surface->resource.height;
4927 struct wined3d_context *context;
4928 GLenum drawBuffer = GL_BACK;
4929 GLenum texture_target;
4930 BOOL noBackBufferBackup;
4931 BOOL src_offscreen;
4932 BOOL upsidedown = FALSE;
4933 RECT dst_rect = *dst_rect_in;
4935 TRACE("Using hwstretch blit\n");
4936 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4937 context = context_acquire(device, src_surface);
4938 context_apply_blit_state(context, device);
4939 surface_internal_preload(dst_surface, SRGB_RGB);
4941 src_offscreen = surface_is_offscreen(src_surface);
4942 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4943 if (!noBackBufferBackup && !src_surface->texture_name)
4945 /* Get it a description */
4946 surface_internal_preload(src_surface, SRGB_RGB);
4948 ENTER_GL();
4950 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4951 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4953 if (context->aux_buffers >= 2)
4955 /* Got more than one aux buffer? Use the 2nd aux buffer */
4956 drawBuffer = GL_AUX1;
4958 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4960 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4961 drawBuffer = GL_AUX0;
4964 if(noBackBufferBackup) {
4965 glGenTextures(1, &backup);
4966 checkGLcall("glGenTextures");
4967 context_bind_texture(context, GL_TEXTURE_2D, backup);
4968 texture_target = GL_TEXTURE_2D;
4969 } else {
4970 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4971 * we are reading from the back buffer, the backup can be used as source texture
4973 texture_target = src_surface->texture_target;
4974 context_bind_texture(context, texture_target, src_surface->texture_name);
4975 glEnable(texture_target);
4976 checkGLcall("glEnable(texture_target)");
4978 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4979 src_surface->flags &= ~SFLAG_INTEXTURE;
4982 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4983 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4985 if(dst_rect.top > dst_rect.bottom) {
4986 UINT tmp = dst_rect.bottom;
4987 dst_rect.bottom = dst_rect.top;
4988 dst_rect.top = tmp;
4989 upsidedown = TRUE;
4992 if (src_offscreen)
4994 TRACE("Reading from an offscreen target\n");
4995 upsidedown = !upsidedown;
4996 glReadBuffer(device->offscreenBuffer);
4998 else
5000 glReadBuffer(surface_get_gl_buffer(src_surface));
5003 /* TODO: Only back up the part that will be overwritten */
5004 glCopyTexSubImage2D(texture_target, 0,
5005 0, 0 /* read offsets */,
5006 0, 0,
5007 fbwidth,
5008 fbheight);
5010 checkGLcall("glCopyTexSubImage2D");
5012 /* No issue with overriding these - the sampler is dirty due to blit usage */
5013 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
5014 wined3d_gl_mag_filter(magLookup, Filter));
5015 checkGLcall("glTexParameteri");
5016 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5017 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
5018 checkGLcall("glTexParameteri");
5020 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5021 src_swapchain = src_surface->container.u.swapchain;
5022 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5024 src = backup ? backup : src_surface->texture_name;
5026 else
5028 glReadBuffer(GL_FRONT);
5029 checkGLcall("glReadBuffer(GL_FRONT)");
5031 glGenTextures(1, &src);
5032 checkGLcall("glGenTextures(1, &src)");
5033 context_bind_texture(context, GL_TEXTURE_2D, src);
5035 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5036 * out for power of 2 sizes
5038 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5039 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5040 checkGLcall("glTexImage2D");
5041 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5042 0, 0 /* read offsets */,
5043 0, 0,
5044 fbwidth,
5045 fbheight);
5047 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5048 checkGLcall("glTexParameteri");
5049 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5050 checkGLcall("glTexParameteri");
5052 glReadBuffer(GL_BACK);
5053 checkGLcall("glReadBuffer(GL_BACK)");
5055 if(texture_target != GL_TEXTURE_2D) {
5056 glDisable(texture_target);
5057 glEnable(GL_TEXTURE_2D);
5058 texture_target = GL_TEXTURE_2D;
5061 checkGLcall("glEnd and previous");
5063 left = src_rect->left;
5064 right = src_rect->right;
5066 if (!upsidedown)
5068 top = src_surface->resource.height - src_rect->top;
5069 bottom = src_surface->resource.height - src_rect->bottom;
5071 else
5073 top = src_surface->resource.height - src_rect->bottom;
5074 bottom = src_surface->resource.height - src_rect->top;
5077 if (src_surface->flags & SFLAG_NORMCOORD)
5079 left /= src_surface->pow2Width;
5080 right /= src_surface->pow2Width;
5081 top /= src_surface->pow2Height;
5082 bottom /= src_surface->pow2Height;
5085 /* draw the source texture stretched and upside down. The correct surface is bound already */
5086 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5087 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5089 context_set_draw_buffer(context, drawBuffer);
5090 glReadBuffer(drawBuffer);
5092 glBegin(GL_QUADS);
5093 /* bottom left */
5094 glTexCoord2f(left, bottom);
5095 glVertex2i(0, 0);
5097 /* top left */
5098 glTexCoord2f(left, top);
5099 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5101 /* top right */
5102 glTexCoord2f(right, top);
5103 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5105 /* bottom right */
5106 glTexCoord2f(right, bottom);
5107 glVertex2i(dst_rect.right - dst_rect.left, 0);
5108 glEnd();
5109 checkGLcall("glEnd and previous");
5111 if (texture_target != dst_surface->texture_target)
5113 glDisable(texture_target);
5114 glEnable(dst_surface->texture_target);
5115 texture_target = dst_surface->texture_target;
5118 /* Now read the stretched and upside down image into the destination texture */
5119 context_bind_texture(context, texture_target, dst_surface->texture_name);
5120 glCopyTexSubImage2D(texture_target,
5122 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5123 0, 0, /* We blitted the image to the origin */
5124 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5125 checkGLcall("glCopyTexSubImage2D");
5127 if(drawBuffer == GL_BACK) {
5128 /* Write the back buffer backup back */
5129 if(backup) {
5130 if(texture_target != GL_TEXTURE_2D) {
5131 glDisable(texture_target);
5132 glEnable(GL_TEXTURE_2D);
5133 texture_target = GL_TEXTURE_2D;
5135 context_bind_texture(context, GL_TEXTURE_2D, backup);
5137 else
5139 if (texture_target != src_surface->texture_target)
5141 glDisable(texture_target);
5142 glEnable(src_surface->texture_target);
5143 texture_target = src_surface->texture_target;
5145 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5148 glBegin(GL_QUADS);
5149 /* top left */
5150 glTexCoord2f(0.0f, 0.0f);
5151 glVertex2i(0, fbheight);
5153 /* bottom left */
5154 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5155 glVertex2i(0, 0);
5157 /* bottom right */
5158 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5159 (float)fbheight / (float)src_surface->pow2Height);
5160 glVertex2i(fbwidth, 0);
5162 /* top right */
5163 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5164 glVertex2i(fbwidth, fbheight);
5165 glEnd();
5167 glDisable(texture_target);
5168 checkGLcall("glDisable(texture_target)");
5170 /* Cleanup */
5171 if (src != src_surface->texture_name && src != backup)
5173 glDeleteTextures(1, &src);
5174 checkGLcall("glDeleteTextures(1, &src)");
5176 if(backup) {
5177 glDeleteTextures(1, &backup);
5178 checkGLcall("glDeleteTextures(1, &backup)");
5181 LEAVE_GL();
5183 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5185 context_release(context);
5187 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5188 * path is never entered
5190 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5193 /* Front buffer coordinates are always full screen coordinates, but our GL
5194 * drawable is limited to the window's client area. The sysmem and texture
5195 * copies do have the full screen size. Note that GL has a bottom-left
5196 * origin, while D3D has a top-left origin. */
5197 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5199 UINT drawable_height;
5201 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5202 && surface == surface->container.u.swapchain->front_buffer)
5204 POINT offset = {0, 0};
5205 RECT windowsize;
5207 ScreenToClient(window, &offset);
5208 OffsetRect(rect, offset.x, offset.y);
5210 GetClientRect(window, &windowsize);
5211 drawable_height = windowsize.bottom - windowsize.top;
5213 else
5215 drawable_height = surface->resource.height;
5218 rect->top = drawable_height - rect->top;
5219 rect->bottom = drawable_height - rect->bottom;
5222 static void surface_blt_to_drawable(const struct wined3d_device *device,
5223 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5224 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5225 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5227 struct wined3d_context *context;
5228 RECT src_rect, dst_rect;
5230 src_rect = *src_rect_in;
5231 dst_rect = *dst_rect_in;
5233 /* Make sure the surface is up-to-date. This should probably use
5234 * surface_load_location() and worry about the destination surface too,
5235 * unless we're overwriting it completely. */
5236 surface_internal_preload(src_surface, SRGB_RGB);
5238 /* Activate the destination context, set it up for blitting */
5239 context = context_acquire(device, dst_surface);
5240 context_apply_blit_state(context, device);
5242 if (!surface_is_offscreen(dst_surface))
5243 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5245 device->blitter->set_shader(device->blit_priv, context, src_surface);
5247 ENTER_GL();
5249 if (color_key)
5251 glEnable(GL_ALPHA_TEST);
5252 checkGLcall("glEnable(GL_ALPHA_TEST)");
5254 /* When the primary render target uses P8, the alpha component
5255 * contains the palette index. Which means that the colorkey is one of
5256 * the palette entries. In other cases pixels that should be masked
5257 * away have alpha set to 0. */
5258 if (primary_render_target_is_p8(device))
5259 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->src_blt_color_key.color_space_low_value / 256.0f);
5260 else
5261 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5262 checkGLcall("glAlphaFunc");
5264 else
5266 glDisable(GL_ALPHA_TEST);
5267 checkGLcall("glDisable(GL_ALPHA_TEST)");
5270 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5272 if (color_key)
5274 glDisable(GL_ALPHA_TEST);
5275 checkGLcall("glDisable(GL_ALPHA_TEST)");
5278 LEAVE_GL();
5280 /* Leave the opengl state valid for blitting */
5281 device->blitter->unset_shader(context->gl_info);
5283 if (wined3d_settings.strict_draw_ordering
5284 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5285 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5286 wglFlush(); /* Flush to ensure ordering across contexts. */
5288 context_release(context);
5291 /* Do not call while under the GL lock. */
5292 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
5294 struct wined3d_device *device = s->resource.device;
5295 const struct blit_shader *blitter;
5297 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5298 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5299 if (!blitter)
5301 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5302 return WINED3DERR_INVALIDCALL;
5305 return blitter->color_fill(device, s, rect, color);
5308 /* Do not call while under the GL lock. */
5309 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5310 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5311 WINED3DTEXTUREFILTERTYPE Filter)
5313 struct wined3d_device *device = dst_surface->resource.device;
5314 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5315 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5317 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5318 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5319 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5321 /* Get the swapchain. One of the surfaces has to be a primary surface */
5322 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5324 WARN("Destination is in sysmem, rejecting gl blt\n");
5325 return WINED3DERR_INVALIDCALL;
5328 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5329 dstSwapchain = dst_surface->container.u.swapchain;
5331 if (src_surface)
5333 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5335 WARN("Src is in sysmem, rejecting gl blt\n");
5336 return WINED3DERR_INVALIDCALL;
5339 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5340 srcSwapchain = src_surface->container.u.swapchain;
5343 /* Early sort out of cases where no render target is used */
5344 if (!dstSwapchain && !srcSwapchain
5345 && src_surface != device->fb.render_targets[0]
5346 && dst_surface != device->fb.render_targets[0])
5348 TRACE("No surface is render target, not using hardware blit.\n");
5349 return WINED3DERR_INVALIDCALL;
5352 /* No destination color keying supported */
5353 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5355 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5356 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5357 return WINED3DERR_INVALIDCALL;
5360 if (dstSwapchain && dstSwapchain == srcSwapchain)
5362 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5363 return WINED3DERR_INVALIDCALL;
5366 if (dstSwapchain && srcSwapchain)
5368 FIXME("Implement hardware blit between two different swapchains\n");
5369 return WINED3DERR_INVALIDCALL;
5372 if (dstSwapchain)
5374 /* Handled with regular texture -> swapchain blit */
5375 if (src_surface == device->fb.render_targets[0])
5376 TRACE("Blit from active render target to a swapchain\n");
5378 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5380 FIXME("Implement blit from a swapchain to the active render target\n");
5381 return WINED3DERR_INVALIDCALL;
5384 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5386 /* Blit from render target to texture */
5387 BOOL stretchx;
5389 /* P8 read back is not implemented */
5390 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5391 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5393 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5394 return WINED3DERR_INVALIDCALL;
5397 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5399 TRACE("Color keying not supported by frame buffer to texture blit\n");
5400 return WINED3DERR_INVALIDCALL;
5401 /* Destination color key is checked above */
5404 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5405 stretchx = TRUE;
5406 else
5407 stretchx = FALSE;
5409 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5410 * flip the image nor scale it.
5412 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5413 * -> If the app wants a image width an unscaled width, copy it line per line
5414 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5415 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5416 * back buffer. This is slower than reading line per line, thus not used for flipping
5417 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5418 * pixel by pixel. */
5419 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5420 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5422 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5423 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, Filter);
5424 } else {
5425 TRACE("Using hardware stretching to flip / stretch the texture\n");
5426 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5429 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5431 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5432 dst_surface->resource.allocatedMemory = NULL;
5433 dst_surface->resource.heapMemory = NULL;
5435 else
5437 dst_surface->flags &= ~SFLAG_INSYSMEM;
5440 return WINED3D_OK;
5442 else if (src_surface)
5444 /* Blit from offscreen surface to render target */
5445 struct wined3d_color_key old_blt_key = src_surface->src_blt_color_key;
5446 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5448 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5450 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5451 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5452 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5454 FIXME("Unsupported blit operation falling back to software\n");
5455 return WINED3DERR_INVALIDCALL;
5458 /* Color keying: Check if we have to do a color keyed blt,
5459 * and if not check if a color key is activated.
5461 * Just modify the color keying parameters in the surface and restore them afterwards
5462 * The surface keeps track of the color key last used to load the opengl surface.
5463 * PreLoad will catch the change to the flags and color key and reload if necessary.
5465 if (flags & WINEDDBLT_KEYSRC)
5467 /* Use color key from surface */
5469 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5471 /* Use color key from DDBltFx */
5472 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5473 src_surface->src_blt_color_key = DDBltFx->ddckSrcColorkey;
5475 else
5477 /* Do not use color key */
5478 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5481 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5482 src_surface, src_rect, dst_surface, dst_rect);
5484 /* Restore the color key parameters */
5485 src_surface->CKeyFlags = oldCKeyFlags;
5486 src_surface->src_blt_color_key = old_blt_key;
5488 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5490 return WINED3D_OK;
5493 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5494 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5495 return WINED3DERR_INVALIDCALL;
5498 /* GL locking is done by the caller */
5499 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5500 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5502 struct wined3d_device *device = surface->resource.device;
5503 const struct wined3d_gl_info *gl_info = context->gl_info;
5504 GLint compare_mode = GL_NONE;
5505 struct blt_info info;
5506 GLint old_binding = 0;
5507 RECT rect;
5509 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5511 glDisable(GL_CULL_FACE);
5512 glDisable(GL_BLEND);
5513 glDisable(GL_ALPHA_TEST);
5514 glDisable(GL_SCISSOR_TEST);
5515 glDisable(GL_STENCIL_TEST);
5516 glEnable(GL_DEPTH_TEST);
5517 glDepthFunc(GL_ALWAYS);
5518 glDepthMask(GL_TRUE);
5519 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5520 glViewport(x, y, w, h);
5522 SetRect(&rect, 0, h, w, 0);
5523 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5524 context_active_texture(context, context->gl_info, 0);
5525 glGetIntegerv(info.binding, &old_binding);
5526 glBindTexture(info.bind_target, texture);
5527 if (gl_info->supported[ARB_SHADOW])
5529 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5530 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5533 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5534 gl_info, info.tex_type, &surface->ds_current_size);
5536 glBegin(GL_TRIANGLE_STRIP);
5537 glTexCoord3fv(info.coords[0]);
5538 glVertex2f(-1.0f, -1.0f);
5539 glTexCoord3fv(info.coords[1]);
5540 glVertex2f(1.0f, -1.0f);
5541 glTexCoord3fv(info.coords[2]);
5542 glVertex2f(-1.0f, 1.0f);
5543 glTexCoord3fv(info.coords[3]);
5544 glVertex2f(1.0f, 1.0f);
5545 glEnd();
5547 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5548 glBindTexture(info.bind_target, old_binding);
5550 glPopAttrib();
5552 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5555 void surface_modify_ds_location(struct wined3d_surface *surface,
5556 DWORD location, UINT w, UINT h)
5558 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5560 if (location & ~SFLAG_DS_LOCATIONS)
5561 FIXME("Invalid location (%#x) specified.\n", location);
5563 surface->ds_current_size.cx = w;
5564 surface->ds_current_size.cy = h;
5565 surface->flags &= ~SFLAG_DS_LOCATIONS;
5566 surface->flags |= location;
5569 /* Context activation is done by the caller. */
5570 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5572 struct wined3d_device *device = surface->resource.device;
5573 GLsizei w, h;
5575 TRACE("surface %p, new location %#x.\n", surface, location);
5577 /* TODO: Make this work for modes other than FBO */
5578 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5580 if (!(surface->flags & location))
5582 w = surface->ds_current_size.cx;
5583 h = surface->ds_current_size.cy;
5584 surface->ds_current_size.cx = 0;
5585 surface->ds_current_size.cy = 0;
5587 else
5589 w = surface->resource.width;
5590 h = surface->resource.height;
5593 if (surface->ds_current_size.cx == surface->resource.width
5594 && surface->ds_current_size.cy == surface->resource.height)
5596 TRACE("Location (%#x) is already up to date.\n", location);
5597 return;
5600 if (surface->current_renderbuffer)
5602 FIXME("Not supported with fixed up depth stencil.\n");
5603 return;
5606 if (!(surface->flags & SFLAG_DS_LOCATIONS))
5608 /* This mostly happens when a depth / stencil is used without being
5609 * cleared first. In principle we could upload from sysmem, or
5610 * explicitly clear before first usage. For the moment there don't
5611 * appear to be a lot of applications depending on this, so a FIXME
5612 * should do. */
5613 FIXME("No up to date depth stencil location.\n");
5614 surface->flags |= location;
5615 surface->ds_current_size.cx = surface->resource.width;
5616 surface->ds_current_size.cy = surface->resource.height;
5617 return;
5620 if (location == SFLAG_DS_OFFSCREEN)
5622 GLint old_binding = 0;
5623 GLenum bind_target;
5625 /* The render target is allowed to be smaller than the depth/stencil
5626 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5627 * than the offscreen surface. Don't overwrite the offscreen surface
5628 * with undefined data. */
5629 w = min(w, context->swapchain->desc.backbuffer_width);
5630 h = min(h, context->swapchain->desc.backbuffer_height);
5632 TRACE("Copying onscreen depth buffer to depth texture.\n");
5634 ENTER_GL();
5636 if (!device->depth_blt_texture)
5638 glGenTextures(1, &device->depth_blt_texture);
5641 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5642 * directly on the FBO texture. That's because we need to flip. */
5643 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5644 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5645 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5647 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5648 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5650 else
5652 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5653 bind_target = GL_TEXTURE_2D;
5655 glBindTexture(bind_target, device->depth_blt_texture);
5656 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5657 * internal format, because the internal format might include stencil
5658 * data. In principle we should copy stencil data as well, but unless
5659 * the driver supports stencil export it's hard to do, and doesn't
5660 * seem to be needed in practice. If the hardware doesn't support
5661 * writing stencil data, the glCopyTexImage2D() call might trigger
5662 * software fallbacks. */
5663 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5664 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5665 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5666 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5667 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5668 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5669 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5670 glBindTexture(bind_target, old_binding);
5672 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5673 NULL, surface, SFLAG_INTEXTURE);
5674 context_set_draw_buffer(context, GL_NONE);
5675 glReadBuffer(GL_NONE);
5677 /* Do the actual blit */
5678 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5679 checkGLcall("depth_blt");
5681 context_invalidate_state(context, STATE_FRAMEBUFFER);
5683 LEAVE_GL();
5685 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5687 else if (location == SFLAG_DS_ONSCREEN)
5689 TRACE("Copying depth texture to onscreen depth buffer.\n");
5691 ENTER_GL();
5693 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5694 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5695 surface_depth_blt(surface, context, surface->texture_name,
5696 0, surface->pow2Height - h, w, h, surface->texture_target);
5697 checkGLcall("depth_blt");
5699 context_invalidate_state(context, STATE_FRAMEBUFFER);
5701 LEAVE_GL();
5703 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5705 else
5707 ERR("Invalid location (%#x) specified.\n", location);
5710 surface->flags |= location;
5711 surface->ds_current_size.cx = surface->resource.width;
5712 surface->ds_current_size.cy = surface->resource.height;
5715 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5717 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5718 struct wined3d_surface *overlay;
5720 TRACE("surface %p, location %s, persistent %#x.\n",
5721 surface, debug_surflocation(location), persistent);
5723 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5724 && (location & SFLAG_INDRAWABLE))
5725 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5727 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5728 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5729 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5731 if (persistent)
5733 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5734 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5736 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5738 TRACE("Passing to container.\n");
5739 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5742 surface->flags &= ~SFLAG_LOCATIONS;
5743 surface->flags |= location;
5745 /* Redraw emulated overlays, if any */
5746 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5748 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5750 surface_draw_overlay(overlay);
5754 else
5756 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5758 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5760 TRACE("Passing to container\n");
5761 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5764 surface->flags &= ~location;
5767 if (!(surface->flags & SFLAG_LOCATIONS))
5769 ERR("Surface %p does not have any up to date location.\n", surface);
5773 static DWORD resource_access_from_location(DWORD location)
5775 switch (location)
5777 case SFLAG_INSYSMEM:
5778 return WINED3D_RESOURCE_ACCESS_CPU;
5780 case SFLAG_INDRAWABLE:
5781 case SFLAG_INSRGBTEX:
5782 case SFLAG_INTEXTURE:
5783 case SFLAG_INRB_MULTISAMPLE:
5784 case SFLAG_INRB_RESOLVED:
5785 return WINED3D_RESOURCE_ACCESS_GPU;
5787 default:
5788 FIXME("Unhandled location %#x.\n", location);
5789 return 0;
5793 static void surface_load_sysmem(struct wined3d_surface *surface,
5794 const struct wined3d_gl_info *gl_info, const RECT *rect)
5796 surface_prepare_system_memory(surface);
5798 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5799 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5801 /* Download the surface to system memory. */
5802 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5804 struct wined3d_device *device = surface->resource.device;
5805 struct wined3d_context *context;
5807 /* TODO: Use already acquired context when possible. */
5808 context = context_acquire(device, NULL);
5810 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5811 surface_download_data(surface, gl_info);
5813 context_release(context);
5815 return;
5818 if (surface->flags & SFLAG_INDRAWABLE)
5820 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5821 wined3d_surface_get_pitch(surface));
5822 return;
5825 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5826 surface, surface->flags & SFLAG_LOCATIONS);
5829 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5830 const struct wined3d_gl_info *gl_info, const RECT *rect)
5832 struct wined3d_device *device = surface->resource.device;
5833 struct wined3d_format format;
5834 CONVERT_TYPES convert;
5835 UINT byte_count;
5836 BYTE *mem;
5838 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5840 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5841 return WINED3DERR_INVALIDCALL;
5844 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5845 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5847 if (surface->flags & SFLAG_INTEXTURE)
5849 RECT r;
5851 surface_get_rect(surface, rect, &r);
5852 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5854 return WINED3D_OK;
5857 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5859 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5860 * path through sysmem. */
5861 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5864 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5866 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5867 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5868 * called. */
5869 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5871 struct wined3d_context *context;
5873 TRACE("Removing the pbo attached to surface %p.\n", surface);
5875 /* TODO: Use already acquired context when possible. */
5876 context = context_acquire(device, NULL);
5878 surface_remove_pbo(surface, gl_info);
5880 context_release(context);
5883 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5885 UINT height = surface->resource.height;
5886 UINT width = surface->resource.width;
5887 UINT src_pitch, dst_pitch;
5889 byte_count = format.conv_byte_count;
5890 src_pitch = wined3d_surface_get_pitch(surface);
5892 /* Stick to the alignment for the converted surface too, makes it
5893 * easier to load the surface. */
5894 dst_pitch = width * byte_count;
5895 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5897 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5899 ERR("Out of memory (%u).\n", dst_pitch * height);
5900 return E_OUTOFMEMORY;
5903 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5904 src_pitch, width, height, dst_pitch, convert, surface);
5906 surface->flags |= SFLAG_CONVERTED;
5908 else
5910 surface->flags &= ~SFLAG_CONVERTED;
5911 mem = surface->resource.allocatedMemory;
5912 byte_count = format.byte_count;
5915 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5917 /* Don't delete PBO memory. */
5918 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5919 HeapFree(GetProcessHeap(), 0, mem);
5921 return WINED3D_OK;
5924 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5925 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5927 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5928 struct wined3d_device *device = surface->resource.device;
5929 struct wined3d_context *context;
5930 UINT width, src_pitch, dst_pitch;
5931 struct wined3d_bo_address data;
5932 struct wined3d_format format;
5933 POINT dst_point = {0, 0};
5934 CONVERT_TYPES convert;
5935 BYTE *mem;
5937 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
5938 && surface_is_offscreen(surface)
5939 && (surface->flags & SFLAG_INDRAWABLE))
5941 surface_load_fb_texture(surface, srgb);
5943 return WINED3D_OK;
5946 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
5947 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
5948 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5949 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5950 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5952 if (srgb)
5953 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
5954 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
5955 else
5956 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
5957 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
5959 return WINED3D_OK;
5962 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
5963 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
5964 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5965 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5966 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5968 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
5969 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
5970 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5972 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, src_location,
5973 &rect, surface, dst_location, &rect);
5975 return WINED3D_OK;
5978 /* Upload from system memory */
5980 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
5981 TRUE /* We will use textures */, &format, &convert);
5983 if (srgb)
5985 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
5987 /* Performance warning... */
5988 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
5989 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5992 else
5994 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
5996 /* Performance warning... */
5997 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
5998 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6002 if (!(surface->flags & SFLAG_INSYSMEM))
6004 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6005 /* Lets hope we get it from somewhere... */
6006 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6009 /* TODO: Use already acquired context when possible. */
6010 context = context_acquire(device, NULL);
6012 surface_prepare_texture(surface, context, srgb);
6013 surface_bind_and_dirtify(surface, context, srgb);
6015 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6017 surface->flags |= SFLAG_GLCKEY;
6018 surface->gl_color_key = surface->src_blt_color_key;
6020 else surface->flags &= ~SFLAG_GLCKEY;
6022 width = surface->resource.width;
6023 src_pitch = wined3d_surface_get_pitch(surface);
6025 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6026 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6027 * called. */
6028 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6030 TRACE("Removing the pbo attached to surface %p.\n", surface);
6031 surface_remove_pbo(surface, gl_info);
6034 if (format.convert)
6036 /* This code is entered for texture formats which need a fixup. */
6037 UINT height = surface->resource.height;
6039 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6040 dst_pitch = width * format.conv_byte_count;
6041 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6043 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6045 ERR("Out of memory (%u).\n", dst_pitch * height);
6046 context_release(context);
6047 return E_OUTOFMEMORY;
6049 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6050 format.byte_count = format.conv_byte_count;
6051 src_pitch = dst_pitch;
6053 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6055 /* This code is only entered for color keying fixups */
6056 UINT height = surface->resource.height;
6058 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6059 dst_pitch = width * format.conv_byte_count;
6060 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6062 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6064 ERR("Out of memory (%u).\n", dst_pitch * height);
6065 context_release(context);
6066 return E_OUTOFMEMORY;
6068 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6069 width, height, dst_pitch, convert, surface);
6070 format.byte_count = format.conv_byte_count;
6071 src_pitch = dst_pitch;
6073 else
6075 mem = surface->resource.allocatedMemory;
6078 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6079 data.addr = mem;
6080 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
6082 context_release(context);
6084 /* Don't delete PBO memory. */
6085 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6086 HeapFree(GetProcessHeap(), 0, mem);
6088 return WINED3D_OK;
6091 static void surface_multisample_resolve(struct wined3d_surface *surface)
6093 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6095 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6096 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6098 surface_blt_fbo(surface->resource.device, WINED3DTEXF_POINT,
6099 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6102 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6104 struct wined3d_device *device = surface->resource.device;
6105 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6106 HRESULT hr;
6108 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6110 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6112 if (location == SFLAG_INTEXTURE)
6114 struct wined3d_context *context = context_acquire(device, NULL);
6115 surface_load_ds_location(surface, context, SFLAG_DS_OFFSCREEN);
6116 context_release(context);
6117 return WINED3D_OK;
6119 else
6121 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6122 return WINED3DERR_INVALIDCALL;
6126 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6127 location = SFLAG_INTEXTURE;
6129 if (surface->flags & location)
6131 TRACE("Location already up to date.\n");
6133 if (location == SFLAG_INSYSMEM && !(surface->flags & SFLAG_PBO)
6134 && surface_need_pbo(surface, gl_info))
6135 surface_load_pbo(surface, gl_info);
6137 return WINED3D_OK;
6140 if (WARN_ON(d3d_surface))
6142 DWORD required_access = resource_access_from_location(location);
6143 if ((surface->resource.access_flags & required_access) != required_access)
6144 WARN("Operation requires %#x access, but surface only has %#x.\n",
6145 required_access, surface->resource.access_flags);
6148 if (!(surface->flags & SFLAG_LOCATIONS))
6150 ERR("Surface %p does not have any up to date location.\n", surface);
6151 surface->flags |= SFLAG_LOST;
6152 return WINED3DERR_DEVICELOST;
6155 switch (location)
6157 case SFLAG_INSYSMEM:
6158 surface_load_sysmem(surface, gl_info, rect);
6159 break;
6161 case SFLAG_INDRAWABLE:
6162 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6163 return hr;
6164 break;
6166 case SFLAG_INRB_RESOLVED:
6167 surface_multisample_resolve(surface);
6168 break;
6170 case SFLAG_INTEXTURE:
6171 case SFLAG_INSRGBTEX:
6172 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6173 return hr;
6174 break;
6176 default:
6177 ERR("Don't know how to handle location %#x.\n", location);
6178 break;
6181 if (!rect)
6183 surface->flags |= location;
6185 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6186 surface_evict_sysmem(surface);
6189 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6190 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6192 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6195 return WINED3D_OK;
6198 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6200 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6202 /* Not on a swapchain - must be offscreen */
6203 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6205 /* The front buffer is always onscreen */
6206 if (surface == swapchain->front_buffer) return FALSE;
6208 /* If the swapchain is rendered to an FBO, the backbuffer is
6209 * offscreen, otherwise onscreen */
6210 return swapchain->render_to_fbo;
6213 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6214 /* Context activation is done by the caller. */
6215 static void ffp_blit_free(struct wined3d_device *device) { }
6217 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6218 /* Context activation is done by the caller. */
6219 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6221 BYTE table[256][4];
6222 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6224 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6226 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6227 ENTER_GL();
6228 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6229 LEAVE_GL();
6232 /* Context activation is done by the caller. */
6233 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6235 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6237 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6238 * else the surface is converted in software at upload time in LoadLocation.
6240 if (!(surface->flags & SFLAG_CONVERTED) && fixup == COMPLEX_FIXUP_P8
6241 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6242 ffp_blit_p8_upload_palette(surface, context->gl_info);
6244 ENTER_GL();
6245 glEnable(surface->texture_target);
6246 checkGLcall("glEnable(surface->texture_target)");
6247 LEAVE_GL();
6248 return WINED3D_OK;
6251 /* Context activation is done by the caller. */
6252 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6254 ENTER_GL();
6255 glDisable(GL_TEXTURE_2D);
6256 checkGLcall("glDisable(GL_TEXTURE_2D)");
6257 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6259 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6260 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6262 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6264 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6265 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6267 LEAVE_GL();
6270 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6271 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6272 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6274 enum complex_fixup src_fixup;
6276 switch (blit_op)
6278 case WINED3D_BLIT_OP_COLOR_BLIT:
6279 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
6280 return FALSE;
6282 src_fixup = get_complex_fixup(src_format->color_fixup);
6283 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6285 TRACE("Checking support for fixup:\n");
6286 dump_color_fixup_desc(src_format->color_fixup);
6289 if (!is_identity_fixup(dst_format->color_fixup))
6291 TRACE("Destination fixups are not supported\n");
6292 return FALSE;
6295 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6297 TRACE("P8 fixup supported\n");
6298 return TRUE;
6301 /* We only support identity conversions. */
6302 if (is_identity_fixup(src_format->color_fixup))
6304 TRACE("[OK]\n");
6305 return TRUE;
6308 TRACE("[FAILED]\n");
6309 return FALSE;
6311 case WINED3D_BLIT_OP_COLOR_FILL:
6312 if (dst_pool == WINED3DPOOL_SYSTEMMEM)
6313 return FALSE;
6315 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6317 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6318 return FALSE;
6320 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6322 TRACE("Color fill not supported\n");
6323 return FALSE;
6326 /* FIXME: We should reject color fills on formats with fixups,
6327 * but this would break P8 color fills for example. */
6329 return TRUE;
6331 case WINED3D_BLIT_OP_DEPTH_FILL:
6332 return TRUE;
6334 default:
6335 TRACE("Unsupported blit_op=%d\n", blit_op);
6336 return FALSE;
6340 /* Do not call while under the GL lock. */
6341 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6342 const RECT *dst_rect, const struct wined3d_color *color)
6344 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6345 struct wined3d_fb_state fb = {&dst_surface, NULL};
6347 return device_clear_render_targets(device, 1, &fb,
6348 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6351 /* Do not call while under the GL lock. */
6352 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6353 struct wined3d_surface *surface, const RECT *rect, float depth)
6355 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6356 struct wined3d_fb_state fb = {NULL, surface};
6358 return device_clear_render_targets(device, 0, &fb,
6359 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6362 const struct blit_shader ffp_blit = {
6363 ffp_blit_alloc,
6364 ffp_blit_free,
6365 ffp_blit_set,
6366 ffp_blit_unset,
6367 ffp_blit_supported,
6368 ffp_blit_color_fill,
6369 ffp_blit_depth_fill,
6372 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6374 return WINED3D_OK;
6377 /* Context activation is done by the caller. */
6378 static void cpu_blit_free(struct wined3d_device *device)
6382 /* Context activation is done by the caller. */
6383 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6385 return WINED3D_OK;
6388 /* Context activation is done by the caller. */
6389 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6393 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6394 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6395 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6397 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6399 return TRUE;
6402 return FALSE;
6405 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
6406 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
6407 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
6409 UINT row_block_count;
6410 const BYTE *src_row;
6411 BYTE *dst_row;
6412 UINT x, y;
6414 src_row = src_data;
6415 dst_row = dst_data;
6417 row_block_count = (update_w + format->block_width - 1) / format->block_width;
6419 if (!flags)
6421 for (y = 0; y < update_h; y += format->block_height)
6423 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
6424 src_row += src_pitch;
6425 dst_row += dst_pitch;
6428 return WINED3D_OK;
6431 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
6433 src_row += (((update_h / format->block_height) - 1) * src_pitch);
6435 switch (format->id)
6437 case WINED3DFMT_DXT1:
6438 for (y = 0; y < update_h; y += format->block_height)
6440 struct block
6442 WORD color[2];
6443 BYTE control_row[4];
6446 const struct block *s = (const struct block *)src_row;
6447 struct block *d = (struct block *)dst_row;
6449 for (x = 0; x < row_block_count; ++x)
6451 d[x].color[0] = s[x].color[0];
6452 d[x].color[1] = s[x].color[1];
6453 d[x].control_row[0] = s[x].control_row[3];
6454 d[x].control_row[1] = s[x].control_row[2];
6455 d[x].control_row[2] = s[x].control_row[1];
6456 d[x].control_row[3] = s[x].control_row[0];
6458 src_row -= src_pitch;
6459 dst_row += dst_pitch;
6461 return WINED3D_OK;
6463 case WINED3DFMT_DXT3:
6464 for (y = 0; y < update_h; y += format->block_height)
6466 struct block
6468 WORD alpha_row[4];
6469 WORD color[2];
6470 BYTE control_row[4];
6473 const struct block *s = (const struct block *)src_row;
6474 struct block *d = (struct block *)dst_row;
6476 for (x = 0; x < row_block_count; ++x)
6478 d[x].alpha_row[0] = s[x].alpha_row[3];
6479 d[x].alpha_row[1] = s[x].alpha_row[2];
6480 d[x].alpha_row[2] = s[x].alpha_row[1];
6481 d[x].alpha_row[3] = s[x].alpha_row[0];
6482 d[x].color[0] = s[x].color[0];
6483 d[x].color[1] = s[x].color[1];
6484 d[x].control_row[0] = s[x].control_row[3];
6485 d[x].control_row[1] = s[x].control_row[2];
6486 d[x].control_row[2] = s[x].control_row[1];
6487 d[x].control_row[3] = s[x].control_row[0];
6489 src_row -= src_pitch;
6490 dst_row += dst_pitch;
6492 return WINED3D_OK;
6494 default:
6495 FIXME("Compressed flip not implemented for format %s.\n",
6496 debug_d3dformat(format->id));
6497 return E_NOTIMPL;
6501 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
6502 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
6504 return E_NOTIMPL;
6507 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6508 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6509 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6511 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6512 const struct wined3d_format *src_format, *dst_format;
6513 struct wined3d_surface *orig_src = src_surface;
6514 struct wined3d_mapped_rect dst_map, src_map;
6515 HRESULT hr = WINED3D_OK;
6516 const BYTE *sbuf;
6517 RECT xdst,xsrc;
6518 BYTE *dbuf;
6519 int x, y;
6521 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6522 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6523 flags, fx, debug_d3dtexturefiltertype(filter));
6525 xsrc = *src_rect;
6527 if (!src_surface)
6529 RECT full_rect;
6531 full_rect.left = 0;
6532 full_rect.top = 0;
6533 full_rect.right = dst_surface->resource.width;
6534 full_rect.bottom = dst_surface->resource.height;
6535 IntersectRect(&xdst, &full_rect, dst_rect);
6537 else
6539 BOOL clip_horiz, clip_vert;
6541 xdst = *dst_rect;
6542 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6543 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6545 if (clip_vert || clip_horiz)
6547 /* Now check if this is a special case or not... */
6548 if ((flags & WINEDDBLT_DDFX)
6549 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6550 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6552 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6553 return WINED3D_OK;
6556 if (clip_horiz)
6558 if (xdst.left < 0)
6560 xsrc.left -= xdst.left;
6561 xdst.left = 0;
6563 if (xdst.right > dst_surface->resource.width)
6565 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6566 xdst.right = (int)dst_surface->resource.width;
6570 if (clip_vert)
6572 if (xdst.top < 0)
6574 xsrc.top -= xdst.top;
6575 xdst.top = 0;
6577 if (xdst.bottom > dst_surface->resource.height)
6579 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6580 xdst.bottom = (int)dst_surface->resource.height;
6584 /* And check if after clipping something is still to be done... */
6585 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6586 || (xdst.left >= (int)dst_surface->resource.width)
6587 || (xdst.top >= (int)dst_surface->resource.height)
6588 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6589 || (xsrc.left >= (int)src_surface->resource.width)
6590 || (xsrc.top >= (int)src_surface->resource.height))
6592 TRACE("Nothing to be done after clipping.\n");
6593 return WINED3D_OK;
6598 if (src_surface == dst_surface)
6600 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6601 src_map = dst_map;
6602 src_format = dst_surface->resource.format;
6603 dst_format = src_format;
6605 else
6607 dst_format = dst_surface->resource.format;
6608 if (src_surface)
6610 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6612 src_surface = surface_convert_format(src_surface, dst_format->id);
6613 if (!src_surface)
6615 /* The conv function writes a FIXME */
6616 WARN("Cannot convert source surface format to dest format.\n");
6617 goto release;
6620 wined3d_surface_map(src_surface, &src_map, NULL, WINED3DLOCK_READONLY);
6621 src_format = src_surface->resource.format;
6623 else
6625 src_format = dst_format;
6627 if (dst_rect)
6628 wined3d_surface_map(dst_surface, &dst_map, &xdst, 0);
6629 else
6630 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
6633 bpp = dst_surface->resource.format->byte_count;
6634 srcheight = xsrc.bottom - xsrc.top;
6635 srcwidth = xsrc.right - xsrc.left;
6636 dstheight = xdst.bottom - xdst.top;
6637 dstwidth = xdst.right - xdst.left;
6638 width = (xdst.right - xdst.left) * bpp;
6640 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
6642 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6644 if (src_surface == dst_surface)
6646 FIXME("Only plain blits supported on compressed surfaces.\n");
6647 hr = E_NOTIMPL;
6648 goto release;
6651 if (srcheight != dstheight || srcwidth != dstwidth)
6653 WARN("Stretching not supported on compressed surfaces.\n");
6654 hr = WINED3DERR_INVALIDCALL;
6655 goto release;
6658 if (srcwidth & (src_format->block_width - 1) || srcheight & (src_format->block_height - 1))
6660 WARN("Rectangle not block-aligned.\n");
6661 hr = WINED3DERR_INVALIDCALL;
6662 goto release;
6665 hr = surface_cpu_blt_compressed(src_map.data, dst_map.data,
6666 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
6667 src_format, flags, fx);
6668 goto release;
6671 if (dst_rect && src_surface != dst_surface)
6672 dbuf = dst_map.data;
6673 else
6674 dbuf = (BYTE *)dst_map.data + (xdst.top * dst_map.row_pitch) + (xdst.left * bpp);
6676 /* First, all the 'source-less' blits */
6677 if (flags & WINEDDBLT_COLORFILL)
6679 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
6680 flags &= ~WINEDDBLT_COLORFILL;
6683 if (flags & WINEDDBLT_DEPTHFILL)
6685 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6687 if (flags & WINEDDBLT_ROP)
6689 /* Catch some degenerate cases here. */
6690 switch (fx->dwROP)
6692 case BLACKNESS:
6693 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
6694 break;
6695 case 0xAA0029: /* No-op */
6696 break;
6697 case WHITENESS:
6698 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
6699 break;
6700 case SRCCOPY: /* Well, we do that below? */
6701 break;
6702 default:
6703 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6704 goto error;
6706 flags &= ~WINEDDBLT_ROP;
6708 if (flags & WINEDDBLT_DDROPS)
6710 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6712 /* Now the 'with source' blits. */
6713 if (src_surface)
6715 const BYTE *sbase;
6716 int sx, xinc, sy, yinc;
6718 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6719 goto release;
6721 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6722 && (srcwidth != dstwidth || srcheight != dstheight))
6724 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6725 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6728 sbase = (BYTE *)src_map.data + (xsrc.top * src_map.row_pitch) + xsrc.left * bpp;
6729 xinc = (srcwidth << 16) / dstwidth;
6730 yinc = (srcheight << 16) / dstheight;
6732 if (!flags)
6734 /* No effects, we can cheat here. */
6735 if (dstwidth == srcwidth)
6737 if (dstheight == srcheight)
6739 /* No stretching in either direction. This needs to be as
6740 * fast as possible. */
6741 sbuf = sbase;
6743 /* Check for overlapping surfaces. */
6744 if (src_surface != dst_surface || xdst.top < xsrc.top
6745 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6747 /* No overlap, or dst above src, so copy from top downwards. */
6748 for (y = 0; y < dstheight; ++y)
6750 memcpy(dbuf, sbuf, width);
6751 sbuf += src_map.row_pitch;
6752 dbuf += dst_map.row_pitch;
6755 else if (xdst.top > xsrc.top)
6757 /* Copy from bottom upwards. */
6758 sbuf += src_map.row_pitch * dstheight;
6759 dbuf += dst_map.row_pitch * dstheight;
6760 for (y = 0; y < dstheight; ++y)
6762 sbuf -= src_map.row_pitch;
6763 dbuf -= dst_map.row_pitch;
6764 memcpy(dbuf, sbuf, width);
6767 else
6769 /* Src and dst overlapping on the same line, use memmove. */
6770 for (y = 0; y < dstheight; ++y)
6772 memmove(dbuf, sbuf, width);
6773 sbuf += src_map.row_pitch;
6774 dbuf += dst_map.row_pitch;
6778 else
6780 /* Stretching in y direction only. */
6781 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6783 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6784 memcpy(dbuf, sbuf, width);
6785 dbuf += dst_map.row_pitch;
6789 else
6791 /* Stretching in X direction. */
6792 int last_sy = -1;
6793 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6795 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
6797 if ((sy >> 16) == (last_sy >> 16))
6799 /* This source row is the same as last source row -
6800 * Copy the already stretched row. */
6801 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
6803 else
6805 #define STRETCH_ROW(type) \
6806 do { \
6807 const type *s = (const type *)sbuf; \
6808 type *d = (type *)dbuf; \
6809 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6810 d[x] = s[sx >> 16]; \
6811 } while(0)
6813 switch(bpp)
6815 case 1:
6816 STRETCH_ROW(BYTE);
6817 break;
6818 case 2:
6819 STRETCH_ROW(WORD);
6820 break;
6821 case 4:
6822 STRETCH_ROW(DWORD);
6823 break;
6824 case 3:
6826 const BYTE *s;
6827 BYTE *d = dbuf;
6828 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6830 DWORD pixel;
6832 s = sbuf + 3 * (sx >> 16);
6833 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6834 d[0] = (pixel ) & 0xff;
6835 d[1] = (pixel >> 8) & 0xff;
6836 d[2] = (pixel >> 16) & 0xff;
6837 d += 3;
6839 break;
6841 default:
6842 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6843 hr = WINED3DERR_NOTAVAILABLE;
6844 goto error;
6846 #undef STRETCH_ROW
6848 dbuf += dst_map.row_pitch;
6849 last_sy = sy;
6853 else
6855 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
6856 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6857 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6858 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6860 /* The color keying flags are checked for correctness in ddraw */
6861 if (flags & WINEDDBLT_KEYSRC)
6863 keylow = src_surface->src_blt_color_key.color_space_low_value;
6864 keyhigh = src_surface->src_blt_color_key.color_space_high_value;
6866 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6868 keylow = fx->ddckSrcColorkey.color_space_low_value;
6869 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
6872 if (flags & WINEDDBLT_KEYDEST)
6874 /* Destination color keys are taken from the source surface! */
6875 destkeylow = src_surface->dst_blt_color_key.color_space_low_value;
6876 destkeyhigh = src_surface->dst_blt_color_key.color_space_high_value;
6878 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6880 destkeylow = fx->ddckDestColorkey.color_space_low_value;
6881 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
6884 if (bpp == 1)
6886 keymask = 0xff;
6888 else
6890 keymask = src_format->red_mask
6891 | src_format->green_mask
6892 | src_format->blue_mask;
6894 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6897 if (flags & WINEDDBLT_DDFX)
6899 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6900 LONG tmpxy;
6901 dTopLeft = dbuf;
6902 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6903 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
6904 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6906 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6908 /* I don't think we need to do anything about this flag */
6909 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6911 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6913 tmp = dTopRight;
6914 dTopRight = dTopLeft;
6915 dTopLeft = tmp;
6916 tmp = dBottomRight;
6917 dBottomRight = dBottomLeft;
6918 dBottomLeft = tmp;
6919 dstxinc = dstxinc * -1;
6921 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6923 tmp = dTopLeft;
6924 dTopLeft = dBottomLeft;
6925 dBottomLeft = tmp;
6926 tmp = dTopRight;
6927 dTopRight = dBottomRight;
6928 dBottomRight = tmp;
6929 dstyinc = dstyinc * -1;
6931 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6933 /* I don't think we need to do anything about this flag */
6934 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6936 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6938 tmp = dBottomRight;
6939 dBottomRight = dTopLeft;
6940 dTopLeft = tmp;
6941 tmp = dBottomLeft;
6942 dBottomLeft = dTopRight;
6943 dTopRight = tmp;
6944 dstxinc = dstxinc * -1;
6945 dstyinc = dstyinc * -1;
6947 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6949 tmp = dTopLeft;
6950 dTopLeft = dBottomLeft;
6951 dBottomLeft = dBottomRight;
6952 dBottomRight = dTopRight;
6953 dTopRight = tmp;
6954 tmpxy = dstxinc;
6955 dstxinc = dstyinc;
6956 dstyinc = tmpxy;
6957 dstxinc = dstxinc * -1;
6959 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6961 tmp = dTopLeft;
6962 dTopLeft = dTopRight;
6963 dTopRight = dBottomRight;
6964 dBottomRight = dBottomLeft;
6965 dBottomLeft = tmp;
6966 tmpxy = dstxinc;
6967 dstxinc = dstyinc;
6968 dstyinc = tmpxy;
6969 dstyinc = dstyinc * -1;
6971 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6973 /* I don't think we need to do anything about this flag */
6974 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6976 dbuf = dTopLeft;
6977 flags &= ~(WINEDDBLT_DDFX);
6980 #define COPY_COLORKEY_FX(type) \
6981 do { \
6982 const type *s; \
6983 type *d = (type *)dbuf, *dx, tmp; \
6984 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6986 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
6987 dx = d; \
6988 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6990 tmp = s[sx >> 16]; \
6991 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6992 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6994 dx[0] = tmp; \
6996 dx = (type *)(((BYTE *)dx) + dstxinc); \
6998 d = (type *)(((BYTE *)d) + dstyinc); \
7000 } while(0)
7002 switch (bpp)
7004 case 1:
7005 COPY_COLORKEY_FX(BYTE);
7006 break;
7007 case 2:
7008 COPY_COLORKEY_FX(WORD);
7009 break;
7010 case 4:
7011 COPY_COLORKEY_FX(DWORD);
7012 break;
7013 case 3:
7015 const BYTE *s;
7016 BYTE *d = dbuf, *dx;
7017 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
7019 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
7020 dx = d;
7021 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
7023 DWORD pixel, dpixel = 0;
7024 s = sbuf + 3 * (sx>>16);
7025 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
7026 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
7027 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
7028 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
7030 dx[0] = (pixel ) & 0xff;
7031 dx[1] = (pixel >> 8) & 0xff;
7032 dx[2] = (pixel >> 16) & 0xff;
7034 dx += dstxinc;
7036 d += dstyinc;
7038 break;
7040 default:
7041 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7042 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7043 hr = WINED3DERR_NOTAVAILABLE;
7044 goto error;
7045 #undef COPY_COLORKEY_FX
7050 error:
7051 if (flags && FIXME_ON(d3d_surface))
7053 FIXME("\tUnsupported flags: %#x.\n", flags);
7056 release:
7057 wined3d_surface_unmap(dst_surface);
7058 if (src_surface && src_surface != dst_surface)
7059 wined3d_surface_unmap(src_surface);
7060 /* Release the converted surface, if any. */
7061 if (src_surface && src_surface != orig_src)
7062 wined3d_surface_decref(src_surface);
7064 return hr;
7067 /* Do not call while under the GL lock. */
7068 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7069 const RECT *dst_rect, const struct wined3d_color *color)
7071 static const RECT src_rect;
7072 WINEDDBLTFX BltFx;
7074 memset(&BltFx, 0, sizeof(BltFx));
7075 BltFx.dwSize = sizeof(BltFx);
7076 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7077 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7078 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7081 /* Do not call while under the GL lock. */
7082 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7083 struct wined3d_surface *surface, const RECT *rect, float depth)
7085 FIXME("Depth filling not implemented by cpu_blit.\n");
7086 return WINED3DERR_INVALIDCALL;
7089 const struct blit_shader cpu_blit = {
7090 cpu_blit_alloc,
7091 cpu_blit_free,
7092 cpu_blit_set,
7093 cpu_blit_unset,
7094 cpu_blit_supported,
7095 cpu_blit_color_fill,
7096 cpu_blit_depth_fill,
7099 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7100 UINT width, UINT height, UINT level, WINED3DMULTISAMPLE_TYPE multisample_type,
7101 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7102 WINED3DPOOL pool, DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops)
7104 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7105 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7106 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
7107 unsigned int resource_size;
7108 HRESULT hr;
7110 if (multisample_quality > 0)
7112 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7113 multisample_quality = 0;
7116 /* Quick lockable sanity check.
7117 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7118 * this function is too deep to need to care about things like this.
7119 * Levels need to be checked too, since they all affect what can be done. */
7120 switch (pool)
7122 case WINED3DPOOL_SCRATCH:
7123 if (!lockable)
7125 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7126 "which are mutually exclusive, setting lockable to TRUE.\n");
7127 lockable = TRUE;
7129 break;
7131 case WINED3DPOOL_SYSTEMMEM:
7132 if (!lockable)
7133 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7134 break;
7136 case WINED3DPOOL_MANAGED:
7137 if (usage & WINED3DUSAGE_DYNAMIC)
7138 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7139 break;
7141 case WINED3DPOOL_DEFAULT:
7142 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7143 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7144 break;
7146 default:
7147 FIXME("Unknown pool %#x.\n", pool);
7148 break;
7151 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7152 FIXME("Trying to create a render target that isn't in the default pool.\n");
7154 /* FIXME: Check that the format is supported by the device. */
7156 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7157 if (!resource_size)
7158 return WINED3DERR_INVALIDCALL;
7160 surface->surface_type = surface_type;
7162 switch (surface_type)
7164 case SURFACE_OPENGL:
7165 surface->surface_ops = &surface_ops;
7166 break;
7168 case SURFACE_GDI:
7169 surface->surface_ops = &gdi_surface_ops;
7170 break;
7172 default:
7173 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7174 return WINED3DERR_INVALIDCALL;
7177 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7178 multisample_type, multisample_quality, usage, pool, width, height, 1,
7179 resource_size, parent, parent_ops, &surface_resource_ops);
7180 if (FAILED(hr))
7182 WARN("Failed to initialize resource, returning %#x.\n", hr);
7183 return hr;
7186 /* "Standalone" surface. */
7187 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7189 surface->texture_level = level;
7190 list_init(&surface->overlays);
7192 /* Flags */
7193 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7194 if (flags & WINED3D_SURFACE_DISCARD)
7195 surface->flags |= SFLAG_DISCARD;
7196 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
7197 surface->flags |= SFLAG_PIN_SYSMEM;
7198 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7199 surface->flags |= SFLAG_LOCKABLE;
7200 /* I'm not sure if this qualifies as a hack or as an optimization. It
7201 * seems reasonable to assume that lockable render targets will get
7202 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7203 * creation. However, the other reason we want to do this is that several
7204 * ddraw applications access surface memory while the surface isn't
7205 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7206 * future locks prevents these from crashing. */
7207 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7208 surface->flags |= SFLAG_DYNLOCK;
7210 /* Mark the texture as dirty so that it gets loaded first time around. */
7211 surface_add_dirty_rect(surface, NULL);
7212 list_init(&surface->renderbuffers);
7214 TRACE("surface %p, memory %p, size %u\n",
7215 surface, surface->resource.allocatedMemory, surface->resource.size);
7217 /* Call the private setup routine */
7218 hr = surface->surface_ops->surface_private_setup(surface);
7219 if (FAILED(hr))
7221 ERR("Private setup failed, returning %#x\n", hr);
7222 surface_cleanup(surface);
7223 return hr;
7226 /* Similar to lockable rendertargets above, creating the DIB section
7227 * during surface initialization prevents the sysmem pointer from changing
7228 * after a wined3d_surface_getdc() call. */
7229 if ((usage & WINED3DUSAGE_OWNDC) && !surface->hDC
7230 && SUCCEEDED(surface_create_dib_section(surface)))
7232 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
7233 surface->resource.heapMemory = NULL;
7234 surface->resource.allocatedMemory = surface->dib.bitmap_data;
7237 return hr;
7240 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7241 enum wined3d_format_id format_id, UINT level, DWORD usage, WINED3DPOOL pool,
7242 WINED3DMULTISAMPLE_TYPE multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7243 DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7245 struct wined3d_surface *object;
7246 HRESULT hr;
7248 TRACE("device %p, width %u, height %u, format %s, level %u\n",
7249 device, width, height, debug_d3dformat(format_id), level);
7250 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7251 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7252 TRACE("surface_type %#x, flags %#x, parent %p, parent_ops %p.\n", surface_type, flags, parent, parent_ops);
7254 if (surface_type == SURFACE_OPENGL && !device->adapter)
7256 ERR("OpenGL surfaces are not available without OpenGL.\n");
7257 return WINED3DERR_NOTAVAILABLE;
7260 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7261 if (!object)
7263 ERR("Failed to allocate surface memory.\n");
7264 return WINED3DERR_OUTOFVIDEOMEMORY;
7267 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level,
7268 multisample_type, multisample_quality, device, usage, format_id, pool, flags, parent, parent_ops);
7269 if (FAILED(hr))
7271 WARN("Failed to initialize surface, returning %#x.\n", hr);
7272 HeapFree(GetProcessHeap(), 0, object);
7273 return hr;
7276 TRACE("Created surface %p.\n", object);
7277 *surface = object;
7279 return hr;