wined3d: Move the surface map counting code to wined3d_surface_map().
[wine/multimedia.git] / dlls / wined3d / surface.c
blobb74e8d52b21bcbdb46116849ab2bb6362c658531
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 void surface_prepare_system_memory(struct wined3d_surface *surface)
525 struct wined3d_device *device = surface->resource.device;
526 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
528 TRACE("surface %p.\n", surface);
530 /* Create a PBO for dynamically locked surfaces but don't do it for
531 * converted or NPOT surfaces. Also don't create a PBO for systemmem
532 * surfaces. */
533 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (surface->flags & SFLAG_DYNLOCK)
534 && !(surface->flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2 | SFLAG_PIN_SYSMEM))
535 && (surface->resource.pool != WINED3DPOOL_SYSTEMMEM))
537 struct wined3d_context *context;
538 GLenum error;
540 context = context_acquire(device, NULL);
541 ENTER_GL();
543 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
544 error = glGetError();
545 if (!surface->pbo || error != GL_NO_ERROR)
546 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
548 TRACE("Binding PBO %u.\n", surface->pbo);
550 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
551 checkGLcall("glBindBufferARB");
553 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
554 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
555 checkGLcall("glBufferDataARB");
557 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
558 checkGLcall("glBindBufferARB");
560 /* We don't need the system memory anymore and we can't even use it for PBOs. */
561 if (!(surface->flags & SFLAG_CLIENT))
563 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
564 surface->resource.heapMemory = NULL;
566 surface->resource.allocatedMemory = NULL;
567 surface->flags |= SFLAG_PBO;
568 LEAVE_GL();
569 context_release(context);
571 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
573 /* Whatever surface we have, make sure that there is memory allocated
574 * for the downloaded copy, or a PBO to map. */
575 if (!surface->resource.heapMemory)
576 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
578 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
579 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
581 if (surface->flags & SFLAG_INSYSMEM)
582 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
586 static void surface_evict_sysmem(struct wined3d_surface *surface)
588 if (surface->flags & SFLAG_DONOTFREE)
589 return;
591 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
592 surface->resource.allocatedMemory = NULL;
593 surface->resource.heapMemory = NULL;
594 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
597 /* Context activation is done by the caller. */
598 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
599 struct wined3d_context *context, BOOL srgb)
601 struct wined3d_device *device = surface->resource.device;
602 DWORD active_sampler;
604 /* We don't need a specific texture unit, but after binding the texture
605 * the current unit is dirty. Read the unit back instead of switching to
606 * 0, this avoids messing around with the state manager's GL states. The
607 * current texture unit should always be a valid one.
609 * To be more specific, this is tricky because we can implicitly be
610 * called from sampler() in state.c. This means we can't touch anything
611 * other than whatever happens to be the currently active texture, or we
612 * would risk marking already applied sampler states dirty again. */
613 active_sampler = device->rev_tex_unit_map[context->active_texture];
615 if (active_sampler != WINED3D_UNMAPPED_STAGE)
616 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
617 surface_bind(surface, context, srgb);
620 static void surface_force_reload(struct wined3d_surface *surface)
622 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
625 static void surface_release_client_storage(struct wined3d_surface *surface)
627 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
629 ENTER_GL();
630 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
631 if (surface->texture_name)
633 surface_bind_and_dirtify(surface, context, FALSE);
634 glTexImage2D(surface->texture_target, surface->texture_level,
635 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
637 if (surface->texture_name_srgb)
639 surface_bind_and_dirtify(surface, context, TRUE);
640 glTexImage2D(surface->texture_target, surface->texture_level,
641 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
643 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
644 LEAVE_GL();
646 context_release(context);
648 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
649 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
650 surface_force_reload(surface);
653 static HRESULT surface_private_setup(struct wined3d_surface *surface)
655 /* TODO: Check against the maximum texture sizes supported by the video card. */
656 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
657 unsigned int pow2Width, pow2Height;
659 TRACE("surface %p.\n", surface);
661 surface->texture_name = 0;
662 surface->texture_target = GL_TEXTURE_2D;
664 /* Non-power2 support */
665 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
667 pow2Width = surface->resource.width;
668 pow2Height = surface->resource.height;
670 else
672 /* Find the nearest pow2 match */
673 pow2Width = pow2Height = 1;
674 while (pow2Width < surface->resource.width)
675 pow2Width <<= 1;
676 while (pow2Height < surface->resource.height)
677 pow2Height <<= 1;
679 surface->pow2Width = pow2Width;
680 surface->pow2Height = pow2Height;
682 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
684 /* TODO: Add support for non power two compressed textures. */
685 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
687 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
688 surface, surface->resource.width, surface->resource.height);
689 return WINED3DERR_NOTAVAILABLE;
693 if (pow2Width != surface->resource.width
694 || pow2Height != surface->resource.height)
696 surface->flags |= SFLAG_NONPOW2;
699 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
700 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
702 /* One of three options:
703 * 1: Do the same as we do with NPOT and scale the texture, (any
704 * texture ops would require the texture to be scaled which is
705 * potentially slow)
706 * 2: Set the texture to the maximum size (bad idea).
707 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
708 * 4: Create the surface, but allow it to be used only for DirectDraw
709 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
710 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
711 * the render target. */
712 if (surface->resource.pool == WINED3DPOOL_DEFAULT || surface->resource.pool == WINED3DPOOL_MANAGED)
714 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
715 return WINED3DERR_NOTAVAILABLE;
718 /* We should never use this surface in combination with OpenGL! */
719 TRACE("Creating an oversized surface: %ux%u.\n",
720 surface->pow2Width, surface->pow2Height);
722 else
724 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
725 * and EXT_PALETTED_TEXTURE is used in combination with texture
726 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
727 * EXT_PALETTED_TEXTURE doesn't work in combination with
728 * ARB_TEXTURE_RECTANGLE. */
729 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
730 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
731 && gl_info->supported[EXT_PALETTED_TEXTURE]
732 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
734 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
735 surface->pow2Width = surface->resource.width;
736 surface->pow2Height = surface->resource.height;
737 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
741 switch (wined3d_settings.offscreen_rendering_mode)
743 case ORM_FBO:
744 surface->get_drawable_size = get_drawable_size_fbo;
745 break;
747 case ORM_BACKBUFFER:
748 surface->get_drawable_size = get_drawable_size_backbuffer;
749 break;
751 default:
752 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
753 return WINED3DERR_INVALIDCALL;
756 surface->flags |= SFLAG_INSYSMEM;
758 return WINED3D_OK;
761 static void surface_realize_palette(struct wined3d_surface *surface)
763 struct wined3d_palette *palette = surface->palette;
765 TRACE("surface %p.\n", surface);
767 if (!palette) return;
769 if (surface->resource.format->id == WINED3DFMT_P8_UINT
770 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
772 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
774 /* Make sure the texture is up to date. This call doesn't do
775 * anything if the texture is already up to date. */
776 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
778 /* We want to force a palette refresh, so mark the drawable as not being up to date */
779 if (!surface_is_offscreen(surface))
780 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
782 else
784 if (!(surface->flags & SFLAG_INSYSMEM))
786 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
787 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
789 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
793 if (surface->flags & SFLAG_DIBSECTION)
795 RGBQUAD col[256];
796 unsigned int i;
798 TRACE("Updating the DC's palette.\n");
800 for (i = 0; i < 256; ++i)
802 col[i].rgbRed = palette->palents[i].peRed;
803 col[i].rgbGreen = palette->palents[i].peGreen;
804 col[i].rgbBlue = palette->palents[i].peBlue;
805 col[i].rgbReserved = 0;
807 SetDIBColorTable(surface->hDC, 0, 256, col);
810 /* Propagate the changes to the drawable when we have a palette. */
811 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
812 surface_load_location(surface, surface->draw_binding, NULL);
815 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
817 HRESULT hr;
819 /* If there's no destination surface there is nothing to do. */
820 if (!surface->overlay_dest)
821 return WINED3D_OK;
823 /* Blt calls ModifyLocation on the dest surface, which in turn calls
824 * DrawOverlay to update the overlay. Prevent an endless recursion. */
825 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
826 return WINED3D_OK;
828 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
829 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
830 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3DTEXF_LINEAR);
831 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
833 return hr;
836 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
838 struct wined3d_device *device = surface->resource.device;
839 const RECT *pass_rect = rect;
841 TRACE("surface %p, rect %s, flags %#x.\n",
842 surface, wine_dbgstr_rect(rect), flags);
844 if (flags & WINED3DLOCK_DISCARD)
846 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
847 surface_prepare_system_memory(surface);
848 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
850 else
852 /* surface_load_location() does not check if the rectangle specifies
853 * the full surface. Most callers don't need that, so do it here. */
854 if (rect && !rect->top && !rect->left
855 && rect->right == surface->resource.width
856 && rect->bottom == surface->resource.height)
857 pass_rect = NULL;
858 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
861 if (surface->flags & SFLAG_PBO)
863 const struct wined3d_gl_info *gl_info;
864 struct wined3d_context *context;
866 context = context_acquire(device, NULL);
867 gl_info = context->gl_info;
869 ENTER_GL();
870 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
871 checkGLcall("glBindBufferARB");
873 /* This shouldn't happen but could occur if some other function
874 * didn't handle the PBO properly. */
875 if (surface->resource.allocatedMemory)
876 ERR("The surface already has PBO memory allocated.\n");
878 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
879 checkGLcall("glMapBufferARB");
881 /* Make sure the PBO isn't set anymore in order not to break non-PBO
882 * calls. */
883 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
884 checkGLcall("glBindBufferARB");
886 LEAVE_GL();
887 context_release(context);
890 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
892 if (!rect)
893 surface_add_dirty_rect(surface, NULL);
894 else
896 WINED3DBOX b;
898 b.Left = rect->left;
899 b.Top = rect->top;
900 b.Right = rect->right;
901 b.Bottom = rect->bottom;
902 b.Front = 0;
903 b.Back = 1;
904 surface_add_dirty_rect(surface, &b);
909 static void surface_unmap(struct wined3d_surface *surface)
911 struct wined3d_device *device = surface->resource.device;
912 BOOL fullsurface;
914 TRACE("surface %p.\n", surface);
916 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
918 if (surface->flags & SFLAG_PBO)
920 const struct wined3d_gl_info *gl_info;
921 struct wined3d_context *context;
923 TRACE("Freeing PBO memory.\n");
925 context = context_acquire(device, NULL);
926 gl_info = context->gl_info;
928 ENTER_GL();
929 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
930 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
931 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
932 checkGLcall("glUnmapBufferARB");
933 LEAVE_GL();
934 context_release(context);
936 surface->resource.allocatedMemory = NULL;
939 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
941 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
943 TRACE("Not dirtified, nothing to do.\n");
944 goto done;
947 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
948 || (device->fb.render_targets && surface == device->fb.render_targets[0]))
950 if (!surface->dirtyRect.left && !surface->dirtyRect.top
951 && surface->dirtyRect.right == surface->resource.width
952 && surface->dirtyRect.bottom == surface->resource.height)
954 fullsurface = TRUE;
956 else
958 /* TODO: Proper partial rectangle tracking. */
959 fullsurface = FALSE;
960 surface->flags |= SFLAG_INSYSMEM;
963 surface_load_location(surface, surface->draw_binding, fullsurface ? NULL : &surface->dirtyRect);
965 /* Partial rectangle tracking is not commonly implemented, it is only
966 * done for render targets. INSYSMEM was set before to tell
967 * surface_load_location() where to read the rectangle from.
968 * Indrawable is set because all modifications from the partial
969 * sysmem copy are written back to the drawable, thus the surface is
970 * merged again in the drawable. The sysmem copy is not fully up to
971 * date because only a subrectangle was read in Map(). */
972 if (!fullsurface)
974 surface_modify_location(surface, surface->draw_binding, TRUE);
975 surface_evict_sysmem(surface);
978 surface->dirtyRect.left = surface->resource.width;
979 surface->dirtyRect.top = surface->resource.height;
980 surface->dirtyRect.right = 0;
981 surface->dirtyRect.bottom = 0;
983 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
985 FIXME("Depth / stencil buffer locking is not implemented.\n");
988 done:
989 /* Overlays have to be redrawn manually after changes with the GL implementation */
990 if (surface->overlay_dest)
991 surface_draw_overlay(surface);
994 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
996 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
997 return FALSE;
998 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
999 return FALSE;
1000 return TRUE;
1003 static void wined3d_surface_depth_blt_fbo(const struct wined3d_device *device, struct wined3d_surface *src_surface,
1004 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1006 const struct wined3d_gl_info *gl_info;
1007 struct wined3d_context *context;
1008 DWORD src_mask, dst_mask;
1009 GLbitfield gl_mask;
1011 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1012 device, src_surface, wine_dbgstr_rect(src_rect),
1013 dst_surface, wine_dbgstr_rect(dst_rect));
1015 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1016 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1018 if (src_mask != dst_mask)
1020 ERR("Incompatible formats %s and %s.\n",
1021 debug_d3dformat(src_surface->resource.format->id),
1022 debug_d3dformat(dst_surface->resource.format->id));
1023 return;
1026 if (!src_mask)
1028 ERR("Not a depth / stencil format: %s.\n",
1029 debug_d3dformat(src_surface->resource.format->id));
1030 return;
1033 gl_mask = 0;
1034 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1035 gl_mask |= GL_DEPTH_BUFFER_BIT;
1036 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1037 gl_mask |= GL_STENCIL_BUFFER_BIT;
1039 /* Make sure the locations are up-to-date. Loading the destination
1040 * surface isn't required if the entire surface is overwritten. */
1041 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1042 if (!surface_is_full_rect(dst_surface, dst_rect))
1043 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1045 context = context_acquire(device, NULL);
1046 if (!context->valid)
1048 context_release(context);
1049 WARN("Invalid context, skipping blit.\n");
1050 return;
1053 gl_info = context->gl_info;
1055 ENTER_GL();
1057 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1058 glReadBuffer(GL_NONE);
1059 checkGLcall("glReadBuffer()");
1060 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1062 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1063 context_set_draw_buffer(context, GL_NONE);
1064 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1066 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1068 glDepthMask(GL_TRUE);
1069 context_invalidate_state(context, STATE_RENDER(WINED3DRS_ZWRITEENABLE));
1071 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1073 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1075 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1076 context_invalidate_state(context, STATE_RENDER(WINED3DRS_TWOSIDEDSTENCILMODE));
1078 glStencilMask(~0U);
1079 context_invalidate_state(context, STATE_RENDER(WINED3DRS_STENCILWRITEMASK));
1082 glDisable(GL_SCISSOR_TEST);
1083 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1085 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1086 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1087 checkGLcall("glBlitFramebuffer()");
1089 LEAVE_GL();
1091 if (wined3d_settings.strict_draw_ordering)
1092 wglFlush(); /* Flush to ensure ordering across contexts. */
1094 context_release(context);
1097 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1098 * Depth / stencil is not supported. */
1099 static void surface_blt_fbo(const struct wined3d_device *device, const WINED3DTEXTUREFILTERTYPE filter,
1100 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1101 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1103 const struct wined3d_gl_info *gl_info;
1104 struct wined3d_context *context;
1105 RECT src_rect, dst_rect;
1106 GLenum gl_filter;
1107 GLenum buffer;
1109 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1110 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1111 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1112 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1113 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1115 src_rect = *src_rect_in;
1116 dst_rect = *dst_rect_in;
1118 switch (filter)
1120 case WINED3DTEXF_LINEAR:
1121 gl_filter = GL_LINEAR;
1122 break;
1124 default:
1125 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1126 case WINED3DTEXF_NONE:
1127 case WINED3DTEXF_POINT:
1128 gl_filter = GL_NEAREST;
1129 break;
1132 /* Resolve the source surface first if needed. */
1133 if (src_location == SFLAG_INRB_MULTISAMPLE
1134 && (src_surface->resource.format->id != dst_surface->resource.format->id
1135 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
1136 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
1137 src_location = SFLAG_INRB_RESOLVED;
1139 /* Make sure the locations are up-to-date. Loading the destination
1140 * surface isn't required if the entire surface is overwritten. (And is
1141 * in fact harmful if we're being called by surface_load_location() with
1142 * the purpose of loading the destination surface.) */
1143 surface_load_location(src_surface, src_location, NULL);
1144 if (!surface_is_full_rect(dst_surface, &dst_rect))
1145 surface_load_location(dst_surface, dst_location, NULL);
1147 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1148 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1149 else context = context_acquire(device, NULL);
1151 if (!context->valid)
1153 context_release(context);
1154 WARN("Invalid context, skipping blit.\n");
1155 return;
1158 gl_info = context->gl_info;
1160 if (src_location == SFLAG_INDRAWABLE)
1162 TRACE("Source surface %p is onscreen.\n", src_surface);
1163 buffer = surface_get_gl_buffer(src_surface);
1164 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1166 else
1168 TRACE("Source surface %p is offscreen.\n", src_surface);
1169 buffer = GL_COLOR_ATTACHMENT0;
1172 ENTER_GL();
1173 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1174 glReadBuffer(buffer);
1175 checkGLcall("glReadBuffer()");
1176 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1177 LEAVE_GL();
1179 if (dst_location == SFLAG_INDRAWABLE)
1181 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1182 buffer = surface_get_gl_buffer(dst_surface);
1183 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1185 else
1187 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1188 buffer = GL_COLOR_ATTACHMENT0;
1191 ENTER_GL();
1192 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1193 context_set_draw_buffer(context, buffer);
1194 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1195 context_invalidate_state(context, STATE_FRAMEBUFFER);
1197 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1198 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE));
1199 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE1));
1200 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE2));
1201 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE3));
1203 glDisable(GL_SCISSOR_TEST);
1204 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1206 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1207 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1208 checkGLcall("glBlitFramebuffer()");
1210 LEAVE_GL();
1212 if (wined3d_settings.strict_draw_ordering
1213 || (dst_location == SFLAG_INDRAWABLE
1214 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1215 wglFlush();
1217 context_release(context);
1220 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1221 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
1222 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
1224 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1225 return FALSE;
1227 /* Source and/or destination need to be on the GL side */
1228 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
1229 return FALSE;
1231 switch (blit_op)
1233 case WINED3D_BLIT_OP_COLOR_BLIT:
1234 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1235 return FALSE;
1236 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1237 return FALSE;
1238 break;
1240 case WINED3D_BLIT_OP_DEPTH_BLIT:
1241 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1242 return FALSE;
1243 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1244 return FALSE;
1245 break;
1247 default:
1248 return FALSE;
1251 if (!(src_format->id == dst_format->id
1252 || (is_identity_fixup(src_format->color_fixup)
1253 && is_identity_fixup(dst_format->color_fixup))))
1254 return FALSE;
1256 return TRUE;
1259 /* This function checks if the primary render target uses the 8bit paletted format. */
1260 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1262 if (device->fb.render_targets && device->fb.render_targets[0])
1264 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1265 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1266 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1267 return TRUE;
1269 return FALSE;
1272 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1273 DWORD color, struct wined3d_color *float_color)
1275 const struct wined3d_format *format = surface->resource.format;
1276 const struct wined3d_device *device = surface->resource.device;
1278 switch (format->id)
1280 case WINED3DFMT_P8_UINT:
1281 if (surface->palette)
1283 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1284 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1285 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1287 else
1289 float_color->r = 0.0f;
1290 float_color->g = 0.0f;
1291 float_color->b = 0.0f;
1293 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1294 break;
1296 case WINED3DFMT_B5G6R5_UNORM:
1297 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1298 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1299 float_color->b = (color & 0x1f) / 31.0f;
1300 float_color->a = 1.0f;
1301 break;
1303 case WINED3DFMT_B8G8R8_UNORM:
1304 case WINED3DFMT_B8G8R8X8_UNORM:
1305 float_color->r = D3DCOLOR_R(color);
1306 float_color->g = D3DCOLOR_G(color);
1307 float_color->b = D3DCOLOR_B(color);
1308 float_color->a = 1.0f;
1309 break;
1311 case WINED3DFMT_B8G8R8A8_UNORM:
1312 float_color->r = D3DCOLOR_R(color);
1313 float_color->g = D3DCOLOR_G(color);
1314 float_color->b = D3DCOLOR_B(color);
1315 float_color->a = D3DCOLOR_A(color);
1316 break;
1318 default:
1319 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1320 return FALSE;
1323 return TRUE;
1326 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1328 const struct wined3d_format *format = surface->resource.format;
1330 switch (format->id)
1332 case WINED3DFMT_S1_UINT_D15_UNORM:
1333 *float_depth = depth / (float)0x00007fff;
1334 break;
1336 case WINED3DFMT_D16_UNORM:
1337 *float_depth = depth / (float)0x0000ffff;
1338 break;
1340 case WINED3DFMT_D24_UNORM_S8_UINT:
1341 case WINED3DFMT_X8D24_UNORM:
1342 *float_depth = depth / (float)0x00ffffff;
1343 break;
1345 case WINED3DFMT_D32_UNORM:
1346 *float_depth = depth / (float)0xffffffff;
1347 break;
1349 default:
1350 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1351 return FALSE;
1354 return TRUE;
1357 /* Do not call while under the GL lock. */
1358 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1360 const struct wined3d_resource *resource = &surface->resource;
1361 struct wined3d_device *device = resource->device;
1362 const struct blit_shader *blitter;
1364 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1365 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1366 if (!blitter)
1368 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1369 return WINED3DERR_INVALIDCALL;
1372 return blitter->depth_fill(device, surface, rect, depth);
1375 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1376 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1378 struct wined3d_device *device = src_surface->resource.device;
1380 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1381 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1382 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1383 return WINED3DERR_INVALIDCALL;
1385 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1387 surface_modify_ds_location(dst_surface, SFLAG_DS_OFFSCREEN,
1388 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1389 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
1391 return WINED3D_OK;
1394 /* Do not call while under the GL lock. */
1395 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1396 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1397 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1399 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1400 struct wined3d_device *device = dst_surface->resource.device;
1401 DWORD src_ds_flags, dst_ds_flags;
1402 RECT src_rect, dst_rect;
1403 BOOL scale, convert;
1405 static const DWORD simple_blit = WINEDDBLT_ASYNC
1406 | WINEDDBLT_COLORFILL
1407 | WINEDDBLT_WAIT
1408 | WINEDDBLT_DEPTHFILL
1409 | WINEDDBLT_DONOTWAIT;
1411 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1412 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1413 flags, fx, debug_d3dtexturefiltertype(filter));
1414 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1416 if (fx)
1418 TRACE("dwSize %#x.\n", fx->dwSize);
1419 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
1420 TRACE("dwROP %#x.\n", fx->dwROP);
1421 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
1422 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
1423 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
1424 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
1425 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
1426 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
1427 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
1428 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
1429 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
1430 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
1431 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
1432 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
1433 TRACE("dwReserved %#x.\n", fx->dwReserved);
1434 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
1435 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
1436 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
1437 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
1438 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
1439 TRACE("ddckDestColorkey {%#x, %#x}.\n",
1440 fx->ddckDestColorkey.dwColorSpaceLowValue,
1441 fx->ddckDestColorkey.dwColorSpaceHighValue);
1442 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
1443 fx->ddckSrcColorkey.dwColorSpaceLowValue,
1444 fx->ddckSrcColorkey.dwColorSpaceHighValue);
1447 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1449 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1450 return WINEDDERR_SURFACEBUSY;
1453 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1455 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1456 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1457 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1458 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1459 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1461 /* The destination rect can be out of bounds on the condition
1462 * that a clipper is set for the surface. */
1463 if (dst_surface->clipper)
1464 FIXME("Blit clipping not implemented.\n");
1465 else
1466 WARN("The application gave us a bad destination rectangle without a clipper set.\n");
1467 return WINEDDERR_INVALIDRECT;
1470 if (src_surface)
1472 surface_get_rect(src_surface, src_rect_in, &src_rect);
1474 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1475 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1476 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1477 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1478 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1480 WARN("Application gave us bad source rectangle for Blt.\n");
1481 return WINEDDERR_INVALIDRECT;
1484 else
1486 memset(&src_rect, 0, sizeof(src_rect));
1489 if (!fx || !(fx->dwDDFX))
1490 flags &= ~WINEDDBLT_DDFX;
1492 if (flags & WINEDDBLT_WAIT)
1493 flags &= ~WINEDDBLT_WAIT;
1495 if (flags & WINEDDBLT_ASYNC)
1497 static unsigned int once;
1499 if (!once++)
1500 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1501 flags &= ~WINEDDBLT_ASYNC;
1504 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1505 if (flags & WINEDDBLT_DONOTWAIT)
1507 static unsigned int once;
1509 if (!once++)
1510 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1511 flags &= ~WINEDDBLT_DONOTWAIT;
1514 if (!device->d3d_initialized)
1516 WARN("D3D not initialized, using fallback.\n");
1517 goto cpu;
1520 /* We want to avoid invalidating the sysmem location for converted
1521 * surfaces, since otherwise we'd have to convert the data back when
1522 * locking them. */
1523 if (dst_surface->flags & SFLAG_CONVERTED)
1525 WARN("Converted surface, using CPU blit.\n");
1526 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1529 if (flags & ~simple_blit)
1531 WARN("Using fallback for complex blit (%#x).\n", flags);
1532 goto fallback;
1535 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1536 src_swapchain = src_surface->container.u.swapchain;
1537 else
1538 src_swapchain = NULL;
1540 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1541 dst_swapchain = dst_surface->container.u.swapchain;
1542 else
1543 dst_swapchain = NULL;
1545 /* This isn't strictly needed. FBO blits for example could deal with
1546 * cross-swapchain blits by first downloading the source to a texture
1547 * before switching to the destination context. We just have this here to
1548 * not have to deal with the issue, since cross-swapchain blits should be
1549 * rare. */
1550 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1552 FIXME("Using fallback for cross-swapchain blit.\n");
1553 goto fallback;
1556 scale = src_surface
1557 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
1558 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
1559 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
1561 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1562 if (src_surface)
1563 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1564 else
1565 src_ds_flags = 0;
1567 if (src_ds_flags || dst_ds_flags)
1569 if (flags & WINEDDBLT_DEPTHFILL)
1571 float depth;
1573 TRACE("Depth fill.\n");
1575 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1576 return WINED3DERR_INVALIDCALL;
1578 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1579 return WINED3D_OK;
1581 else
1583 /* Accessing depth / stencil surfaces is supposed to fail while in
1584 * a scene, except for fills, which seem to work. */
1585 if (device->inScene)
1587 WARN("Rejecting depth / stencil access while in scene.\n");
1588 return WINED3DERR_INVALIDCALL;
1591 if (src_ds_flags != dst_ds_flags)
1593 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1594 return WINED3DERR_INVALIDCALL;
1597 if (src_rect.top || src_rect.left
1598 || src_rect.bottom != src_surface->resource.height
1599 || src_rect.right != src_surface->resource.width)
1601 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1602 wine_dbgstr_rect(&src_rect));
1603 return WINED3DERR_INVALIDCALL;
1606 if (dst_rect.top || dst_rect.left
1607 || dst_rect.bottom != dst_surface->resource.height
1608 || dst_rect.right != dst_surface->resource.width)
1610 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1611 wine_dbgstr_rect(&src_rect));
1612 return WINED3DERR_INVALIDCALL;
1615 if (scale)
1617 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1618 return WINED3DERR_INVALIDCALL;
1621 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1622 return WINED3D_OK;
1625 else
1627 /* In principle this would apply to depth blits as well, but we don't
1628 * implement those in the CPU blitter at the moment. */
1629 if ((dst_surface->flags & SFLAG_INSYSMEM)
1630 && (!src_surface || (src_surface->flags & SFLAG_INSYSMEM)))
1632 if (scale)
1633 TRACE("Not doing sysmem blit because of scaling.\n");
1634 else if (convert)
1635 TRACE("Not doing sysmem blit because of format conversion.\n");
1636 else
1637 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1640 if (flags & WINEDDBLT_COLORFILL)
1642 struct wined3d_color color;
1644 TRACE("Color fill.\n");
1646 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1647 goto fallback;
1649 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1650 return WINED3D_OK;
1652 else
1654 TRACE("Color blit.\n");
1656 /* Upload */
1657 if ((src_surface->flags & SFLAG_INSYSMEM) && !(dst_surface->flags & SFLAG_INSYSMEM))
1659 if (scale)
1660 TRACE("Not doing upload because of scaling.\n");
1661 else if (convert)
1662 TRACE("Not doing upload because of format conversion.\n");
1663 else
1665 POINT dst_point = {dst_rect.left, dst_rect.top};
1667 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
1669 if (!surface_is_offscreen(dst_surface))
1670 surface_load_location(dst_surface, dst_surface->draw_binding, NULL);
1671 return WINED3D_OK;
1676 /* Use present for back -> front blits. The idea behind this is
1677 * that present is potentially faster than a blit, in particular
1678 * when FBO blits aren't available. Some ddraw applications like
1679 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
1680 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
1681 * applications can't blit directly to the frontbuffer. */
1682 if (dst_swapchain && dst_swapchain->back_buffers
1683 && dst_surface == dst_swapchain->front_buffer
1684 && src_surface == dst_swapchain->back_buffers[0])
1686 WINED3DSWAPEFFECT swap_effect = dst_swapchain->presentParms.SwapEffect;
1688 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1690 /* Set the swap effect to COPY, we don't want the backbuffer
1691 * to become undefined. */
1692 dst_swapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
1693 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
1694 dst_swapchain->presentParms.SwapEffect = swap_effect;
1696 return WINED3D_OK;
1699 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1700 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1701 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1703 TRACE("Using FBO blit.\n");
1705 surface_blt_fbo(device, filter,
1706 src_surface, src_surface->draw_binding, &src_rect,
1707 dst_surface, dst_surface->draw_binding, &dst_rect);
1708 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
1709 return WINED3D_OK;
1712 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1713 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1714 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1716 TRACE("Using arbfp blit.\n");
1718 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1719 return WINED3D_OK;
1724 fallback:
1726 /* Special cases for render targets. */
1727 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1728 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1730 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1731 src_surface, &src_rect, flags, fx, filter)))
1732 return WINED3D_OK;
1735 cpu:
1737 /* For the rest call the X11 surface implementation. For render targets
1738 * this should be implemented OpenGL accelerated in BltOverride, other
1739 * blits are rather rare. */
1740 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1743 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1744 struct wined3d_surface *render_target)
1746 TRACE("surface %p, render_target %p.\n", surface, render_target);
1748 /* TODO: Check surface sizes, pools, etc. */
1750 if (render_target->resource.multisample_type)
1751 return WINED3DERR_INVALIDCALL;
1753 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3DTEXF_POINT);
1756 /* Context activation is done by the caller. */
1757 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1759 if (!surface->resource.heapMemory)
1761 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1762 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1763 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1766 ENTER_GL();
1767 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1768 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1769 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1770 surface->resource.size, surface->resource.allocatedMemory));
1771 checkGLcall("glGetBufferSubDataARB");
1772 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1773 checkGLcall("glDeleteBuffersARB");
1774 LEAVE_GL();
1776 surface->pbo = 0;
1777 surface->flags &= ~SFLAG_PBO;
1780 /* Do not call while under the GL lock. */
1781 static void surface_unload(struct wined3d_resource *resource)
1783 struct wined3d_surface *surface = surface_from_resource(resource);
1784 struct wined3d_renderbuffer_entry *entry, *entry2;
1785 struct wined3d_device *device = resource->device;
1786 const struct wined3d_gl_info *gl_info;
1787 struct wined3d_context *context;
1789 TRACE("surface %p.\n", surface);
1791 if (resource->pool == WINED3DPOOL_DEFAULT)
1793 /* Default pool resources are supposed to be destroyed before Reset is called.
1794 * Implicit resources stay however. So this means we have an implicit render target
1795 * or depth stencil. The content may be destroyed, but we still have to tear down
1796 * opengl resources, so we cannot leave early.
1798 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1799 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1800 * or the depth stencil into an FBO the texture or render buffer will be removed
1801 * and all flags get lost
1803 surface_init_sysmem(surface);
1804 /* We also get here when the ddraw swapchain is destroyed, for example
1805 * for a mode switch. In this case this surface won't necessarily be
1806 * an implicit surface. We have to mark it lost so that the
1807 * application can restore it after the mode switch. */
1808 surface->flags |= SFLAG_LOST;
1810 else
1812 /* Load the surface into system memory */
1813 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1814 surface_modify_location(surface, surface->draw_binding, FALSE);
1816 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1817 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1818 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1820 context = context_acquire(device, NULL);
1821 gl_info = context->gl_info;
1823 /* Destroy PBOs, but load them into real sysmem before */
1824 if (surface->flags & SFLAG_PBO)
1825 surface_remove_pbo(surface, gl_info);
1827 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1828 * all application-created targets the application has to release the surface
1829 * before calling _Reset
1831 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1833 ENTER_GL();
1834 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1835 LEAVE_GL();
1836 list_remove(&entry->entry);
1837 HeapFree(GetProcessHeap(), 0, entry);
1839 list_init(&surface->renderbuffers);
1840 surface->current_renderbuffer = NULL;
1842 ENTER_GL();
1844 /* If we're in a texture, the texture name belongs to the texture.
1845 * Otherwise, destroy it. */
1846 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1848 glDeleteTextures(1, &surface->texture_name);
1849 surface->texture_name = 0;
1850 glDeleteTextures(1, &surface->texture_name_srgb);
1851 surface->texture_name_srgb = 0;
1853 if (surface->rb_multisample)
1855 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1856 surface->rb_multisample = 0;
1858 if (surface->rb_resolved)
1860 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1861 surface->rb_resolved = 0;
1864 LEAVE_GL();
1866 context_release(context);
1868 resource_unload(resource);
1871 static const struct wined3d_resource_ops surface_resource_ops =
1873 surface_unload,
1876 static const struct wined3d_surface_ops surface_ops =
1878 surface_private_setup,
1879 surface_realize_palette,
1880 surface_map,
1881 surface_unmap,
1884 /*****************************************************************************
1885 * Initializes the GDI surface, aka creates the DIB section we render to
1886 * The DIB section creation is done by calling GetDC, which will create the
1887 * section and releasing the dc to allow the app to use it. The dib section
1888 * will stay until the surface is released
1890 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1891 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1892 * avoid confusion in the shared surface code.
1894 * Returns:
1895 * WINED3D_OK on success
1896 * The return values of called methods on failure
1898 *****************************************************************************/
1899 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1901 HRESULT hr;
1903 TRACE("surface %p.\n", surface);
1905 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1907 ERR("Overlays not yet supported by GDI surfaces.\n");
1908 return WINED3DERR_INVALIDCALL;
1911 /* Sysmem textures have memory already allocated - release it,
1912 * this avoids an unnecessary memcpy. */
1913 hr = surface_create_dib_section(surface);
1914 if (SUCCEEDED(hr))
1916 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1917 surface->resource.heapMemory = NULL;
1918 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1921 /* We don't mind the nonpow2 stuff in GDI. */
1922 surface->pow2Width = surface->resource.width;
1923 surface->pow2Height = surface->resource.height;
1925 return WINED3D_OK;
1928 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1930 struct wined3d_palette *palette = surface->palette;
1932 TRACE("surface %p.\n", surface);
1934 if (!palette) return;
1936 if (surface->flags & SFLAG_DIBSECTION)
1938 RGBQUAD col[256];
1939 unsigned int i;
1941 TRACE("Updating the DC's palette.\n");
1943 for (i = 0; i < 256; ++i)
1945 col[i].rgbRed = palette->palents[i].peRed;
1946 col[i].rgbGreen = palette->palents[i].peGreen;
1947 col[i].rgbBlue = palette->palents[i].peBlue;
1948 col[i].rgbReserved = 0;
1950 SetDIBColorTable(surface->hDC, 0, 256, col);
1953 /* Update the image because of the palette change. Some games like e.g.
1954 * Red Alert call SetEntries a lot to implement fading. */
1955 /* Tell the swapchain to update the screen. */
1956 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1958 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1959 if (surface == swapchain->front_buffer)
1961 x11_copy_to_screen(swapchain, NULL);
1966 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
1968 TRACE("surface %p, rect %s, flags %#x.\n",
1969 surface, wine_dbgstr_rect(rect), flags);
1971 if (!surface->resource.allocatedMemory)
1973 /* This happens on gdi surfaces if the application set a user pointer
1974 * and resets it. Recreate the DIB section. */
1975 surface_create_dib_section(surface);
1976 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1980 static void gdi_surface_unmap(struct wined3d_surface *surface)
1982 TRACE("surface %p.\n", surface);
1984 /* Tell the swapchain to update the screen. */
1985 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1987 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1988 if (surface == swapchain->front_buffer)
1990 x11_copy_to_screen(swapchain, &surface->lockedRect);
1994 memset(&surface->lockedRect, 0, sizeof(RECT));
1997 static const struct wined3d_surface_ops gdi_surface_ops =
1999 gdi_surface_private_setup,
2000 gdi_surface_realize_palette,
2001 gdi_surface_map,
2002 gdi_surface_unmap,
2005 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2007 GLuint *name;
2008 DWORD flag;
2010 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2012 if(srgb)
2014 name = &surface->texture_name_srgb;
2015 flag = SFLAG_INSRGBTEX;
2017 else
2019 name = &surface->texture_name;
2020 flag = SFLAG_INTEXTURE;
2023 if (!*name && new_name)
2025 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2026 * surface has no texture name yet. See if we can get rid of this. */
2027 if (surface->flags & flag)
2028 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2029 surface_modify_location(surface, flag, FALSE);
2032 *name = new_name;
2033 surface_force_reload(surface);
2036 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2038 TRACE("surface %p, target %#x.\n", surface, target);
2040 if (surface->texture_target != target)
2042 if (target == GL_TEXTURE_RECTANGLE_ARB)
2044 surface->flags &= ~SFLAG_NORMCOORD;
2046 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2048 surface->flags |= SFLAG_NORMCOORD;
2051 surface->texture_target = target;
2052 surface_force_reload(surface);
2055 /* Context activation is done by the caller. */
2056 void surface_bind(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
2058 TRACE("surface %p, context %p, srgb %#x.\n", surface, context, srgb);
2060 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2062 struct wined3d_texture *texture = surface->container.u.texture;
2064 TRACE("Passing to container (%p).\n", texture);
2065 texture->texture_ops->texture_bind(texture, context, srgb);
2067 else
2069 if (surface->texture_level)
2071 ERR("Standalone surface %p is non-zero texture level %u.\n",
2072 surface, surface->texture_level);
2075 if (srgb)
2076 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2078 ENTER_GL();
2080 if (!surface->texture_name)
2082 glGenTextures(1, &surface->texture_name);
2083 checkGLcall("glGenTextures");
2085 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2087 context_bind_texture(context, surface->texture_target, surface->texture_name);
2088 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2089 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2090 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2091 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2092 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2093 checkGLcall("glTexParameteri");
2095 else
2097 context_bind_texture(context, surface->texture_target, surface->texture_name);
2100 LEAVE_GL();
2104 /* This call just downloads data, the caller is responsible for binding the
2105 * correct texture. */
2106 /* Context activation is done by the caller. */
2107 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2109 const struct wined3d_format *format = surface->resource.format;
2111 /* Only support read back of converted P8 surfaces. */
2112 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2114 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
2115 return;
2118 ENTER_GL();
2120 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2122 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2123 surface, surface->texture_level, format->glFormat, format->glType,
2124 surface->resource.allocatedMemory);
2126 if (surface->flags & SFLAG_PBO)
2128 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2129 checkGLcall("glBindBufferARB");
2130 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2131 checkGLcall("glGetCompressedTexImageARB");
2132 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2133 checkGLcall("glBindBufferARB");
2135 else
2137 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2138 surface->texture_level, surface->resource.allocatedMemory));
2139 checkGLcall("glGetCompressedTexImageARB");
2142 LEAVE_GL();
2144 else
2146 void *mem;
2147 GLenum gl_format = format->glFormat;
2148 GLenum gl_type = format->glType;
2149 int src_pitch = 0;
2150 int dst_pitch = 0;
2152 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2153 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2155 gl_format = GL_ALPHA;
2156 gl_type = GL_UNSIGNED_BYTE;
2159 if (surface->flags & SFLAG_NONPOW2)
2161 unsigned char alignment = surface->resource.device->surface_alignment;
2162 src_pitch = format->byte_count * surface->pow2Width;
2163 dst_pitch = wined3d_surface_get_pitch(surface);
2164 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2165 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2167 else
2169 mem = surface->resource.allocatedMemory;
2172 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2173 surface, surface->texture_level, gl_format, gl_type, mem);
2175 if (surface->flags & SFLAG_PBO)
2177 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2178 checkGLcall("glBindBufferARB");
2180 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2181 checkGLcall("glGetTexImage");
2183 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2184 checkGLcall("glBindBufferARB");
2186 else
2188 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2189 checkGLcall("glGetTexImage");
2191 LEAVE_GL();
2193 if (surface->flags & SFLAG_NONPOW2)
2195 const BYTE *src_data;
2196 BYTE *dst_data;
2197 UINT y;
2199 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2200 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2201 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2203 * We're doing this...
2205 * instead of boxing the texture :
2206 * |<-texture width ->| -->pow2width| /\
2207 * |111111111111111111| | |
2208 * |222 Texture 222222| boxed empty | texture height
2209 * |3333 Data 33333333| | |
2210 * |444444444444444444| | \/
2211 * ----------------------------------- |
2212 * | boxed empty | boxed empty | pow2height
2213 * | | | \/
2214 * -----------------------------------
2217 * we're repacking the data to the expected texture width
2219 * |<-texture width ->| -->pow2width| /\
2220 * |111111111111111111222222222222222| |
2221 * |222333333333333333333444444444444| texture height
2222 * |444444 | |
2223 * | | \/
2224 * | | |
2225 * | empty | pow2height
2226 * | | \/
2227 * -----------------------------------
2229 * == is the same as
2231 * |<-texture width ->| /\
2232 * |111111111111111111|
2233 * |222222222222222222|texture height
2234 * |333333333333333333|
2235 * |444444444444444444| \/
2236 * --------------------
2238 * this also means that any references to allocatedMemory should work with the data as if were a
2239 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2241 * internally the texture is still stored in a boxed format so any references to textureName will
2242 * get a boxed texture with width pow2width and not a texture of width resource.width.
2244 * Performance should not be an issue, because applications normally do not lock the surfaces when
2245 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2246 * and doesn't have to be re-read. */
2247 src_data = mem;
2248 dst_data = surface->resource.allocatedMemory;
2249 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2250 for (y = 1; y < surface->resource.height; ++y)
2252 /* skip the first row */
2253 src_data += src_pitch;
2254 dst_data += dst_pitch;
2255 memcpy(dst_data, src_data, dst_pitch);
2258 HeapFree(GetProcessHeap(), 0, mem);
2262 /* Surface has now been downloaded */
2263 surface->flags |= SFLAG_INSYSMEM;
2266 /* This call just uploads data, the caller is responsible for binding the
2267 * correct texture. */
2268 /* Context activation is done by the caller. */
2269 static void surface_upload_data(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2270 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
2271 BOOL srgb, const struct wined3d_bo_address *data)
2273 UINT update_w = src_rect->right - src_rect->left;
2274 UINT update_h = src_rect->bottom - src_rect->top;
2276 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
2277 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
2278 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2280 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2281 update_h *= format->heightscale;
2283 ENTER_GL();
2285 if (data->buffer_object)
2287 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2288 checkGLcall("glBindBufferARB");
2291 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2293 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2294 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2295 const BYTE *addr = data->addr;
2296 GLenum internal;
2298 addr += (src_rect->top / format->block_height) * src_pitch;
2299 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2301 if (srgb)
2302 internal = format->glGammaInternal;
2303 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2304 internal = format->rtInternal;
2305 else
2306 internal = format->glInternal;
2308 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2309 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2310 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2312 if (row_length == src_pitch)
2314 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2315 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2317 else
2319 UINT row, y;
2321 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2322 * can't use the unpack row length like below. */
2323 for (row = 0, y = dst_point->y; row < row_count; ++row)
2325 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2326 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2327 y += format->block_height;
2328 addr += src_pitch;
2331 checkGLcall("glCompressedTexSubImage2DARB");
2333 else
2335 const BYTE *addr = data->addr;
2337 addr += src_rect->top * src_pitch;
2338 addr += src_rect->left * format->byte_count;
2340 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2341 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2342 update_w, update_h, format->glFormat, format->glType, addr);
2344 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
2345 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2346 update_w, update_h, format->glFormat, format->glType, addr);
2347 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2348 checkGLcall("glTexSubImage2D");
2351 if (data->buffer_object)
2353 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2354 checkGLcall("glBindBufferARB");
2357 LEAVE_GL();
2359 if (wined3d_settings.strict_draw_ordering)
2360 wglFlush();
2362 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2364 struct wined3d_device *device = surface->resource.device;
2365 unsigned int i;
2367 for (i = 0; i < device->context_count; ++i)
2369 context_surface_update(device->contexts[i], surface);
2374 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
2375 struct wined3d_surface *src_surface, const RECT *src_rect)
2377 const struct wined3d_format *src_format;
2378 const struct wined3d_format *dst_format;
2379 const struct wined3d_gl_info *gl_info;
2380 struct wined3d_context *context;
2381 struct wined3d_bo_address data;
2382 struct wined3d_format format;
2383 UINT update_w, update_h;
2384 CONVERT_TYPES convert;
2385 UINT dst_w, dst_h;
2386 UINT src_w, src_h;
2387 UINT src_pitch;
2388 POINT p;
2389 RECT r;
2391 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
2392 dst_surface, wine_dbgstr_point(dst_point),
2393 src_surface, wine_dbgstr_rect(src_rect));
2395 src_format = src_surface->resource.format;
2396 dst_format = dst_surface->resource.format;
2398 if (src_format->id != dst_format->id)
2400 WARN("Source and destination surfaces should have the same format.\n");
2401 return WINED3DERR_INVALIDCALL;
2404 if (!dst_point)
2406 p.x = 0;
2407 p.y = 0;
2408 dst_point = &p;
2410 else if (dst_point->x < 0 || dst_point->y < 0)
2412 WARN("Invalid destination point.\n");
2413 return WINED3DERR_INVALIDCALL;
2416 if (!src_rect)
2418 r.left = 0;
2419 r.top = 0;
2420 r.right = src_surface->resource.width;
2421 r.bottom = src_surface->resource.height;
2422 src_rect = &r;
2424 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
2425 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
2427 WARN("Invalid source rectangle.\n");
2428 return WINED3DERR_INVALIDCALL;
2431 src_w = src_surface->resource.width;
2432 src_h = src_surface->resource.height;
2434 dst_w = dst_surface->resource.width;
2435 dst_h = dst_surface->resource.height;
2437 update_w = src_rect->right - src_rect->left;
2438 update_h = src_rect->bottom - src_rect->top;
2440 if (update_w > dst_w || dst_point->x > dst_w - update_w
2441 || update_h > dst_h || dst_point->y > dst_h - update_h)
2443 WARN("Destination out of bounds.\n");
2444 return WINED3DERR_INVALIDCALL;
2447 /* NPOT block sizes would be silly. */
2448 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS)
2449 && ((update_w & (src_format->block_width - 1) || update_h & (src_format->block_height - 1))
2450 && (src_w != update_w || dst_w != update_w || src_h != update_h || dst_h != update_h)))
2452 WARN("Update rect not block-aligned.\n");
2453 return WINED3DERR_INVALIDCALL;
2456 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
2457 d3dfmt_get_conv(dst_surface, FALSE, TRUE, &format, &convert);
2458 if (convert != NO_CONVERSION || format.convert)
2460 RECT dst_rect = {dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h};
2461 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3DTEXF_POINT);
2464 context = context_acquire(dst_surface->resource.device, NULL);
2465 gl_info = context->gl_info;
2467 /* Only load the surface for partial updates. For newly allocated texture
2468 * the texture wouldn't be the current location, and we'd upload zeroes
2469 * just to overwrite them again. */
2470 if (update_w == dst_w && update_h == dst_h)
2471 surface_prepare_texture(dst_surface, context, FALSE);
2472 else
2473 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
2474 surface_bind(dst_surface, context, FALSE);
2476 data.buffer_object = src_surface->pbo;
2477 data.addr = src_surface->resource.allocatedMemory;
2478 src_pitch = wined3d_surface_get_pitch(src_surface);
2480 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
2482 invalidate_active_texture(dst_surface->resource.device, context);
2484 context_release(context);
2486 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
2487 return WINED3D_OK;
2490 /* This call just allocates the texture, the caller is responsible for binding
2491 * the correct texture. */
2492 /* Context activation is done by the caller. */
2493 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2494 const struct wined3d_format *format, BOOL srgb)
2496 BOOL enable_client_storage = FALSE;
2497 GLsizei width = surface->pow2Width;
2498 GLsizei height = surface->pow2Height;
2499 const BYTE *mem = NULL;
2500 GLenum internal;
2502 if (srgb)
2504 internal = format->glGammaInternal;
2506 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2508 internal = format->rtInternal;
2510 else
2512 internal = format->glInternal;
2515 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2517 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",
2518 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2519 internal, width, height, format->glFormat, format->glType);
2521 ENTER_GL();
2523 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2525 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2526 || !surface->resource.allocatedMemory)
2528 /* In some cases we want to disable client storage.
2529 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2530 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2531 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2532 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2534 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2535 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2536 surface->flags &= ~SFLAG_CLIENT;
2537 enable_client_storage = TRUE;
2539 else
2541 surface->flags |= SFLAG_CLIENT;
2543 /* Point OpenGL to our allocated texture memory. Do not use
2544 * resource.allocatedMemory here because it might point into a
2545 * PBO. Instead use heapMemory, but get the alignment right. */
2546 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2547 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2551 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2553 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2554 internal, width, height, 0, surface->resource.size, mem));
2555 checkGLcall("glCompressedTexImage2DARB");
2557 else
2559 glTexImage2D(surface->texture_target, surface->texture_level,
2560 internal, width, height, 0, format->glFormat, format->glType, mem);
2561 checkGLcall("glTexImage2D");
2564 if(enable_client_storage) {
2565 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2566 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2568 LEAVE_GL();
2571 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2572 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2573 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2574 /* GL locking is done by the caller */
2575 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2577 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2578 struct wined3d_renderbuffer_entry *entry;
2579 GLuint renderbuffer = 0;
2580 unsigned int src_width, src_height;
2581 unsigned int width, height;
2583 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2585 width = rt->pow2Width;
2586 height = rt->pow2Height;
2588 else
2590 width = surface->pow2Width;
2591 height = surface->pow2Height;
2594 src_width = surface->pow2Width;
2595 src_height = surface->pow2Height;
2597 /* A depth stencil smaller than the render target is not valid */
2598 if (width > src_width || height > src_height) return;
2600 /* Remove any renderbuffer set if the sizes match */
2601 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2602 || (width == src_width && height == src_height))
2604 surface->current_renderbuffer = NULL;
2605 return;
2608 /* Look if we've already got a renderbuffer of the correct dimensions */
2609 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2611 if (entry->width == width && entry->height == height)
2613 renderbuffer = entry->id;
2614 surface->current_renderbuffer = entry;
2615 break;
2619 if (!renderbuffer)
2621 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2622 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2623 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2624 surface->resource.format->glInternal, width, height);
2626 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2627 entry->width = width;
2628 entry->height = height;
2629 entry->id = renderbuffer;
2630 list_add_head(&surface->renderbuffers, &entry->entry);
2632 surface->current_renderbuffer = entry;
2635 checkGLcall("set_compatible_renderbuffer");
2638 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2640 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2642 TRACE("surface %p.\n", surface);
2644 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2646 ERR("Surface %p is not on a swapchain.\n", surface);
2647 return GL_NONE;
2650 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2652 if (swapchain->render_to_fbo)
2654 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2655 return GL_COLOR_ATTACHMENT0;
2657 TRACE("Returning GL_BACK\n");
2658 return GL_BACK;
2660 else if (surface == swapchain->front_buffer)
2662 TRACE("Returning GL_FRONT\n");
2663 return GL_FRONT;
2666 FIXME("Higher back buffer, returning GL_BACK\n");
2667 return GL_BACK;
2670 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2671 void surface_add_dirty_rect(struct wined3d_surface *surface, const WINED3DBOX *dirty_rect)
2673 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2675 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2676 /* No partial locking for textures yet. */
2677 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2679 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2680 if (dirty_rect)
2682 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->Left);
2683 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->Top);
2684 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->Right);
2685 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->Bottom);
2687 else
2689 surface->dirtyRect.left = 0;
2690 surface->dirtyRect.top = 0;
2691 surface->dirtyRect.right = surface->resource.width;
2692 surface->dirtyRect.bottom = surface->resource.height;
2695 /* if the container is a texture then mark it dirty. */
2696 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2698 TRACE("Passing to container.\n");
2699 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2703 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2705 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2706 BOOL ck_changed;
2708 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2710 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2712 ERR("Not supported on scratch surfaces.\n");
2713 return WINED3DERR_INVALIDCALL;
2716 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2718 /* Reload if either the texture and sysmem have different ideas about the
2719 * color key, or the actual key values changed. */
2720 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2721 && (surface->glCKey.dwColorSpaceLowValue != surface->SrcBltCKey.dwColorSpaceLowValue
2722 || surface->glCKey.dwColorSpaceHighValue != surface->SrcBltCKey.dwColorSpaceHighValue)))
2724 TRACE("Reloading because of color keying\n");
2725 /* To perform the color key conversion we need a sysmem copy of
2726 * the surface. Make sure we have it. */
2728 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2729 /* Make sure the texture is reloaded because of the color key change,
2730 * this kills performance though :( */
2731 /* TODO: This is not necessarily needed with hw palettized texture support. */
2732 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2733 /* Switching color keying on / off may change the internal format. */
2734 if (ck_changed)
2735 surface_force_reload(surface);
2737 else if (!(surface->flags & flag))
2739 TRACE("Reloading because surface is dirty.\n");
2741 else
2743 TRACE("surface is already in texture\n");
2744 return WINED3D_OK;
2747 /* No partial locking for textures yet. */
2748 surface_load_location(surface, flag, NULL);
2749 surface_evict_sysmem(surface);
2751 return WINED3D_OK;
2754 /* See also float_16_to_32() in wined3d_private.h */
2755 static inline unsigned short float_32_to_16(const float *in)
2757 int exp = 0;
2758 float tmp = fabsf(*in);
2759 unsigned int mantissa;
2760 unsigned short ret;
2762 /* Deal with special numbers */
2763 if (*in == 0.0f)
2764 return 0x0000;
2765 if (isnan(*in))
2766 return 0x7c01;
2767 if (isinf(*in))
2768 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2770 if (tmp < powf(2, 10))
2774 tmp = tmp * 2.0f;
2775 exp--;
2776 } while (tmp < powf(2, 10));
2778 else if (tmp >= powf(2, 11))
2782 tmp /= 2.0f;
2783 exp++;
2784 } while (tmp >= powf(2, 11));
2787 mantissa = (unsigned int)tmp;
2788 if (tmp - mantissa >= 0.5f)
2789 ++mantissa; /* Round to nearest, away from zero. */
2791 exp += 10; /* Normalize the mantissa. */
2792 exp += 15; /* Exponent is encoded with excess 15. */
2794 if (exp > 30) /* too big */
2796 ret = 0x7c00; /* INF */
2798 else if (exp <= 0)
2800 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2801 while (exp <= 0)
2803 mantissa = mantissa >> 1;
2804 ++exp;
2806 ret = mantissa & 0x3ff;
2808 else
2810 ret = (exp << 10) | (mantissa & 0x3ff);
2813 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2814 return ret;
2817 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2819 ULONG refcount;
2821 TRACE("Surface %p, container %p of type %#x.\n",
2822 surface, surface->container.u.base, surface->container.type);
2824 switch (surface->container.type)
2826 case WINED3D_CONTAINER_TEXTURE:
2827 return wined3d_texture_incref(surface->container.u.texture);
2829 case WINED3D_CONTAINER_SWAPCHAIN:
2830 return wined3d_swapchain_incref(surface->container.u.swapchain);
2832 default:
2833 ERR("Unhandled container type %#x.\n", surface->container.type);
2834 case WINED3D_CONTAINER_NONE:
2835 break;
2838 refcount = InterlockedIncrement(&surface->resource.ref);
2839 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2841 return refcount;
2844 /* Do not call while under the GL lock. */
2845 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2847 ULONG refcount;
2849 TRACE("Surface %p, container %p of type %#x.\n",
2850 surface, surface->container.u.base, surface->container.type);
2852 switch (surface->container.type)
2854 case WINED3D_CONTAINER_TEXTURE:
2855 return wined3d_texture_decref(surface->container.u.texture);
2857 case WINED3D_CONTAINER_SWAPCHAIN:
2858 return wined3d_swapchain_decref(surface->container.u.swapchain);
2860 default:
2861 ERR("Unhandled container type %#x.\n", surface->container.type);
2862 case WINED3D_CONTAINER_NONE:
2863 break;
2866 refcount = InterlockedDecrement(&surface->resource.ref);
2867 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2869 if (!refcount)
2871 surface_cleanup(surface);
2872 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2874 TRACE("Destroyed surface %p.\n", surface);
2875 HeapFree(GetProcessHeap(), 0, surface);
2878 return refcount;
2881 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2883 return resource_set_priority(&surface->resource, priority);
2886 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2888 return resource_get_priority(&surface->resource);
2891 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2893 TRACE("surface %p.\n", surface);
2895 if (!surface->resource.device->d3d_initialized)
2897 ERR("D3D not initialized.\n");
2898 return;
2901 surface_internal_preload(surface, SRGB_ANY);
2904 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2906 TRACE("surface %p.\n", surface);
2908 return surface->resource.parent;
2911 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2913 TRACE("surface %p.\n", surface);
2915 return &surface->resource;
2918 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2920 TRACE("surface %p, flags %#x.\n", surface, flags);
2922 switch (flags)
2924 case WINEDDGBS_CANBLT:
2925 case WINEDDGBS_ISBLTDONE:
2926 return WINED3D_OK;
2928 default:
2929 return WINED3DERR_INVALIDCALL;
2933 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2935 TRACE("surface %p, flags %#x.\n", surface, flags);
2937 /* XXX: DDERR_INVALIDSURFACETYPE */
2939 switch (flags)
2941 case WINEDDGFS_CANFLIP:
2942 case WINEDDGFS_ISFLIPDONE:
2943 return WINED3D_OK;
2945 default:
2946 return WINED3DERR_INVALIDCALL;
2950 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2952 TRACE("surface %p.\n", surface);
2954 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2955 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2958 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2960 TRACE("surface %p.\n", surface);
2962 surface->flags &= ~SFLAG_LOST;
2963 return WINED3D_OK;
2966 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2968 TRACE("surface %p, palette %p.\n", surface, palette);
2970 if (surface->palette == palette)
2972 TRACE("Nop palette change.\n");
2973 return WINED3D_OK;
2976 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2977 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2979 surface->palette = palette;
2981 if (palette)
2983 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2984 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
2986 surface->surface_ops->surface_realize_palette(surface);
2989 return WINED3D_OK;
2992 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
2993 DWORD flags, const WINEDDCOLORKEY *color_key)
2995 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
2997 if (flags & WINEDDCKEY_COLORSPACE)
2999 FIXME(" colorkey value not supported (%08x) !\n", flags);
3000 return WINED3DERR_INVALIDCALL;
3003 /* Dirtify the surface, but only if a key was changed. */
3004 if (color_key)
3006 switch (flags & ~WINEDDCKEY_COLORSPACE)
3008 case WINEDDCKEY_DESTBLT:
3009 surface->DestBltCKey = *color_key;
3010 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3011 break;
3013 case WINEDDCKEY_DESTOVERLAY:
3014 surface->DestOverlayCKey = *color_key;
3015 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3016 break;
3018 case WINEDDCKEY_SRCOVERLAY:
3019 surface->SrcOverlayCKey = *color_key;
3020 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3021 break;
3023 case WINEDDCKEY_SRCBLT:
3024 surface->SrcBltCKey = *color_key;
3025 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3026 break;
3029 else
3031 switch (flags & ~WINEDDCKEY_COLORSPACE)
3033 case WINEDDCKEY_DESTBLT:
3034 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3035 break;
3037 case WINEDDCKEY_DESTOVERLAY:
3038 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3039 break;
3041 case WINEDDCKEY_SRCOVERLAY:
3042 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3043 break;
3045 case WINEDDCKEY_SRCBLT:
3046 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3047 break;
3051 return WINED3D_OK;
3054 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3056 TRACE("surface %p.\n", surface);
3058 return surface->palette;
3061 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3063 const struct wined3d_format *format = surface->resource.format;
3064 DWORD pitch;
3066 TRACE("surface %p.\n", surface);
3068 if (format->flags & WINED3DFMT_FLAG_BLOCKS)
3070 /* Since compressed formats are block based, pitch means the amount of
3071 * bytes to the next row of block rather than the next row of pixels. */
3072 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3073 pitch = row_block_count * format->block_byte_count;
3075 else
3077 unsigned char alignment = surface->resource.device->surface_alignment;
3078 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3079 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3082 TRACE("Returning %u.\n", pitch);
3084 return pitch;
3087 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3089 TRACE("surface %p, mem %p.\n", surface, mem);
3091 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3093 WARN("Surface is locked or the DC is in use.\n");
3094 return WINED3DERR_INVALIDCALL;
3097 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
3098 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3100 ERR("Not supported on render targets.\n");
3101 return WINED3DERR_INVALIDCALL;
3104 if (mem && mem != surface->resource.allocatedMemory)
3106 void *release = NULL;
3108 /* Do I have to copy the old surface content? */
3109 if (surface->flags & SFLAG_DIBSECTION)
3111 DeleteDC(surface->hDC);
3112 DeleteObject(surface->dib.DIBsection);
3113 surface->dib.bitmap_data = NULL;
3114 surface->resource.allocatedMemory = NULL;
3115 surface->hDC = NULL;
3116 surface->flags &= ~SFLAG_DIBSECTION;
3118 else if (!(surface->flags & SFLAG_USERPTR))
3120 release = surface->resource.heapMemory;
3121 surface->resource.heapMemory = NULL;
3123 surface->resource.allocatedMemory = mem;
3124 surface->flags |= SFLAG_USERPTR;
3126 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
3127 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3129 /* For client textures OpenGL has to be notified. */
3130 if (surface->flags & SFLAG_CLIENT)
3131 surface_release_client_storage(surface);
3133 /* Now free the old memory if any. */
3134 HeapFree(GetProcessHeap(), 0, release);
3136 else if (surface->flags & SFLAG_USERPTR)
3138 /* HeapMemory should be NULL already. */
3139 if (surface->resource.heapMemory)
3140 ERR("User pointer surface has heap memory allocated.\n");
3142 if (!mem)
3144 surface->resource.allocatedMemory = NULL;
3145 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
3147 if (surface->flags & SFLAG_CLIENT)
3148 surface_release_client_storage(surface);
3150 surface_prepare_system_memory(surface);
3153 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3156 return WINED3D_OK;
3159 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3161 LONG w, h;
3163 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3165 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3167 WARN("Not an overlay surface.\n");
3168 return WINEDDERR_NOTAOVERLAYSURFACE;
3171 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3172 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3173 surface->overlay_destrect.left = x;
3174 surface->overlay_destrect.top = y;
3175 surface->overlay_destrect.right = x + w;
3176 surface->overlay_destrect.bottom = y + h;
3178 surface_draw_overlay(surface);
3180 return WINED3D_OK;
3183 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3185 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3187 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3189 TRACE("Not an overlay surface.\n");
3190 return WINEDDERR_NOTAOVERLAYSURFACE;
3193 if (!surface->overlay_dest)
3195 TRACE("Overlay not visible.\n");
3196 *x = 0;
3197 *y = 0;
3198 return WINEDDERR_OVERLAYNOTVISIBLE;
3201 *x = surface->overlay_destrect.left;
3202 *y = surface->overlay_destrect.top;
3204 TRACE("Returning position %d, %d.\n", *x, *y);
3206 return WINED3D_OK;
3209 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3210 DWORD flags, struct wined3d_surface *ref)
3212 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3214 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3216 TRACE("Not an overlay surface.\n");
3217 return WINEDDERR_NOTAOVERLAYSURFACE;
3220 return WINED3D_OK;
3223 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3224 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3226 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3227 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3229 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3231 WARN("Not an overlay surface.\n");
3232 return WINEDDERR_NOTAOVERLAYSURFACE;
3234 else if (!dst_surface)
3236 WARN("Dest surface is NULL.\n");
3237 return WINED3DERR_INVALIDCALL;
3240 if (src_rect)
3242 surface->overlay_srcrect = *src_rect;
3244 else
3246 surface->overlay_srcrect.left = 0;
3247 surface->overlay_srcrect.top = 0;
3248 surface->overlay_srcrect.right = surface->resource.width;
3249 surface->overlay_srcrect.bottom = surface->resource.height;
3252 if (dst_rect)
3254 surface->overlay_destrect = *dst_rect;
3256 else
3258 surface->overlay_destrect.left = 0;
3259 surface->overlay_destrect.top = 0;
3260 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3261 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3264 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3266 surface->overlay_dest = NULL;
3267 list_remove(&surface->overlay_entry);
3270 if (flags & WINEDDOVER_SHOW)
3272 if (surface->overlay_dest != dst_surface)
3274 surface->overlay_dest = dst_surface;
3275 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3278 else if (flags & WINEDDOVER_HIDE)
3280 /* tests show that the rectangles are erased on hide */
3281 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3282 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3283 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3284 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3285 surface->overlay_dest = NULL;
3288 surface_draw_overlay(surface);
3290 return WINED3D_OK;
3293 HRESULT CDECL wined3d_surface_set_clipper(struct wined3d_surface *surface, struct wined3d_clipper *clipper)
3295 TRACE("surface %p, clipper %p.\n", surface, clipper);
3297 surface->clipper = clipper;
3299 return WINED3D_OK;
3302 struct wined3d_clipper * CDECL wined3d_surface_get_clipper(const struct wined3d_surface *surface)
3304 TRACE("surface %p.\n", surface);
3306 return surface->clipper;
3309 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3311 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3313 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3315 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3317 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3318 return WINED3DERR_INVALIDCALL;
3321 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3322 surface->pow2Width, surface->pow2Height);
3323 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3324 surface->resource.format = format;
3326 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3327 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3328 format->glFormat, format->glInternal, format->glType);
3330 return WINED3D_OK;
3333 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3334 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3336 unsigned short *dst_s;
3337 const float *src_f;
3338 unsigned int x, y;
3340 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3342 for (y = 0; y < h; ++y)
3344 src_f = (const float *)(src + y * pitch_in);
3345 dst_s = (unsigned short *) (dst + y * pitch_out);
3346 for (x = 0; x < w; ++x)
3348 dst_s[x] = float_32_to_16(src_f + x);
3353 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3354 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3356 static const unsigned char convert_5to8[] =
3358 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3359 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3360 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3361 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3363 static const unsigned char convert_6to8[] =
3365 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3366 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3367 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3368 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3369 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3370 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3371 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3372 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3374 unsigned int x, y;
3376 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3378 for (y = 0; y < h; ++y)
3380 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3381 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3382 for (x = 0; x < w; ++x)
3384 WORD pixel = src_line[x];
3385 dst_line[x] = 0xff000000
3386 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3387 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3388 | convert_5to8[(pixel & 0x001f)];
3393 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3394 * in both cases we're just setting the X / Alpha channel to 0xff. */
3395 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3396 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3398 unsigned int x, y;
3400 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3402 for (y = 0; y < h; ++y)
3404 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3405 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3407 for (x = 0; x < w; ++x)
3409 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3414 static inline BYTE cliptobyte(int x)
3416 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3419 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3420 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3422 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3423 unsigned int x, y;
3425 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3427 for (y = 0; y < h; ++y)
3429 const BYTE *src_line = src + y * pitch_in;
3430 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3431 for (x = 0; x < w; ++x)
3433 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3434 * C = Y - 16; D = U - 128; E = V - 128;
3435 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3436 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3437 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3438 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3439 * U and V are shared between the pixels. */
3440 if (!(x & 1)) /* For every even pixel, read new U and V. */
3442 d = (int) src_line[1] - 128;
3443 e = (int) src_line[3] - 128;
3444 r2 = 409 * e + 128;
3445 g2 = - 100 * d - 208 * e + 128;
3446 b2 = 516 * d + 128;
3448 c2 = 298 * ((int) src_line[0] - 16);
3449 dst_line[x] = 0xff000000
3450 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3451 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3452 | cliptobyte((c2 + b2) >> 8); /* blue */
3453 /* Scale RGB values to 0..255 range,
3454 * then clip them if still not in range (may be negative),
3455 * then shift them within DWORD if necessary. */
3456 src_line += 2;
3461 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3462 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3464 unsigned int x, y;
3465 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3467 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3469 for (y = 0; y < h; ++y)
3471 const BYTE *src_line = src + y * pitch_in;
3472 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3473 for (x = 0; x < w; ++x)
3475 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3476 * C = Y - 16; D = U - 128; E = V - 128;
3477 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3478 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3479 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3480 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3481 * U and V are shared between the pixels. */
3482 if (!(x & 1)) /* For every even pixel, read new U and V. */
3484 d = (int) src_line[1] - 128;
3485 e = (int) src_line[3] - 128;
3486 r2 = 409 * e + 128;
3487 g2 = - 100 * d - 208 * e + 128;
3488 b2 = 516 * d + 128;
3490 c2 = 298 * ((int) src_line[0] - 16);
3491 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3492 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3493 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3494 /* Scale RGB values to 0..255 range,
3495 * then clip them if still not in range (may be negative),
3496 * then shift them within DWORD if necessary. */
3497 src_line += 2;
3502 struct d3dfmt_convertor_desc
3504 enum wined3d_format_id from, to;
3505 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3508 static const struct d3dfmt_convertor_desc convertors[] =
3510 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3511 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3512 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3513 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3514 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3515 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3518 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3519 enum wined3d_format_id to)
3521 unsigned int i;
3523 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3525 if (convertors[i].from == from && convertors[i].to == to)
3526 return &convertors[i];
3529 return NULL;
3532 /*****************************************************************************
3533 * surface_convert_format
3535 * Creates a duplicate of a surface in a different format. Is used by Blt to
3536 * blit between surfaces with different formats.
3538 * Parameters
3539 * source: Source surface
3540 * fmt: Requested destination format
3542 *****************************************************************************/
3543 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3545 const struct d3dfmt_convertor_desc *conv;
3546 WINED3DLOCKED_RECT lock_src, lock_dst;
3547 struct wined3d_surface *ret = NULL;
3548 HRESULT hr;
3550 conv = find_convertor(source->resource.format->id, to_fmt);
3551 if (!conv)
3553 FIXME("Cannot find a conversion function from format %s to %s.\n",
3554 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3555 return NULL;
3558 wined3d_surface_create(source->resource.device, source->resource.width,
3559 source->resource.height, to_fmt, 0 /* level */, 0 /* usage */, WINED3DPOOL_SCRATCH,
3560 WINED3DMULTISAMPLE_NONE /* TODO: Multisampled conversion */, 0 /* MultiSampleQuality */,
3561 source->surface_type, WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD,
3562 NULL /* parent */, &wined3d_null_parent_ops, &ret);
3563 if (!ret)
3565 ERR("Failed to create a destination surface for conversion.\n");
3566 return NULL;
3569 memset(&lock_src, 0, sizeof(lock_src));
3570 memset(&lock_dst, 0, sizeof(lock_dst));
3572 hr = wined3d_surface_map(source, &lock_src, NULL, WINED3DLOCK_READONLY);
3573 if (FAILED(hr))
3575 ERR("Failed to lock the source surface.\n");
3576 wined3d_surface_decref(ret);
3577 return NULL;
3579 hr = wined3d_surface_map(ret, &lock_dst, NULL, WINED3DLOCK_READONLY);
3580 if (FAILED(hr))
3582 ERR("Failed to lock the destination surface.\n");
3583 wined3d_surface_unmap(source);
3584 wined3d_surface_decref(ret);
3585 return NULL;
3588 conv->convert(lock_src.pBits, lock_dst.pBits, lock_src.Pitch, lock_dst.Pitch,
3589 source->resource.width, source->resource.height);
3591 wined3d_surface_unmap(ret);
3592 wined3d_surface_unmap(source);
3594 return ret;
3597 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3598 unsigned int bpp, UINT pitch, DWORD color)
3600 BYTE *first;
3601 int x, y;
3603 /* Do first row */
3605 #define COLORFILL_ROW(type) \
3606 do { \
3607 type *d = (type *)buf; \
3608 for (x = 0; x < width; ++x) \
3609 d[x] = (type)color; \
3610 } while(0)
3612 switch (bpp)
3614 case 1:
3615 COLORFILL_ROW(BYTE);
3616 break;
3618 case 2:
3619 COLORFILL_ROW(WORD);
3620 break;
3622 case 3:
3624 BYTE *d = buf;
3625 for (x = 0; x < width; ++x, d += 3)
3627 d[0] = (color ) & 0xFF;
3628 d[1] = (color >> 8) & 0xFF;
3629 d[2] = (color >> 16) & 0xFF;
3631 break;
3633 case 4:
3634 COLORFILL_ROW(DWORD);
3635 break;
3637 default:
3638 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3639 return WINED3DERR_NOTAVAILABLE;
3642 #undef COLORFILL_ROW
3644 /* Now copy first row. */
3645 first = buf;
3646 for (y = 1; y < height; ++y)
3648 buf += pitch;
3649 memcpy(buf, first, width * bpp);
3652 return WINED3D_OK;
3655 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3657 TRACE("surface %p.\n", surface);
3659 if (!(surface->flags & SFLAG_LOCKED))
3661 WARN("Trying to unmap unmapped surface.\n");
3662 return WINEDDERR_NOTLOCKED;
3664 surface->flags &= ~SFLAG_LOCKED;
3666 surface->surface_ops->surface_unmap(surface);
3668 return WINED3D_OK;
3671 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3672 WINED3DLOCKED_RECT *locked_rect, const RECT *rect, DWORD flags)
3674 const struct wined3d_format *format = surface->resource.format;
3676 TRACE("surface %p, locked_rect %p, rect %s, flags %#x.\n",
3677 surface, locked_rect, wine_dbgstr_rect(rect), flags);
3679 if (surface->flags & SFLAG_LOCKED)
3681 WARN("Surface is already mapped.\n");
3682 return WINED3DERR_INVALIDCALL;
3684 if ((format->flags & WINED3DFMT_FLAG_BLOCKS)
3685 && rect && (rect->left || rect->top
3686 || rect->right != surface->resource.width
3687 || rect->bottom != surface->resource.height))
3689 UINT width_mask = format->block_width - 1;
3690 UINT height_mask = format->block_height - 1;
3692 if ((rect->left & width_mask) || (rect->right & width_mask)
3693 || (rect->top & height_mask) || (rect->bottom & height_mask))
3695 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
3696 wine_dbgstr_rect(rect), format->block_width, format->block_height);
3698 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3699 return WINED3DERR_INVALIDCALL;
3703 surface->flags |= SFLAG_LOCKED;
3705 if (!(surface->flags & SFLAG_LOCKABLE))
3706 WARN("Trying to lock unlockable surface.\n");
3708 /* Performance optimization: Count how often a surface is mapped, if it is
3709 * mapped regularly do not throw away the system memory copy. This avoids
3710 * the need to download the surface from OpenGL all the time. The surface
3711 * is still downloaded if the OpenGL texture is changed. */
3712 if (!(surface->flags & SFLAG_DYNLOCK))
3714 if (++surface->lockCount > MAXLOCKCOUNT)
3716 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
3717 surface->flags |= SFLAG_DYNLOCK;
3721 surface->surface_ops->surface_map(surface, rect, flags);
3723 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
3724 locked_rect->Pitch = surface->resource.width * format->byte_count;
3725 else
3726 locked_rect->Pitch = wined3d_surface_get_pitch(surface);
3728 if (!rect)
3730 locked_rect->pBits = surface->resource.allocatedMemory;
3731 surface->lockedRect.left = 0;
3732 surface->lockedRect.top = 0;
3733 surface->lockedRect.right = surface->resource.width;
3734 surface->lockedRect.bottom = surface->resource.height;
3736 else
3738 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
3740 /* Compressed textures are block based, so calculate the offset of
3741 * the block that contains the top-left pixel of the locked rectangle. */
3742 locked_rect->pBits = surface->resource.allocatedMemory
3743 + ((rect->top / format->block_height) * locked_rect->Pitch)
3744 + ((rect->left / format->block_width) * format->block_byte_count);
3746 else
3748 locked_rect->pBits = surface->resource.allocatedMemory
3749 + (locked_rect->Pitch * rect->top)
3750 + (rect->left * format->byte_count);
3752 surface->lockedRect.left = rect->left;
3753 surface->lockedRect.top = rect->top;
3754 surface->lockedRect.right = rect->right;
3755 surface->lockedRect.bottom = rect->bottom;
3758 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3759 TRACE("Returning memory %p, pitch %u.\n", locked_rect->pBits, locked_rect->Pitch);
3761 return WINED3D_OK;
3764 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3766 WINED3DLOCKED_RECT lock;
3767 HRESULT hr;
3769 TRACE("surface %p, dc %p.\n", surface, dc);
3771 if (surface->flags & SFLAG_USERPTR)
3773 ERR("Not supported on surfaces with application-provided memory.\n");
3774 return WINEDDERR_NODC;
3777 /* Give more detailed info for ddraw. */
3778 if (surface->flags & SFLAG_DCINUSE)
3779 return WINEDDERR_DCALREADYCREATED;
3781 /* Can't GetDC if the surface is locked. */
3782 if (surface->flags & SFLAG_LOCKED)
3783 return WINED3DERR_INVALIDCALL;
3785 /* Create a DIB section if there isn't a dc yet. */
3786 if (!surface->hDC)
3788 if (surface->flags & SFLAG_CLIENT)
3790 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3791 surface_release_client_storage(surface);
3793 hr = surface_create_dib_section(surface);
3794 if (FAILED(hr))
3795 return WINED3DERR_INVALIDCALL;
3797 /* Use the DIB section from now on if we are not using a PBO. */
3798 if (!(surface->flags & SFLAG_PBO))
3799 surface->resource.allocatedMemory = surface->dib.bitmap_data;
3802 /* Map the surface. */
3803 hr = wined3d_surface_map(surface, &lock, NULL, 0);
3804 if (FAILED(hr))
3806 ERR("Map failed, hr %#x.\n", hr);
3807 return hr;
3810 /* Sync the DIB with the PBO. This can't be done earlier because Map()
3811 * activates the allocatedMemory. */
3812 if (surface->flags & SFLAG_PBO)
3813 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->resource.size);
3815 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3816 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3818 /* GetDC on palettized formats is unsupported in D3D9, and the method
3819 * is missing in D3D8, so this should only be used for DX <=7
3820 * surfaces (with non-device palettes). */
3821 const PALETTEENTRY *pal = NULL;
3823 if (surface->palette)
3825 pal = surface->palette->palents;
3827 else
3829 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3830 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3832 if (dds_primary && dds_primary->palette)
3833 pal = dds_primary->palette->palents;
3836 if (pal)
3838 RGBQUAD col[256];
3839 unsigned int i;
3841 for (i = 0; i < 256; ++i)
3843 col[i].rgbRed = pal[i].peRed;
3844 col[i].rgbGreen = pal[i].peGreen;
3845 col[i].rgbBlue = pal[i].peBlue;
3846 col[i].rgbReserved = 0;
3848 SetDIBColorTable(surface->hDC, 0, 256, col);
3852 surface->flags |= SFLAG_DCINUSE;
3854 *dc = surface->hDC;
3855 TRACE("Returning dc %p.\n", *dc);
3857 return WINED3D_OK;
3860 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3862 TRACE("surface %p, dc %p.\n", surface, dc);
3864 if (!(surface->flags & SFLAG_DCINUSE))
3865 return WINEDDERR_NODC;
3867 if (surface->hDC != dc)
3869 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3870 dc, surface->hDC);
3871 return WINEDDERR_NODC;
3874 /* Copy the contents of the DIB over to the PBO. */
3875 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3876 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->resource.size);
3878 /* We locked first, so unlock now. */
3879 wined3d_surface_unmap(surface);
3881 surface->flags &= ~SFLAG_DCINUSE;
3883 return WINED3D_OK;
3886 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3888 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3890 if (flags)
3892 static UINT once;
3893 if (!once++)
3894 FIXME("Ignoring flags %#x.\n", flags);
3895 else
3896 WARN("Ignoring flags %#x.\n", flags);
3899 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
3901 ERR("Not supported on swapchain surfaces.\n");
3902 return WINEDDERR_NOTFLIPPABLE;
3905 /* Flipping is only supported on render targets and overlays. */
3906 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
3908 WARN("Tried to flip a non-render target, non-overlay surface.\n");
3909 return WINEDDERR_NOTFLIPPABLE;
3912 flip_surface(surface, override);
3914 /* Update overlays if they're visible. */
3915 if ((surface->resource.usage & WINED3DUSAGE_OVERLAY) && surface->overlay_dest)
3916 return surface_draw_overlay(surface);
3918 return WINED3D_OK;
3921 /* Do not call while under the GL lock. */
3922 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3924 struct wined3d_device *device = surface->resource.device;
3926 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3928 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3930 struct wined3d_texture *texture = surface->container.u.texture;
3932 TRACE("Passing to container (%p).\n", texture);
3933 texture->texture_ops->texture_preload(texture, srgb);
3935 else
3937 struct wined3d_context *context;
3939 TRACE("(%p) : About to load surface\n", surface);
3941 /* TODO: Use already acquired context when possible. */
3942 context = context_acquire(device, NULL);
3944 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3946 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3948 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3949 GLclampf tmp;
3950 tmp = 0.9f;
3951 ENTER_GL();
3952 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3953 LEAVE_GL();
3956 context_release(context);
3960 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3962 if (!surface->resource.allocatedMemory)
3964 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3965 surface->resource.size + RESOURCE_ALIGNMENT);
3966 if (!surface->resource.heapMemory)
3968 ERR("Out of memory\n");
3969 return FALSE;
3971 surface->resource.allocatedMemory =
3972 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
3974 else
3976 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
3979 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3981 return TRUE;
3984 /* Read the framebuffer back into the surface */
3985 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
3987 struct wined3d_device *device = surface->resource.device;
3988 const struct wined3d_gl_info *gl_info;
3989 struct wined3d_context *context;
3990 BYTE *mem;
3991 GLint fmt;
3992 GLint type;
3993 BYTE *row, *top, *bottom;
3994 int i;
3995 BOOL bpp;
3996 RECT local_rect;
3997 BOOL srcIsUpsideDown;
3998 GLint rowLen = 0;
3999 GLint skipPix = 0;
4000 GLint skipRow = 0;
4002 context = context_acquire(device, surface);
4003 context_apply_blit_state(context, device);
4004 gl_info = context->gl_info;
4006 ENTER_GL();
4008 /* Select the correct read buffer, and give some debug output.
4009 * There is no need to keep track of the current read buffer or reset it, every part of the code
4010 * that reads sets the read buffer as desired.
4012 if (surface_is_offscreen(surface))
4014 /* Mapping the primary render target which is not on a swapchain.
4015 * Read from the back buffer. */
4016 TRACE("Mapping offscreen render target.\n");
4017 glReadBuffer(device->offscreenBuffer);
4018 srcIsUpsideDown = TRUE;
4020 else
4022 /* Onscreen surfaces are always part of a swapchain */
4023 GLenum buffer = surface_get_gl_buffer(surface);
4024 TRACE("Mapping %#x buffer.\n", buffer);
4025 glReadBuffer(buffer);
4026 checkGLcall("glReadBuffer");
4027 srcIsUpsideDown = FALSE;
4030 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
4031 if (!rect)
4033 local_rect.left = 0;
4034 local_rect.top = 0;
4035 local_rect.right = surface->resource.width;
4036 local_rect.bottom = surface->resource.height;
4038 else
4040 local_rect = *rect;
4042 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
4044 switch (surface->resource.format->id)
4046 case WINED3DFMT_P8_UINT:
4048 if (primary_render_target_is_p8(device))
4050 /* In case of P8 render targets the index is stored in the alpha component */
4051 fmt = GL_ALPHA;
4052 type = GL_UNSIGNED_BYTE;
4053 mem = dest;
4054 bpp = surface->resource.format->byte_count;
4056 else
4058 /* GL can't return palettized data, so read ARGB pixels into a
4059 * separate block of memory and convert them into palettized format
4060 * in software. Slow, but if the app means to use palettized render
4061 * targets and locks it...
4063 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4064 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4065 * for the color channels when palettizing the colors.
4067 fmt = GL_RGB;
4068 type = GL_UNSIGNED_BYTE;
4069 pitch *= 3;
4070 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4071 if (!mem)
4073 ERR("Out of memory\n");
4074 LEAVE_GL();
4075 return;
4077 bpp = surface->resource.format->byte_count * 3;
4080 break;
4082 default:
4083 mem = dest;
4084 fmt = surface->resource.format->glFormat;
4085 type = surface->resource.format->glType;
4086 bpp = surface->resource.format->byte_count;
4089 if (surface->flags & SFLAG_PBO)
4091 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4092 checkGLcall("glBindBufferARB");
4093 if (mem)
4095 ERR("mem not null for pbo -- unexpected\n");
4096 mem = NULL;
4100 /* Save old pixel store pack state */
4101 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4102 checkGLcall("glGetIntegerv");
4103 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4104 checkGLcall("glGetIntegerv");
4105 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4106 checkGLcall("glGetIntegerv");
4108 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4109 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4110 checkGLcall("glPixelStorei");
4111 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4112 checkGLcall("glPixelStorei");
4113 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4114 checkGLcall("glPixelStorei");
4116 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4117 local_rect.right - local_rect.left,
4118 local_rect.bottom - local_rect.top,
4119 fmt, type, mem);
4120 checkGLcall("glReadPixels");
4122 /* Reset previous pixel store pack state */
4123 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4124 checkGLcall("glPixelStorei");
4125 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4126 checkGLcall("glPixelStorei");
4127 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4128 checkGLcall("glPixelStorei");
4130 if (surface->flags & SFLAG_PBO)
4132 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4133 checkGLcall("glBindBufferARB");
4135 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4136 * to get a pointer to it and perform the flipping in software. This is a lot
4137 * faster than calling glReadPixels for each line. In case we want more speed
4138 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4139 if (!srcIsUpsideDown)
4141 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4142 checkGLcall("glBindBufferARB");
4144 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4145 checkGLcall("glMapBufferARB");
4149 /* TODO: Merge this with the palettization loop below for P8 targets */
4150 if(!srcIsUpsideDown) {
4151 UINT len, off;
4152 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4153 Flip the lines in software */
4154 len = (local_rect.right - local_rect.left) * bpp;
4155 off = local_rect.left * bpp;
4157 row = HeapAlloc(GetProcessHeap(), 0, len);
4158 if(!row) {
4159 ERR("Out of memory\n");
4160 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4161 HeapFree(GetProcessHeap(), 0, mem);
4162 LEAVE_GL();
4163 return;
4166 top = mem + pitch * local_rect.top;
4167 bottom = mem + pitch * (local_rect.bottom - 1);
4168 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4169 memcpy(row, top + off, len);
4170 memcpy(top + off, bottom + off, len);
4171 memcpy(bottom + off, row, len);
4172 top += pitch;
4173 bottom -= pitch;
4175 HeapFree(GetProcessHeap(), 0, row);
4177 /* Unmap the temp PBO buffer */
4178 if (surface->flags & SFLAG_PBO)
4180 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4181 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4185 LEAVE_GL();
4186 context_release(context);
4188 /* For P8 textures we need to perform an inverse palette lookup. This is
4189 * done by searching for a palette index which matches the RGB value.
4190 * Note this isn't guaranteed to work when there are multiple entries for
4191 * the same color but we have no choice. In case of P8 render targets,
4192 * the index is stored in the alpha component so no conversion is needed. */
4193 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4195 const PALETTEENTRY *pal = NULL;
4196 DWORD width = pitch / 3;
4197 int x, y, c;
4199 if (surface->palette)
4201 pal = surface->palette->palents;
4203 else
4205 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4206 HeapFree(GetProcessHeap(), 0, mem);
4207 return;
4210 for(y = local_rect.top; y < local_rect.bottom; y++) {
4211 for(x = local_rect.left; x < local_rect.right; x++) {
4212 /* start lines pixels */
4213 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4214 const BYTE *green = blue + 1;
4215 const BYTE *red = green + 1;
4217 for(c = 0; c < 256; c++) {
4218 if(*red == pal[c].peRed &&
4219 *green == pal[c].peGreen &&
4220 *blue == pal[c].peBlue)
4222 *((BYTE *) dest + y * width + x) = c;
4223 break;
4228 HeapFree(GetProcessHeap(), 0, mem);
4232 /* Read the framebuffer contents into a texture. Note that this function
4233 * doesn't do any kind of flipping. Using this on an onscreen surface will
4234 * result in a flipped D3D texture. */
4235 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
4237 struct wined3d_device *device = surface->resource.device;
4238 struct wined3d_context *context;
4240 context = context_acquire(device, surface);
4241 device_invalidate_state(device, STATE_FRAMEBUFFER);
4243 surface_prepare_texture(surface, context, srgb);
4244 surface_bind_and_dirtify(surface, context, srgb);
4246 TRACE("Reading back offscreen render target %p.\n", surface);
4248 ENTER_GL();
4250 if (surface_is_offscreen(surface))
4251 glReadBuffer(device->offscreenBuffer);
4252 else
4253 glReadBuffer(surface_get_gl_buffer(surface));
4254 checkGLcall("glReadBuffer");
4256 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4257 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4258 checkGLcall("glCopyTexSubImage2D");
4260 LEAVE_GL();
4262 context_release(context);
4265 /* Context activation is done by the caller. */
4266 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4267 struct wined3d_context *context, BOOL srgb)
4269 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4270 CONVERT_TYPES convert;
4271 struct wined3d_format format;
4273 if (surface->flags & alloc_flag) return;
4275 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4276 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4277 else surface->flags &= ~SFLAG_CONVERTED;
4279 surface_bind_and_dirtify(surface, context, srgb);
4280 surface_allocate_surface(surface, context->gl_info, &format, srgb);
4281 surface->flags |= alloc_flag;
4284 /* Context activation is done by the caller. */
4285 void surface_prepare_texture(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
4287 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4289 struct wined3d_texture *texture = surface->container.u.texture;
4290 UINT sub_count = texture->level_count * texture->layer_count;
4291 UINT i;
4293 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4295 for (i = 0; i < sub_count; ++i)
4297 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4298 surface_prepare_texture_internal(s, context, srgb);
4301 return;
4304 surface_prepare_texture_internal(surface, context, srgb);
4307 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
4309 if (multisample)
4311 if (surface->rb_multisample)
4312 return;
4314 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
4315 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
4316 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
4317 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
4318 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
4320 else
4322 if (surface->rb_resolved)
4323 return;
4325 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
4326 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
4327 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
4328 surface->pow2Width, surface->pow2Height);
4329 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
4333 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4334 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4336 struct wined3d_device *device = surface->resource.device;
4337 UINT pitch = wined3d_surface_get_pitch(surface);
4338 const struct wined3d_gl_info *gl_info;
4339 struct wined3d_context *context;
4340 RECT local_rect;
4341 UINT w, h;
4343 surface_get_rect(surface, rect, &local_rect);
4345 mem += local_rect.top * pitch + local_rect.left * bpp;
4346 w = local_rect.right - local_rect.left;
4347 h = local_rect.bottom - local_rect.top;
4349 /* Activate the correct context for the render target */
4350 context = context_acquire(device, surface);
4351 context_apply_blit_state(context, device);
4352 gl_info = context->gl_info;
4354 ENTER_GL();
4356 if (!surface_is_offscreen(surface))
4358 GLenum buffer = surface_get_gl_buffer(surface);
4359 TRACE("Unlocking %#x buffer.\n", buffer);
4360 context_set_draw_buffer(context, buffer);
4362 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4363 glPixelZoom(1.0f, -1.0f);
4365 else
4367 /* Primary offscreen render target */
4368 TRACE("Offscreen render target.\n");
4369 context_set_draw_buffer(context, device->offscreenBuffer);
4371 glPixelZoom(1.0f, 1.0f);
4374 glRasterPos3i(local_rect.left, local_rect.top, 1);
4375 checkGLcall("glRasterPos3i");
4377 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4378 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4380 if (surface->flags & SFLAG_PBO)
4382 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4383 checkGLcall("glBindBufferARB");
4386 glDrawPixels(w, h, fmt, type, mem);
4387 checkGLcall("glDrawPixels");
4389 if (surface->flags & SFLAG_PBO)
4391 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4392 checkGLcall("glBindBufferARB");
4395 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4396 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4398 LEAVE_GL();
4400 if (wined3d_settings.strict_draw_ordering
4401 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4402 && surface->container.u.swapchain->front_buffer == surface))
4403 wglFlush();
4405 context_release(context);
4408 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4409 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4411 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4412 const struct wined3d_device *device = surface->resource.device;
4413 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4414 BOOL blit_supported = FALSE;
4416 /* Copy the default values from the surface. Below we might perform fixups */
4417 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4418 *format = *surface->resource.format;
4419 *convert = NO_CONVERSION;
4421 /* Ok, now look if we have to do any conversion */
4422 switch (surface->resource.format->id)
4424 case WINED3DFMT_P8_UINT:
4425 /* Below the call to blit_supported is disabled for Wine 1.2
4426 * because the function isn't operating correctly yet. At the
4427 * moment 8-bit blits are handled in software and if certain GL
4428 * extensions are around, surface conversion is performed at
4429 * upload time. The blit_supported call recognizes it as a
4430 * destination fixup. This type of upload 'fixup' and 8-bit to
4431 * 8-bit blits need to be handled by the blit_shader.
4432 * TODO: get rid of this #if 0. */
4433 #if 0
4434 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4435 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4436 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4437 #endif
4438 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4440 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4441 * texturing. Further also use conversion in case of color keying.
4442 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4443 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4444 * conflicts with this.
4446 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4447 || colorkey_active || !use_texturing)
4449 format->glFormat = GL_RGBA;
4450 format->glInternal = GL_RGBA;
4451 format->glType = GL_UNSIGNED_BYTE;
4452 format->conv_byte_count = 4;
4453 if (colorkey_active)
4454 *convert = CONVERT_PALETTED_CK;
4455 else
4456 *convert = CONVERT_PALETTED;
4458 break;
4460 case WINED3DFMT_B2G3R3_UNORM:
4461 /* **********************
4462 GL_UNSIGNED_BYTE_3_3_2
4463 ********************** */
4464 if (colorkey_active) {
4465 /* This texture format will never be used.. So do not care about color keying
4466 up until the point in time it will be needed :-) */
4467 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4469 break;
4471 case WINED3DFMT_B5G6R5_UNORM:
4472 if (colorkey_active)
4474 *convert = CONVERT_CK_565;
4475 format->glFormat = GL_RGBA;
4476 format->glInternal = GL_RGB5_A1;
4477 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4478 format->conv_byte_count = 2;
4480 break;
4482 case WINED3DFMT_B5G5R5X1_UNORM:
4483 if (colorkey_active)
4485 *convert = CONVERT_CK_5551;
4486 format->glFormat = GL_BGRA;
4487 format->glInternal = GL_RGB5_A1;
4488 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4489 format->conv_byte_count = 2;
4491 break;
4493 case WINED3DFMT_B8G8R8_UNORM:
4494 if (colorkey_active)
4496 *convert = CONVERT_CK_RGB24;
4497 format->glFormat = GL_RGBA;
4498 format->glInternal = GL_RGBA8;
4499 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4500 format->conv_byte_count = 4;
4502 break;
4504 case WINED3DFMT_B8G8R8X8_UNORM:
4505 if (colorkey_active)
4507 *convert = CONVERT_RGB32_888;
4508 format->glFormat = GL_RGBA;
4509 format->glInternal = GL_RGBA8;
4510 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4511 format->conv_byte_count = 4;
4513 break;
4515 default:
4516 break;
4519 return WINED3D_OK;
4522 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4524 const struct wined3d_device *device = surface->resource.device;
4525 const struct wined3d_palette *pal = surface->palette;
4526 BOOL index_in_alpha = FALSE;
4527 unsigned int i;
4529 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4530 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4531 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4532 * duplicate entries. Store the color key in the unused alpha component to speed the
4533 * download up and to make conversion unneeded. */
4534 index_in_alpha = primary_render_target_is_p8(device);
4536 if (!pal)
4538 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4539 if (index_in_alpha)
4541 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4542 * there's no palette at this time. */
4543 for (i = 0; i < 256; i++) table[i][3] = i;
4546 else
4548 TRACE("Using surface palette %p\n", pal);
4549 /* Get the surface's palette */
4550 for (i = 0; i < 256; ++i)
4552 table[i][0] = pal->palents[i].peRed;
4553 table[i][1] = pal->palents[i].peGreen;
4554 table[i][2] = pal->palents[i].peBlue;
4556 /* When index_in_alpha is set the palette index is stored in the
4557 * alpha component. In case of a readback we can then read
4558 * GL_ALPHA. Color keying is handled in BltOverride using a
4559 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4560 * color key itself is passed to glAlphaFunc in other cases the
4561 * alpha component of pixels that should be masked away is set to 0. */
4562 if (index_in_alpha)
4564 table[i][3] = i;
4566 else if (colorkey && (i >= surface->SrcBltCKey.dwColorSpaceLowValue)
4567 && (i <= surface->SrcBltCKey.dwColorSpaceHighValue))
4569 table[i][3] = 0x00;
4571 else if (pal->flags & WINEDDPCAPS_ALPHA)
4573 table[i][3] = pal->palents[i].peFlags;
4575 else
4577 table[i][3] = 0xFF;
4583 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4584 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4586 const BYTE *source;
4587 BYTE *dest;
4588 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4590 switch (convert) {
4591 case NO_CONVERSION:
4593 memcpy(dst, src, pitch * height);
4594 break;
4596 case CONVERT_PALETTED:
4597 case CONVERT_PALETTED_CK:
4599 BYTE table[256][4];
4600 unsigned int x, y;
4602 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4604 for (y = 0; y < height; y++)
4606 source = src + pitch * y;
4607 dest = dst + outpitch * y;
4608 /* This is an 1 bpp format, using the width here is fine */
4609 for (x = 0; x < width; x++) {
4610 BYTE color = *source++;
4611 *dest++ = table[color][0];
4612 *dest++ = table[color][1];
4613 *dest++ = table[color][2];
4614 *dest++ = table[color][3];
4618 break;
4620 case CONVERT_CK_565:
4622 /* Converting the 565 format in 5551 packed to emulate color-keying.
4624 Note : in all these conversion, it would be best to average the averaging
4625 pixels to get the color of the pixel that will be color-keyed to
4626 prevent 'color bleeding'. This will be done later on if ever it is
4627 too visible.
4629 Note2: Nvidia documents say that their driver does not support alpha + color keying
4630 on the same surface and disables color keying in such a case
4632 unsigned int x, y;
4633 const WORD *Source;
4634 WORD *Dest;
4636 TRACE("Color keyed 565\n");
4638 for (y = 0; y < height; y++) {
4639 Source = (const WORD *)(src + y * pitch);
4640 Dest = (WORD *) (dst + y * outpitch);
4641 for (x = 0; x < width; x++ ) {
4642 WORD color = *Source++;
4643 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4644 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4645 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4646 *Dest |= 0x0001;
4647 Dest++;
4651 break;
4653 case CONVERT_CK_5551:
4655 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4656 unsigned int x, y;
4657 const WORD *Source;
4658 WORD *Dest;
4659 TRACE("Color keyed 5551\n");
4660 for (y = 0; y < height; y++) {
4661 Source = (const WORD *)(src + y * pitch);
4662 Dest = (WORD *) (dst + y * outpitch);
4663 for (x = 0; x < width; x++ ) {
4664 WORD color = *Source++;
4665 *Dest = color;
4666 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4667 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
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 < surface->SrcBltCKey.dwColorSpaceLowValue)
4689 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4690 dstcolor |= 0xff;
4691 *(DWORD*)dest = dstcolor;
4692 source += 3;
4693 dest += 4;
4697 break;
4699 case CONVERT_RGB32_888:
4701 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4702 unsigned int x, y;
4703 for (y = 0; y < height; y++)
4705 source = src + pitch * y;
4706 dest = dst + outpitch * y;
4707 for (x = 0; x < width; x++) {
4708 DWORD color = 0xffffff & *(const DWORD*)source;
4709 DWORD dstcolor = color << 8;
4710 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4711 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4712 dstcolor |= 0xff;
4713 *(DWORD*)dest = dstcolor;
4714 source += 4;
4715 dest += 4;
4719 break;
4721 default:
4722 ERR("Unsupported conversion type %#x.\n", convert);
4724 return WINED3D_OK;
4727 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4729 /* Flip the surface contents */
4730 /* Flip the DC */
4732 HDC tmp;
4733 tmp = front->hDC;
4734 front->hDC = back->hDC;
4735 back->hDC = tmp;
4738 /* Flip the DIBsection */
4740 HBITMAP tmp;
4741 BOOL hasDib = front->flags & SFLAG_DIBSECTION;
4742 tmp = front->dib.DIBsection;
4743 front->dib.DIBsection = back->dib.DIBsection;
4744 back->dib.DIBsection = tmp;
4746 if (back->flags & SFLAG_DIBSECTION) front->flags |= SFLAG_DIBSECTION;
4747 else front->flags &= ~SFLAG_DIBSECTION;
4748 if (hasDib) back->flags |= SFLAG_DIBSECTION;
4749 else back->flags &= ~SFLAG_DIBSECTION;
4752 /* Flip the surface data */
4754 void* tmp;
4756 tmp = front->dib.bitmap_data;
4757 front->dib.bitmap_data = back->dib.bitmap_data;
4758 back->dib.bitmap_data = tmp;
4760 tmp = front->resource.allocatedMemory;
4761 front->resource.allocatedMemory = back->resource.allocatedMemory;
4762 back->resource.allocatedMemory = tmp;
4764 tmp = front->resource.heapMemory;
4765 front->resource.heapMemory = back->resource.heapMemory;
4766 back->resource.heapMemory = tmp;
4769 /* Flip the PBO */
4771 GLuint tmp_pbo = front->pbo;
4772 front->pbo = back->pbo;
4773 back->pbo = tmp_pbo;
4776 /* Flip the opengl texture */
4778 GLuint tmp;
4780 tmp = back->texture_name;
4781 back->texture_name = front->texture_name;
4782 front->texture_name = tmp;
4784 tmp = back->texture_name_srgb;
4785 back->texture_name_srgb = front->texture_name_srgb;
4786 front->texture_name_srgb = tmp;
4788 tmp = back->rb_multisample;
4789 back->rb_multisample = front->rb_multisample;
4790 front->rb_multisample = tmp;
4792 tmp = back->rb_resolved;
4793 back->rb_resolved = front->rb_resolved;
4794 front->rb_resolved = tmp;
4796 resource_unload(&back->resource);
4797 resource_unload(&front->resource);
4801 DWORD tmp_flags = back->flags;
4802 back->flags = front->flags;
4803 front->flags = tmp_flags;
4807 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4808 * pixel copy calls. */
4809 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4810 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4812 struct wined3d_device *device = dst_surface->resource.device;
4813 float xrel, yrel;
4814 UINT row;
4815 struct wined3d_context *context;
4816 BOOL upsidedown = FALSE;
4817 RECT dst_rect = *dst_rect_in;
4819 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4820 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4822 if(dst_rect.top > dst_rect.bottom) {
4823 UINT tmp = dst_rect.bottom;
4824 dst_rect.bottom = dst_rect.top;
4825 dst_rect.top = tmp;
4826 upsidedown = TRUE;
4829 context = context_acquire(device, src_surface);
4830 context_apply_blit_state(context, device);
4831 surface_internal_preload(dst_surface, SRGB_RGB);
4832 ENTER_GL();
4834 /* Bind the target texture */
4835 context_bind_texture(context, dst_surface->texture_target, dst_surface->texture_name);
4836 if (surface_is_offscreen(src_surface))
4838 TRACE("Reading from an offscreen target\n");
4839 upsidedown = !upsidedown;
4840 glReadBuffer(device->offscreenBuffer);
4842 else
4844 glReadBuffer(surface_get_gl_buffer(src_surface));
4846 checkGLcall("glReadBuffer");
4848 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4849 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4851 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4853 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4855 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4856 ERR("Texture filtering not supported in direct blit\n");
4859 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4860 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4862 ERR("Texture filtering not supported in direct blit\n");
4865 if (upsidedown
4866 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4867 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4869 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4871 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4872 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4873 src_rect->left, src_surface->resource.height - src_rect->bottom,
4874 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4876 else
4878 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4879 /* I have to process this row by row to swap the image,
4880 * otherwise it would be upside down, so stretching in y direction
4881 * doesn't cost extra time
4883 * However, stretching in x direction can be avoided if not necessary
4885 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4886 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4888 /* Well, that stuff works, but it's very slow.
4889 * find a better way instead
4891 UINT col;
4893 for (col = dst_rect.left; col < dst_rect.right; ++col)
4895 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4896 dst_rect.left + col /* x offset */, row /* y offset */,
4897 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4900 else
4902 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4903 dst_rect.left /* x offset */, row /* y offset */,
4904 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4908 checkGLcall("glCopyTexSubImage2D");
4910 LEAVE_GL();
4911 context_release(context);
4913 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4914 * path is never entered
4916 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4919 /* Uses the hardware to stretch and flip the image */
4920 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4921 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4923 struct wined3d_device *device = dst_surface->resource.device;
4924 struct wined3d_swapchain *src_swapchain = NULL;
4925 GLuint src, backup = 0;
4926 float left, right, top, bottom; /* Texture coordinates */
4927 UINT fbwidth = src_surface->resource.width;
4928 UINT fbheight = src_surface->resource.height;
4929 struct wined3d_context *context;
4930 GLenum drawBuffer = GL_BACK;
4931 GLenum texture_target;
4932 BOOL noBackBufferBackup;
4933 BOOL src_offscreen;
4934 BOOL upsidedown = FALSE;
4935 RECT dst_rect = *dst_rect_in;
4937 TRACE("Using hwstretch blit\n");
4938 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4939 context = context_acquire(device, src_surface);
4940 context_apply_blit_state(context, device);
4941 surface_internal_preload(dst_surface, SRGB_RGB);
4943 src_offscreen = surface_is_offscreen(src_surface);
4944 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4945 if (!noBackBufferBackup && !src_surface->texture_name)
4947 /* Get it a description */
4948 surface_internal_preload(src_surface, SRGB_RGB);
4950 ENTER_GL();
4952 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4953 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4955 if (context->aux_buffers >= 2)
4957 /* Got more than one aux buffer? Use the 2nd aux buffer */
4958 drawBuffer = GL_AUX1;
4960 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4962 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4963 drawBuffer = GL_AUX0;
4966 if(noBackBufferBackup) {
4967 glGenTextures(1, &backup);
4968 checkGLcall("glGenTextures");
4969 context_bind_texture(context, GL_TEXTURE_2D, backup);
4970 texture_target = GL_TEXTURE_2D;
4971 } else {
4972 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4973 * we are reading from the back buffer, the backup can be used as source texture
4975 texture_target = src_surface->texture_target;
4976 context_bind_texture(context, texture_target, src_surface->texture_name);
4977 glEnable(texture_target);
4978 checkGLcall("glEnable(texture_target)");
4980 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4981 src_surface->flags &= ~SFLAG_INTEXTURE;
4984 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4985 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4987 if(dst_rect.top > dst_rect.bottom) {
4988 UINT tmp = dst_rect.bottom;
4989 dst_rect.bottom = dst_rect.top;
4990 dst_rect.top = tmp;
4991 upsidedown = TRUE;
4994 if (src_offscreen)
4996 TRACE("Reading from an offscreen target\n");
4997 upsidedown = !upsidedown;
4998 glReadBuffer(device->offscreenBuffer);
5000 else
5002 glReadBuffer(surface_get_gl_buffer(src_surface));
5005 /* TODO: Only back up the part that will be overwritten */
5006 glCopyTexSubImage2D(texture_target, 0,
5007 0, 0 /* read offsets */,
5008 0, 0,
5009 fbwidth,
5010 fbheight);
5012 checkGLcall("glCopyTexSubImage2D");
5014 /* No issue with overriding these - the sampler is dirty due to blit usage */
5015 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
5016 wined3d_gl_mag_filter(magLookup, Filter));
5017 checkGLcall("glTexParameteri");
5018 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5019 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
5020 checkGLcall("glTexParameteri");
5022 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5023 src_swapchain = src_surface->container.u.swapchain;
5024 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5026 src = backup ? backup : src_surface->texture_name;
5028 else
5030 glReadBuffer(GL_FRONT);
5031 checkGLcall("glReadBuffer(GL_FRONT)");
5033 glGenTextures(1, &src);
5034 checkGLcall("glGenTextures(1, &src)");
5035 context_bind_texture(context, GL_TEXTURE_2D, src);
5037 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5038 * out for power of 2 sizes
5040 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5041 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5042 checkGLcall("glTexImage2D");
5043 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5044 0, 0 /* read offsets */,
5045 0, 0,
5046 fbwidth,
5047 fbheight);
5049 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5050 checkGLcall("glTexParameteri");
5051 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5052 checkGLcall("glTexParameteri");
5054 glReadBuffer(GL_BACK);
5055 checkGLcall("glReadBuffer(GL_BACK)");
5057 if(texture_target != GL_TEXTURE_2D) {
5058 glDisable(texture_target);
5059 glEnable(GL_TEXTURE_2D);
5060 texture_target = GL_TEXTURE_2D;
5063 checkGLcall("glEnd and previous");
5065 left = src_rect->left;
5066 right = src_rect->right;
5068 if (!upsidedown)
5070 top = src_surface->resource.height - src_rect->top;
5071 bottom = src_surface->resource.height - src_rect->bottom;
5073 else
5075 top = src_surface->resource.height - src_rect->bottom;
5076 bottom = src_surface->resource.height - src_rect->top;
5079 if (src_surface->flags & SFLAG_NORMCOORD)
5081 left /= src_surface->pow2Width;
5082 right /= src_surface->pow2Width;
5083 top /= src_surface->pow2Height;
5084 bottom /= src_surface->pow2Height;
5087 /* draw the source texture stretched and upside down. The correct surface is bound already */
5088 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5089 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5091 context_set_draw_buffer(context, drawBuffer);
5092 glReadBuffer(drawBuffer);
5094 glBegin(GL_QUADS);
5095 /* bottom left */
5096 glTexCoord2f(left, bottom);
5097 glVertex2i(0, 0);
5099 /* top left */
5100 glTexCoord2f(left, top);
5101 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5103 /* top right */
5104 glTexCoord2f(right, top);
5105 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5107 /* bottom right */
5108 glTexCoord2f(right, bottom);
5109 glVertex2i(dst_rect.right - dst_rect.left, 0);
5110 glEnd();
5111 checkGLcall("glEnd and previous");
5113 if (texture_target != dst_surface->texture_target)
5115 glDisable(texture_target);
5116 glEnable(dst_surface->texture_target);
5117 texture_target = dst_surface->texture_target;
5120 /* Now read the stretched and upside down image into the destination texture */
5121 context_bind_texture(context, texture_target, dst_surface->texture_name);
5122 glCopyTexSubImage2D(texture_target,
5124 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5125 0, 0, /* We blitted the image to the origin */
5126 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5127 checkGLcall("glCopyTexSubImage2D");
5129 if(drawBuffer == GL_BACK) {
5130 /* Write the back buffer backup back */
5131 if(backup) {
5132 if(texture_target != GL_TEXTURE_2D) {
5133 glDisable(texture_target);
5134 glEnable(GL_TEXTURE_2D);
5135 texture_target = GL_TEXTURE_2D;
5137 context_bind_texture(context, GL_TEXTURE_2D, backup);
5139 else
5141 if (texture_target != src_surface->texture_target)
5143 glDisable(texture_target);
5144 glEnable(src_surface->texture_target);
5145 texture_target = src_surface->texture_target;
5147 context_bind_texture(context, src_surface->texture_target, src_surface->texture_name);
5150 glBegin(GL_QUADS);
5151 /* top left */
5152 glTexCoord2f(0.0f, 0.0f);
5153 glVertex2i(0, fbheight);
5155 /* bottom left */
5156 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5157 glVertex2i(0, 0);
5159 /* bottom right */
5160 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5161 (float)fbheight / (float)src_surface->pow2Height);
5162 glVertex2i(fbwidth, 0);
5164 /* top right */
5165 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5166 glVertex2i(fbwidth, fbheight);
5167 glEnd();
5169 glDisable(texture_target);
5170 checkGLcall("glDisable(texture_target)");
5172 /* Cleanup */
5173 if (src != src_surface->texture_name && src != backup)
5175 glDeleteTextures(1, &src);
5176 checkGLcall("glDeleteTextures(1, &src)");
5178 if(backup) {
5179 glDeleteTextures(1, &backup);
5180 checkGLcall("glDeleteTextures(1, &backup)");
5183 LEAVE_GL();
5185 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5187 context_release(context);
5189 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5190 * path is never entered
5192 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5195 /* Front buffer coordinates are always full screen coordinates, but our GL
5196 * drawable is limited to the window's client area. The sysmem and texture
5197 * copies do have the full screen size. Note that GL has a bottom-left
5198 * origin, while D3D has a top-left origin. */
5199 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5201 UINT drawable_height;
5203 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5204 && surface == surface->container.u.swapchain->front_buffer)
5206 POINT offset = {0, 0};
5207 RECT windowsize;
5209 ScreenToClient(window, &offset);
5210 OffsetRect(rect, offset.x, offset.y);
5212 GetClientRect(window, &windowsize);
5213 drawable_height = windowsize.bottom - windowsize.top;
5215 else
5217 drawable_height = surface->resource.height;
5220 rect->top = drawable_height - rect->top;
5221 rect->bottom = drawable_height - rect->bottom;
5224 static void surface_blt_to_drawable(const struct wined3d_device *device,
5225 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5226 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5227 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5229 struct wined3d_context *context;
5230 RECT src_rect, dst_rect;
5232 src_rect = *src_rect_in;
5233 dst_rect = *dst_rect_in;
5235 /* Make sure the surface is up-to-date. This should probably use
5236 * surface_load_location() and worry about the destination surface too,
5237 * unless we're overwriting it completely. */
5238 surface_internal_preload(src_surface, SRGB_RGB);
5240 /* Activate the destination context, set it up for blitting */
5241 context = context_acquire(device, dst_surface);
5242 context_apply_blit_state(context, device);
5244 if (!surface_is_offscreen(dst_surface))
5245 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5247 device->blitter->set_shader(device->blit_priv, context, src_surface);
5249 ENTER_GL();
5251 if (color_key)
5253 glEnable(GL_ALPHA_TEST);
5254 checkGLcall("glEnable(GL_ALPHA_TEST)");
5256 /* When the primary render target uses P8, the alpha component
5257 * contains the palette index. Which means that the colorkey is one of
5258 * the palette entries. In other cases pixels that should be masked
5259 * away have alpha set to 0. */
5260 if (primary_render_target_is_p8(device))
5261 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
5262 else
5263 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5264 checkGLcall("glAlphaFunc");
5266 else
5268 glDisable(GL_ALPHA_TEST);
5269 checkGLcall("glDisable(GL_ALPHA_TEST)");
5272 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
5274 if (color_key)
5276 glDisable(GL_ALPHA_TEST);
5277 checkGLcall("glDisable(GL_ALPHA_TEST)");
5280 LEAVE_GL();
5282 /* Leave the opengl state valid for blitting */
5283 device->blitter->unset_shader(context->gl_info);
5285 if (wined3d_settings.strict_draw_ordering
5286 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5287 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5288 wglFlush(); /* Flush to ensure ordering across contexts. */
5290 context_release(context);
5293 /* Do not call while under the GL lock. */
5294 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
5296 struct wined3d_device *device = s->resource.device;
5297 const struct blit_shader *blitter;
5299 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5300 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5301 if (!blitter)
5303 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5304 return WINED3DERR_INVALIDCALL;
5307 return blitter->color_fill(device, s, rect, color);
5310 /* Do not call while under the GL lock. */
5311 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5312 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5313 WINED3DTEXTUREFILTERTYPE Filter)
5315 struct wined3d_device *device = dst_surface->resource.device;
5316 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5317 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5319 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5320 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5321 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5323 /* Get the swapchain. One of the surfaces has to be a primary surface */
5324 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5326 WARN("Destination is in sysmem, rejecting gl blt\n");
5327 return WINED3DERR_INVALIDCALL;
5330 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5331 dstSwapchain = dst_surface->container.u.swapchain;
5333 if (src_surface)
5335 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5337 WARN("Src is in sysmem, rejecting gl blt\n");
5338 return WINED3DERR_INVALIDCALL;
5341 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5342 srcSwapchain = src_surface->container.u.swapchain;
5345 /* Early sort out of cases where no render target is used */
5346 if (!dstSwapchain && !srcSwapchain
5347 && src_surface != device->fb.render_targets[0]
5348 && dst_surface != device->fb.render_targets[0])
5350 TRACE("No surface is render target, not using hardware blit.\n");
5351 return WINED3DERR_INVALIDCALL;
5354 /* No destination color keying supported */
5355 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5357 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5358 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5359 return WINED3DERR_INVALIDCALL;
5362 if (dstSwapchain && dstSwapchain == srcSwapchain)
5364 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5365 return WINED3DERR_INVALIDCALL;
5368 if (dstSwapchain && srcSwapchain)
5370 FIXME("Implement hardware blit between two different swapchains\n");
5371 return WINED3DERR_INVALIDCALL;
5374 if (dstSwapchain)
5376 /* Handled with regular texture -> swapchain blit */
5377 if (src_surface == device->fb.render_targets[0])
5378 TRACE("Blit from active render target to a swapchain\n");
5380 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5382 FIXME("Implement blit from a swapchain to the active render target\n");
5383 return WINED3DERR_INVALIDCALL;
5386 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5388 /* Blit from render target to texture */
5389 BOOL stretchx;
5391 /* P8 read back is not implemented */
5392 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5393 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5395 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5396 return WINED3DERR_INVALIDCALL;
5399 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5401 TRACE("Color keying not supported by frame buffer to texture blit\n");
5402 return WINED3DERR_INVALIDCALL;
5403 /* Destination color key is checked above */
5406 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5407 stretchx = TRUE;
5408 else
5409 stretchx = FALSE;
5411 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5412 * flip the image nor scale it.
5414 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5415 * -> If the app wants a image width an unscaled width, copy it line per line
5416 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5417 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5418 * back buffer. This is slower than reading line per line, thus not used for flipping
5419 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5420 * pixel by pixel. */
5421 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5422 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5424 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5425 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, Filter);
5426 } else {
5427 TRACE("Using hardware stretching to flip / stretch the texture\n");
5428 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5431 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5433 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5434 dst_surface->resource.allocatedMemory = NULL;
5435 dst_surface->resource.heapMemory = NULL;
5437 else
5439 dst_surface->flags &= ~SFLAG_INSYSMEM;
5442 return WINED3D_OK;
5444 else if (src_surface)
5446 /* Blit from offscreen surface to render target */
5447 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5448 WINEDDCOLORKEY oldBltCKey = src_surface->SrcBltCKey;
5450 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5452 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5453 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5454 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5456 FIXME("Unsupported blit operation falling back to software\n");
5457 return WINED3DERR_INVALIDCALL;
5460 /* Color keying: Check if we have to do a color keyed blt,
5461 * and if not check if a color key is activated.
5463 * Just modify the color keying parameters in the surface and restore them afterwards
5464 * The surface keeps track of the color key last used to load the opengl surface.
5465 * PreLoad will catch the change to the flags and color key and reload if necessary.
5467 if (flags & WINEDDBLT_KEYSRC)
5469 /* Use color key from surface */
5471 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5473 /* Use color key from DDBltFx */
5474 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5475 src_surface->SrcBltCKey = DDBltFx->ddckSrcColorkey;
5477 else
5479 /* Do not use color key */
5480 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5483 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5484 src_surface, src_rect, dst_surface, dst_rect);
5486 /* Restore the color key parameters */
5487 src_surface->CKeyFlags = oldCKeyFlags;
5488 src_surface->SrcBltCKey = oldBltCKey;
5490 surface_modify_location(dst_surface, dst_surface->draw_binding, TRUE);
5492 return WINED3D_OK;
5495 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5496 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5497 return WINED3DERR_INVALIDCALL;
5500 /* GL locking is done by the caller */
5501 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
5502 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5504 struct wined3d_device *device = surface->resource.device;
5505 const struct wined3d_gl_info *gl_info = context->gl_info;
5506 GLint compare_mode = GL_NONE;
5507 struct blt_info info;
5508 GLint old_binding = 0;
5509 RECT rect;
5511 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5513 glDisable(GL_CULL_FACE);
5514 glDisable(GL_BLEND);
5515 glDisable(GL_ALPHA_TEST);
5516 glDisable(GL_SCISSOR_TEST);
5517 glDisable(GL_STENCIL_TEST);
5518 glEnable(GL_DEPTH_TEST);
5519 glDepthFunc(GL_ALWAYS);
5520 glDepthMask(GL_TRUE);
5521 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5522 glViewport(x, y, w, h);
5524 SetRect(&rect, 0, h, w, 0);
5525 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5526 context_active_texture(context, context->gl_info, 0);
5527 glGetIntegerv(info.binding, &old_binding);
5528 glBindTexture(info.bind_target, texture);
5529 if (gl_info->supported[ARB_SHADOW])
5531 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5532 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5535 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5536 gl_info, info.tex_type, &surface->ds_current_size);
5538 glBegin(GL_TRIANGLE_STRIP);
5539 glTexCoord3fv(info.coords[0]);
5540 glVertex2f(-1.0f, -1.0f);
5541 glTexCoord3fv(info.coords[1]);
5542 glVertex2f(1.0f, -1.0f);
5543 glTexCoord3fv(info.coords[2]);
5544 glVertex2f(-1.0f, 1.0f);
5545 glTexCoord3fv(info.coords[3]);
5546 glVertex2f(1.0f, 1.0f);
5547 glEnd();
5549 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5550 glBindTexture(info.bind_target, old_binding);
5552 glPopAttrib();
5554 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5557 void surface_modify_ds_location(struct wined3d_surface *surface,
5558 DWORD location, UINT w, UINT h)
5560 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5562 if (location & ~SFLAG_DS_LOCATIONS)
5563 FIXME("Invalid location (%#x) specified.\n", location);
5565 surface->ds_current_size.cx = w;
5566 surface->ds_current_size.cy = h;
5567 surface->flags &= ~SFLAG_DS_LOCATIONS;
5568 surface->flags |= location;
5571 /* Context activation is done by the caller. */
5572 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5574 struct wined3d_device *device = surface->resource.device;
5575 GLsizei w, h;
5577 TRACE("surface %p, new location %#x.\n", surface, location);
5579 /* TODO: Make this work for modes other than FBO */
5580 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5582 if (!(surface->flags & location))
5584 w = surface->ds_current_size.cx;
5585 h = surface->ds_current_size.cy;
5586 surface->ds_current_size.cx = 0;
5587 surface->ds_current_size.cy = 0;
5589 else
5591 w = surface->resource.width;
5592 h = surface->resource.height;
5595 if (surface->ds_current_size.cx == surface->resource.width
5596 && surface->ds_current_size.cy == surface->resource.height)
5598 TRACE("Location (%#x) is already up to date.\n", location);
5599 return;
5602 if (surface->current_renderbuffer)
5604 FIXME("Not supported with fixed up depth stencil.\n");
5605 return;
5608 if (!(surface->flags & SFLAG_DS_LOCATIONS))
5610 /* This mostly happens when a depth / stencil is used without being
5611 * cleared first. In principle we could upload from sysmem, or
5612 * explicitly clear before first usage. For the moment there don't
5613 * appear to be a lot of applications depending on this, so a FIXME
5614 * should do. */
5615 FIXME("No up to date depth stencil location.\n");
5616 surface->flags |= location;
5617 surface->ds_current_size.cx = surface->resource.width;
5618 surface->ds_current_size.cy = surface->resource.height;
5619 return;
5622 if (location == SFLAG_DS_OFFSCREEN)
5624 GLint old_binding = 0;
5625 GLenum bind_target;
5627 /* The render target is allowed to be smaller than the depth/stencil
5628 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5629 * than the offscreen surface. Don't overwrite the offscreen surface
5630 * with undefined data. */
5631 w = min(w, context->swapchain->presentParms.BackBufferWidth);
5632 h = min(h, context->swapchain->presentParms.BackBufferHeight);
5634 TRACE("Copying onscreen depth buffer to depth texture.\n");
5636 ENTER_GL();
5638 if (!device->depth_blt_texture)
5640 glGenTextures(1, &device->depth_blt_texture);
5643 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5644 * directly on the FBO texture. That's because we need to flip. */
5645 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5646 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5647 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5649 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5650 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5652 else
5654 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5655 bind_target = GL_TEXTURE_2D;
5657 glBindTexture(bind_target, device->depth_blt_texture);
5658 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5659 * internal format, because the internal format might include stencil
5660 * data. In principle we should copy stencil data as well, but unless
5661 * the driver supports stencil export it's hard to do, and doesn't
5662 * seem to be needed in practice. If the hardware doesn't support
5663 * writing stencil data, the glCopyTexImage2D() call might trigger
5664 * software fallbacks. */
5665 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5666 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5667 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5668 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5669 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5670 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5671 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5672 glBindTexture(bind_target, old_binding);
5674 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5675 NULL, surface, SFLAG_INTEXTURE);
5676 context_set_draw_buffer(context, GL_NONE);
5677 glReadBuffer(GL_NONE);
5679 /* Do the actual blit */
5680 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
5681 checkGLcall("depth_blt");
5683 context_invalidate_state(context, STATE_FRAMEBUFFER);
5685 LEAVE_GL();
5687 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5689 else if (location == SFLAG_DS_ONSCREEN)
5691 TRACE("Copying depth texture to onscreen depth buffer.\n");
5693 ENTER_GL();
5695 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5696 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5697 surface_depth_blt(surface, context, surface->texture_name,
5698 0, surface->pow2Height - h, w, h, surface->texture_target);
5699 checkGLcall("depth_blt");
5701 context_invalidate_state(context, STATE_FRAMEBUFFER);
5703 LEAVE_GL();
5705 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5707 else
5709 ERR("Invalid location (%#x) specified.\n", location);
5712 surface->flags |= location;
5713 surface->ds_current_size.cx = surface->resource.width;
5714 surface->ds_current_size.cy = surface->resource.height;
5717 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5719 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5720 struct wined3d_surface *overlay;
5722 TRACE("surface %p, location %s, persistent %#x.\n",
5723 surface, debug_surflocation(location), persistent);
5725 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface)
5726 && (location & SFLAG_INDRAWABLE))
5727 ERR("Trying to invalidate the SFLAG_INDRAWABLE location of an offscreen surface.\n");
5729 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5730 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5731 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5733 if (persistent)
5735 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5736 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5738 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5740 TRACE("Passing to container.\n");
5741 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5744 surface->flags &= ~SFLAG_LOCATIONS;
5745 surface->flags |= location;
5747 /* Redraw emulated overlays, if any */
5748 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5750 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5752 surface_draw_overlay(overlay);
5756 else
5758 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5760 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5762 TRACE("Passing to container\n");
5763 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5766 surface->flags &= ~location;
5769 if (!(surface->flags & SFLAG_LOCATIONS))
5771 ERR("Surface %p does not have any up to date location.\n", surface);
5775 static DWORD resource_access_from_location(DWORD location)
5777 switch (location)
5779 case SFLAG_INSYSMEM:
5780 return WINED3D_RESOURCE_ACCESS_CPU;
5782 case SFLAG_INDRAWABLE:
5783 case SFLAG_INSRGBTEX:
5784 case SFLAG_INTEXTURE:
5785 case SFLAG_INRB_MULTISAMPLE:
5786 case SFLAG_INRB_RESOLVED:
5787 return WINED3D_RESOURCE_ACCESS_GPU;
5789 default:
5790 FIXME("Unhandled location %#x.\n", location);
5791 return 0;
5795 static void surface_load_sysmem(struct wined3d_surface *surface,
5796 const struct wined3d_gl_info *gl_info, const RECT *rect)
5798 surface_prepare_system_memory(surface);
5800 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED))
5801 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5803 /* Download the surface to system memory. */
5804 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5806 struct wined3d_device *device = surface->resource.device;
5807 struct wined3d_context *context;
5809 /* TODO: Use already acquired context when possible. */
5810 context = context_acquire(device, NULL);
5812 surface_bind_and_dirtify(surface, context, !(surface->flags & SFLAG_INTEXTURE));
5813 surface_download_data(surface, gl_info);
5815 context_release(context);
5817 return;
5820 if (surface->flags & SFLAG_INDRAWABLE)
5822 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5823 wined3d_surface_get_pitch(surface));
5824 return;
5827 FIXME("Can't load surface %p with location flags %#x into sysmem.\n",
5828 surface, surface->flags & SFLAG_LOCATIONS);
5831 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5832 const struct wined3d_gl_info *gl_info, const RECT *rect)
5834 struct wined3d_device *device = surface->resource.device;
5835 struct wined3d_format format;
5836 CONVERT_TYPES convert;
5837 UINT byte_count;
5838 BYTE *mem;
5840 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && surface_is_offscreen(surface))
5842 ERR("Trying to load offscreen surface into SFLAG_INDRAWABLE.\n");
5843 return WINED3DERR_INVALIDCALL;
5846 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5847 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5849 if (surface->flags & SFLAG_INTEXTURE)
5851 RECT r;
5853 surface_get_rect(surface, rect, &r);
5854 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5856 return WINED3D_OK;
5859 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5861 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5862 * path through sysmem. */
5863 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5866 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5868 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5869 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5870 * called. */
5871 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5873 struct wined3d_context *context;
5875 TRACE("Removing the pbo attached to surface %p.\n", surface);
5877 /* TODO: Use already acquired context when possible. */
5878 context = context_acquire(device, NULL);
5880 surface_remove_pbo(surface, gl_info);
5882 context_release(context);
5885 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5887 UINT height = surface->resource.height;
5888 UINT width = surface->resource.width;
5889 UINT src_pitch, dst_pitch;
5891 byte_count = format.conv_byte_count;
5892 src_pitch = wined3d_surface_get_pitch(surface);
5894 /* Stick to the alignment for the converted surface too, makes it
5895 * easier to load the surface. */
5896 dst_pitch = width * byte_count;
5897 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5899 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5901 ERR("Out of memory (%u).\n", dst_pitch * height);
5902 return E_OUTOFMEMORY;
5905 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5906 src_pitch, width, height, dst_pitch, convert, surface);
5908 surface->flags |= SFLAG_CONVERTED;
5910 else
5912 surface->flags &= ~SFLAG_CONVERTED;
5913 mem = surface->resource.allocatedMemory;
5914 byte_count = format.byte_count;
5917 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5919 /* Don't delete PBO memory. */
5920 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5921 HeapFree(GetProcessHeap(), 0, mem);
5923 return WINED3D_OK;
5926 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5927 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5929 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5930 struct wined3d_device *device = surface->resource.device;
5931 struct wined3d_context *context;
5932 UINT width, src_pitch, dst_pitch;
5933 struct wined3d_bo_address data;
5934 struct wined3d_format format;
5935 POINT dst_point = {0, 0};
5936 CONVERT_TYPES convert;
5937 BYTE *mem;
5939 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
5940 && surface_is_offscreen(surface)
5941 && (surface->flags & SFLAG_INDRAWABLE))
5943 surface_load_fb_texture(surface, srgb);
5945 return WINED3D_OK;
5948 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
5949 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
5950 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5951 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5952 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5954 if (srgb)
5955 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
5956 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
5957 else
5958 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
5959 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
5961 return WINED3D_OK;
5964 if (surface->flags & (SFLAG_INRB_MULTISAMPLE | SFLAG_INRB_RESOLVED)
5965 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
5966 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5967 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
5968 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
5970 DWORD src_location = surface->flags & SFLAG_INRB_RESOLVED ? SFLAG_INRB_RESOLVED : SFLAG_INRB_MULTISAMPLE;
5971 DWORD dst_location = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
5972 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
5974 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, src_location,
5975 &rect, surface, dst_location, &rect);
5977 return WINED3D_OK;
5980 /* Upload from system memory */
5982 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
5983 TRUE /* We will use textures */, &format, &convert);
5985 if (srgb)
5987 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
5989 /* Performance warning... */
5990 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
5991 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5994 else
5996 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
5998 /* Performance warning... */
5999 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6000 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6004 if (!(surface->flags & SFLAG_INSYSMEM))
6006 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6007 /* Lets hope we get it from somewhere... */
6008 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6011 /* TODO: Use already acquired context when possible. */
6012 context = context_acquire(device, NULL);
6014 surface_prepare_texture(surface, context, srgb);
6015 surface_bind_and_dirtify(surface, context, srgb);
6017 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6019 surface->flags |= SFLAG_GLCKEY;
6020 surface->glCKey = surface->SrcBltCKey;
6022 else surface->flags &= ~SFLAG_GLCKEY;
6024 width = surface->resource.width;
6025 src_pitch = wined3d_surface_get_pitch(surface);
6027 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6028 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6029 * called. */
6030 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6032 TRACE("Removing the pbo attached to surface %p.\n", surface);
6033 surface_remove_pbo(surface, gl_info);
6036 if (format.convert)
6038 /* This code is entered for texture formats which need a fixup. */
6039 UINT height = surface->resource.height;
6041 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6042 dst_pitch = width * format.conv_byte_count;
6043 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6045 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6047 ERR("Out of memory (%u).\n", dst_pitch * height);
6048 context_release(context);
6049 return E_OUTOFMEMORY;
6051 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
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);
6071 else
6073 mem = surface->resource.allocatedMemory;
6076 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6077 data.addr = mem;
6078 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
6080 context_release(context);
6082 /* Don't delete PBO memory. */
6083 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6084 HeapFree(GetProcessHeap(), 0, mem);
6086 return WINED3D_OK;
6089 static void surface_multisample_resolve(struct wined3d_surface *surface)
6091 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6093 if (!(surface->flags & SFLAG_INRB_MULTISAMPLE))
6094 ERR("Trying to resolve multisampled surface %p, but location SFLAG_INRB_MULTISAMPLE not current.\n", surface);
6096 surface_blt_fbo(surface->resource.device, WINED3DTEXF_POINT,
6097 surface, SFLAG_INRB_MULTISAMPLE, &rect, surface, SFLAG_INRB_RESOLVED, &rect);
6100 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6102 struct wined3d_device *device = surface->resource.device;
6103 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6104 HRESULT hr;
6106 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6108 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6110 if (location == SFLAG_INTEXTURE)
6112 struct wined3d_context *context = context_acquire(device, NULL);
6113 surface_load_ds_location(surface, context, SFLAG_DS_OFFSCREEN);
6114 context_release(context);
6115 return WINED3D_OK;
6117 else
6119 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6120 return WINED3DERR_INVALIDCALL;
6124 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6125 location = SFLAG_INTEXTURE;
6127 if (surface->flags & location)
6129 TRACE("Location already up to date.\n");
6130 return WINED3D_OK;
6133 if (WARN_ON(d3d_surface))
6135 DWORD required_access = resource_access_from_location(location);
6136 if ((surface->resource.access_flags & required_access) != required_access)
6137 WARN("Operation requires %#x access, but surface only has %#x.\n",
6138 required_access, surface->resource.access_flags);
6141 if (!(surface->flags & SFLAG_LOCATIONS))
6143 ERR("Surface %p does not have any up to date location.\n", surface);
6144 surface->flags |= SFLAG_LOST;
6145 return WINED3DERR_DEVICELOST;
6148 switch (location)
6150 case SFLAG_INSYSMEM:
6151 surface_load_sysmem(surface, gl_info, rect);
6152 break;
6154 case SFLAG_INDRAWABLE:
6155 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6156 return hr;
6157 break;
6159 case SFLAG_INRB_RESOLVED:
6160 surface_multisample_resolve(surface);
6161 break;
6163 case SFLAG_INTEXTURE:
6164 case SFLAG_INSRGBTEX:
6165 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6166 return hr;
6167 break;
6169 default:
6170 ERR("Don't know how to handle location %#x.\n", location);
6171 break;
6174 if (!rect)
6176 surface->flags |= location;
6178 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6179 surface_evict_sysmem(surface);
6182 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6183 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6185 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6188 return WINED3D_OK;
6191 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6193 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6195 /* Not on a swapchain - must be offscreen */
6196 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6198 /* The front buffer is always onscreen */
6199 if (surface == swapchain->front_buffer) return FALSE;
6201 /* If the swapchain is rendered to an FBO, the backbuffer is
6202 * offscreen, otherwise onscreen */
6203 return swapchain->render_to_fbo;
6206 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6207 /* Context activation is done by the caller. */
6208 static void ffp_blit_free(struct wined3d_device *device) { }
6210 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6211 /* Context activation is done by the caller. */
6212 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6214 BYTE table[256][4];
6215 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6217 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6219 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6220 ENTER_GL();
6221 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6222 LEAVE_GL();
6225 /* Context activation is done by the caller. */
6226 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6228 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6230 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6231 * else the surface is converted in software at upload time in LoadLocation.
6233 if (!(surface->flags & SFLAG_CONVERTED) && fixup == COMPLEX_FIXUP_P8
6234 && context->gl_info->supported[EXT_PALETTED_TEXTURE])
6235 ffp_blit_p8_upload_palette(surface, context->gl_info);
6237 ENTER_GL();
6238 glEnable(surface->texture_target);
6239 checkGLcall("glEnable(surface->texture_target)");
6240 LEAVE_GL();
6241 return WINED3D_OK;
6244 /* Context activation is done by the caller. */
6245 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6247 ENTER_GL();
6248 glDisable(GL_TEXTURE_2D);
6249 checkGLcall("glDisable(GL_TEXTURE_2D)");
6250 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6252 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6253 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6255 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6257 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6258 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6260 LEAVE_GL();
6263 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6264 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6265 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6267 enum complex_fixup src_fixup;
6269 switch (blit_op)
6271 case WINED3D_BLIT_OP_COLOR_BLIT:
6272 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
6273 return FALSE;
6275 src_fixup = get_complex_fixup(src_format->color_fixup);
6276 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6278 TRACE("Checking support for fixup:\n");
6279 dump_color_fixup_desc(src_format->color_fixup);
6282 if (!is_identity_fixup(dst_format->color_fixup))
6284 TRACE("Destination fixups are not supported\n");
6285 return FALSE;
6288 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6290 TRACE("P8 fixup supported\n");
6291 return TRUE;
6294 /* We only support identity conversions. */
6295 if (is_identity_fixup(src_format->color_fixup))
6297 TRACE("[OK]\n");
6298 return TRUE;
6301 TRACE("[FAILED]\n");
6302 return FALSE;
6304 case WINED3D_BLIT_OP_COLOR_FILL:
6305 if (dst_pool == WINED3DPOOL_SYSTEMMEM)
6306 return FALSE;
6308 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6310 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6311 return FALSE;
6313 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6315 TRACE("Color fill not supported\n");
6316 return FALSE;
6319 /* FIXME: We should reject color fills on formats with fixups,
6320 * but this would break P8 color fills for example. */
6322 return TRUE;
6324 case WINED3D_BLIT_OP_DEPTH_FILL:
6325 return TRUE;
6327 default:
6328 TRACE("Unsupported blit_op=%d\n", blit_op);
6329 return FALSE;
6333 /* Do not call while under the GL lock. */
6334 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6335 const RECT *dst_rect, const struct wined3d_color *color)
6337 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6338 struct wined3d_fb_state fb = {&dst_surface, NULL};
6340 return device_clear_render_targets(device, 1, &fb,
6341 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6344 /* Do not call while under the GL lock. */
6345 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6346 struct wined3d_surface *surface, const RECT *rect, float depth)
6348 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6349 struct wined3d_fb_state fb = {NULL, surface};
6351 return device_clear_render_targets(device, 0, &fb,
6352 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6355 const struct blit_shader ffp_blit = {
6356 ffp_blit_alloc,
6357 ffp_blit_free,
6358 ffp_blit_set,
6359 ffp_blit_unset,
6360 ffp_blit_supported,
6361 ffp_blit_color_fill,
6362 ffp_blit_depth_fill,
6365 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6367 return WINED3D_OK;
6370 /* Context activation is done by the caller. */
6371 static void cpu_blit_free(struct wined3d_device *device)
6375 /* Context activation is done by the caller. */
6376 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
6378 return WINED3D_OK;
6381 /* Context activation is done by the caller. */
6382 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6386 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6387 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6388 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6390 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6392 return TRUE;
6395 return FALSE;
6398 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
6399 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
6400 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
6402 UINT row_block_count;
6403 const BYTE *src_row;
6404 BYTE *dst_row;
6405 UINT x, y;
6407 src_row = src_data;
6408 dst_row = dst_data;
6410 row_block_count = (update_w + format->block_width - 1) / format->block_width;
6412 if (!flags)
6414 for (y = 0; y < update_h; y += format->block_height)
6416 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
6417 src_row += src_pitch;
6418 dst_row += dst_pitch;
6421 return WINED3D_OK;
6424 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
6426 src_row += (((update_h / format->block_height) - 1) * src_pitch);
6428 switch (format->id)
6430 case WINED3DFMT_DXT1:
6431 for (y = 0; y < update_h; y += format->block_height)
6433 struct block
6435 WORD color[2];
6436 BYTE control_row[4];
6439 const struct block *s = (const struct block *)src_row;
6440 struct block *d = (struct block *)dst_row;
6442 for (x = 0; x < row_block_count; ++x)
6444 d[x].color[0] = s[x].color[0];
6445 d[x].color[1] = s[x].color[1];
6446 d[x].control_row[0] = s[x].control_row[3];
6447 d[x].control_row[1] = s[x].control_row[2];
6448 d[x].control_row[2] = s[x].control_row[1];
6449 d[x].control_row[3] = s[x].control_row[0];
6451 src_row -= src_pitch;
6452 dst_row += dst_pitch;
6454 return WINED3D_OK;
6456 case WINED3DFMT_DXT3:
6457 for (y = 0; y < update_h; y += format->block_height)
6459 struct block
6461 WORD alpha_row[4];
6462 WORD color[2];
6463 BYTE control_row[4];
6466 const struct block *s = (const struct block *)src_row;
6467 struct block *d = (struct block *)dst_row;
6469 for (x = 0; x < row_block_count; ++x)
6471 d[x].alpha_row[0] = s[x].alpha_row[3];
6472 d[x].alpha_row[1] = s[x].alpha_row[2];
6473 d[x].alpha_row[2] = s[x].alpha_row[1];
6474 d[x].alpha_row[3] = s[x].alpha_row[0];
6475 d[x].color[0] = s[x].color[0];
6476 d[x].color[1] = s[x].color[1];
6477 d[x].control_row[0] = s[x].control_row[3];
6478 d[x].control_row[1] = s[x].control_row[2];
6479 d[x].control_row[2] = s[x].control_row[1];
6480 d[x].control_row[3] = s[x].control_row[0];
6482 src_row -= src_pitch;
6483 dst_row += dst_pitch;
6485 return WINED3D_OK;
6487 default:
6488 FIXME("Compressed flip not implemented for format %s.\n",
6489 debug_d3dformat(format->id));
6490 return E_NOTIMPL;
6494 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
6495 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
6497 return E_NOTIMPL;
6500 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6501 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6502 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6504 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6505 const struct wined3d_format *src_format, *dst_format;
6506 struct wined3d_surface *orig_src = src_surface;
6507 WINED3DLOCKED_RECT dlock, slock;
6508 HRESULT hr = WINED3D_OK;
6509 const BYTE *sbuf;
6510 RECT xdst,xsrc;
6511 BYTE *dbuf;
6512 int x, y;
6514 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6515 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6516 flags, fx, debug_d3dtexturefiltertype(filter));
6518 xsrc = *src_rect;
6520 if (!src_surface)
6522 RECT full_rect;
6524 full_rect.left = 0;
6525 full_rect.top = 0;
6526 full_rect.right = dst_surface->resource.width;
6527 full_rect.bottom = dst_surface->resource.height;
6528 IntersectRect(&xdst, &full_rect, dst_rect);
6530 else
6532 BOOL clip_horiz, clip_vert;
6534 xdst = *dst_rect;
6535 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6536 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6538 if (clip_vert || clip_horiz)
6540 /* Now check if this is a special case or not... */
6541 if ((flags & WINEDDBLT_DDFX)
6542 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6543 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6545 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6546 return WINED3D_OK;
6549 if (clip_horiz)
6551 if (xdst.left < 0)
6553 xsrc.left -= xdst.left;
6554 xdst.left = 0;
6556 if (xdst.right > dst_surface->resource.width)
6558 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6559 xdst.right = (int)dst_surface->resource.width;
6563 if (clip_vert)
6565 if (xdst.top < 0)
6567 xsrc.top -= xdst.top;
6568 xdst.top = 0;
6570 if (xdst.bottom > dst_surface->resource.height)
6572 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6573 xdst.bottom = (int)dst_surface->resource.height;
6577 /* And check if after clipping something is still to be done... */
6578 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6579 || (xdst.left >= (int)dst_surface->resource.width)
6580 || (xdst.top >= (int)dst_surface->resource.height)
6581 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6582 || (xsrc.left >= (int)src_surface->resource.width)
6583 || (xsrc.top >= (int)src_surface->resource.height))
6585 TRACE("Nothing to be done after clipping.\n");
6586 return WINED3D_OK;
6591 if (src_surface == dst_surface)
6593 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6594 slock = dlock;
6595 src_format = dst_surface->resource.format;
6596 dst_format = src_format;
6598 else
6600 dst_format = dst_surface->resource.format;
6601 if (src_surface)
6603 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6605 src_surface = surface_convert_format(src_surface, dst_format->id);
6606 if (!src_surface)
6608 /* The conv function writes a FIXME */
6609 WARN("Cannot convert source surface format to dest format.\n");
6610 goto release;
6613 wined3d_surface_map(src_surface, &slock, NULL, WINED3DLOCK_READONLY);
6614 src_format = src_surface->resource.format;
6616 else
6618 src_format = dst_format;
6620 if (dst_rect)
6621 wined3d_surface_map(dst_surface, &dlock, &xdst, 0);
6622 else
6623 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6626 bpp = dst_surface->resource.format->byte_count;
6627 srcheight = xsrc.bottom - xsrc.top;
6628 srcwidth = xsrc.right - xsrc.left;
6629 dstheight = xdst.bottom - xdst.top;
6630 dstwidth = xdst.right - xdst.left;
6631 width = (xdst.right - xdst.left) * bpp;
6633 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
6635 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6637 if (src_surface == dst_surface)
6639 FIXME("Only plain blits supported on compressed surfaces.\n");
6640 hr = E_NOTIMPL;
6641 goto release;
6644 if (srcheight != dstheight || srcwidth != dstwidth)
6646 WARN("Stretching not supported on compressed surfaces.\n");
6647 hr = WINED3DERR_INVALIDCALL;
6648 goto release;
6651 if (srcwidth & (src_format->block_width - 1) || srcheight & (src_format->block_height - 1))
6653 WARN("Rectangle not block-aligned.\n");
6654 hr = WINED3DERR_INVALIDCALL;
6655 goto release;
6658 hr = surface_cpu_blt_compressed(slock.pBits, dlock.pBits,
6659 slock.Pitch, dlock.Pitch, dstwidth, dstheight,
6660 src_format, flags, fx);
6661 goto release;
6664 if (dst_rect && src_surface != dst_surface)
6665 dbuf = dlock.pBits;
6666 else
6667 dbuf = (BYTE*)dlock.pBits+(xdst.top*dlock.Pitch)+(xdst.left*bpp);
6669 /* First, all the 'source-less' blits */
6670 if (flags & WINEDDBLT_COLORFILL)
6672 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dlock.Pitch, fx->u5.dwFillColor);
6673 flags &= ~WINEDDBLT_COLORFILL;
6676 if (flags & WINEDDBLT_DEPTHFILL)
6678 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6680 if (flags & WINEDDBLT_ROP)
6682 /* Catch some degenerate cases here. */
6683 switch (fx->dwROP)
6685 case BLACKNESS:
6686 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,0);
6687 break;
6688 case 0xAA0029: /* No-op */
6689 break;
6690 case WHITENESS:
6691 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,~0);
6692 break;
6693 case SRCCOPY: /* Well, we do that below? */
6694 break;
6695 default:
6696 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6697 goto error;
6699 flags &= ~WINEDDBLT_ROP;
6701 if (flags & WINEDDBLT_DDROPS)
6703 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6705 /* Now the 'with source' blits. */
6706 if (src_surface)
6708 const BYTE *sbase;
6709 int sx, xinc, sy, yinc;
6711 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6712 goto release;
6714 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6715 && (srcwidth != dstwidth || srcheight != dstheight))
6717 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6718 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6721 sbase = (BYTE*)slock.pBits+(xsrc.top*slock.Pitch)+xsrc.left*bpp;
6722 xinc = (srcwidth << 16) / dstwidth;
6723 yinc = (srcheight << 16) / dstheight;
6725 if (!flags)
6727 /* No effects, we can cheat here. */
6728 if (dstwidth == srcwidth)
6730 if (dstheight == srcheight)
6732 /* No stretching in either direction. This needs to be as
6733 * fast as possible. */
6734 sbuf = sbase;
6736 /* Check for overlapping surfaces. */
6737 if (src_surface != dst_surface || xdst.top < xsrc.top
6738 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6740 /* No overlap, or dst above src, so copy from top downwards. */
6741 for (y = 0; y < dstheight; ++y)
6743 memcpy(dbuf, sbuf, width);
6744 sbuf += slock.Pitch;
6745 dbuf += dlock.Pitch;
6748 else if (xdst.top > xsrc.top)
6750 /* Copy from bottom upwards. */
6751 sbuf += (slock.Pitch*dstheight);
6752 dbuf += (dlock.Pitch*dstheight);
6753 for (y = 0; y < dstheight; ++y)
6755 sbuf -= slock.Pitch;
6756 dbuf -= dlock.Pitch;
6757 memcpy(dbuf, sbuf, width);
6760 else
6762 /* Src and dst overlapping on the same line, use memmove. */
6763 for (y = 0; y < dstheight; ++y)
6765 memmove(dbuf, sbuf, width);
6766 sbuf += slock.Pitch;
6767 dbuf += dlock.Pitch;
6771 else
6773 /* Stretching in y direction only. */
6774 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6776 sbuf = sbase + (sy >> 16) * slock.Pitch;
6777 memcpy(dbuf, sbuf, width);
6778 dbuf += dlock.Pitch;
6782 else
6784 /* Stretching in X direction. */
6785 int last_sy = -1;
6786 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6788 sbuf = sbase + (sy >> 16) * slock.Pitch;
6790 if ((sy >> 16) == (last_sy >> 16))
6792 /* This source row is the same as last source row -
6793 * Copy the already stretched row. */
6794 memcpy(dbuf, dbuf - dlock.Pitch, width);
6796 else
6798 #define STRETCH_ROW(type) \
6799 do { \
6800 const type *s = (const type *)sbuf; \
6801 type *d = (type *)dbuf; \
6802 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6803 d[x] = s[sx >> 16]; \
6804 } while(0)
6806 switch(bpp)
6808 case 1:
6809 STRETCH_ROW(BYTE);
6810 break;
6811 case 2:
6812 STRETCH_ROW(WORD);
6813 break;
6814 case 4:
6815 STRETCH_ROW(DWORD);
6816 break;
6817 case 3:
6819 const BYTE *s;
6820 BYTE *d = dbuf;
6821 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6823 DWORD pixel;
6825 s = sbuf + 3 * (sx >> 16);
6826 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6827 d[0] = (pixel ) & 0xff;
6828 d[1] = (pixel >> 8) & 0xff;
6829 d[2] = (pixel >> 16) & 0xff;
6830 d += 3;
6832 break;
6834 default:
6835 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6836 hr = WINED3DERR_NOTAVAILABLE;
6837 goto error;
6839 #undef STRETCH_ROW
6841 dbuf += dlock.Pitch;
6842 last_sy = sy;
6846 else
6848 LONG dstyinc = dlock.Pitch, dstxinc = bpp;
6849 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6850 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6851 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6853 /* The color keying flags are checked for correctness in ddraw */
6854 if (flags & WINEDDBLT_KEYSRC)
6856 keylow = src_surface->SrcBltCKey.dwColorSpaceLowValue;
6857 keyhigh = src_surface->SrcBltCKey.dwColorSpaceHighValue;
6859 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6861 keylow = fx->ddckSrcColorkey.dwColorSpaceLowValue;
6862 keyhigh = fx->ddckSrcColorkey.dwColorSpaceHighValue;
6865 if (flags & WINEDDBLT_KEYDEST)
6867 /* Destination color keys are taken from the source surface! */
6868 destkeylow = src_surface->DestBltCKey.dwColorSpaceLowValue;
6869 destkeyhigh = src_surface->DestBltCKey.dwColorSpaceHighValue;
6871 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6873 destkeylow = fx->ddckDestColorkey.dwColorSpaceLowValue;
6874 destkeyhigh = fx->ddckDestColorkey.dwColorSpaceHighValue;
6877 if (bpp == 1)
6879 keymask = 0xff;
6881 else
6883 keymask = src_format->red_mask
6884 | src_format->green_mask
6885 | src_format->blue_mask;
6887 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6890 if (flags & WINEDDBLT_DDFX)
6892 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6893 LONG tmpxy;
6894 dTopLeft = dbuf;
6895 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6896 dBottomLeft = dTopLeft + ((dstheight - 1) * dlock.Pitch);
6897 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6899 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6901 /* I don't think we need to do anything about this flag */
6902 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6904 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6906 tmp = dTopRight;
6907 dTopRight = dTopLeft;
6908 dTopLeft = tmp;
6909 tmp = dBottomRight;
6910 dBottomRight = dBottomLeft;
6911 dBottomLeft = tmp;
6912 dstxinc = dstxinc * -1;
6914 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6916 tmp = dTopLeft;
6917 dTopLeft = dBottomLeft;
6918 dBottomLeft = tmp;
6919 tmp = dTopRight;
6920 dTopRight = dBottomRight;
6921 dBottomRight = tmp;
6922 dstyinc = dstyinc * -1;
6924 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6926 /* I don't think we need to do anything about this flag */
6927 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6929 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6931 tmp = dBottomRight;
6932 dBottomRight = dTopLeft;
6933 dTopLeft = tmp;
6934 tmp = dBottomLeft;
6935 dBottomLeft = dTopRight;
6936 dTopRight = tmp;
6937 dstxinc = dstxinc * -1;
6938 dstyinc = dstyinc * -1;
6940 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6942 tmp = dTopLeft;
6943 dTopLeft = dBottomLeft;
6944 dBottomLeft = dBottomRight;
6945 dBottomRight = dTopRight;
6946 dTopRight = tmp;
6947 tmpxy = dstxinc;
6948 dstxinc = dstyinc;
6949 dstyinc = tmpxy;
6950 dstxinc = dstxinc * -1;
6952 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6954 tmp = dTopLeft;
6955 dTopLeft = dTopRight;
6956 dTopRight = dBottomRight;
6957 dBottomRight = dBottomLeft;
6958 dBottomLeft = tmp;
6959 tmpxy = dstxinc;
6960 dstxinc = dstyinc;
6961 dstyinc = tmpxy;
6962 dstyinc = dstyinc * -1;
6964 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6966 /* I don't think we need to do anything about this flag */
6967 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6969 dbuf = dTopLeft;
6970 flags &= ~(WINEDDBLT_DDFX);
6973 #define COPY_COLORKEY_FX(type) \
6974 do { \
6975 const type *s; \
6976 type *d = (type *)dbuf, *dx, tmp; \
6977 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6979 s = (const type *)(sbase + (sy >> 16) * slock.Pitch); \
6980 dx = d; \
6981 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6983 tmp = s[sx >> 16]; \
6984 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6985 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6987 dx[0] = tmp; \
6989 dx = (type *)(((BYTE *)dx) + dstxinc); \
6991 d = (type *)(((BYTE *)d) + dstyinc); \
6993 } while(0)
6995 switch (bpp)
6997 case 1:
6998 COPY_COLORKEY_FX(BYTE);
6999 break;
7000 case 2:
7001 COPY_COLORKEY_FX(WORD);
7002 break;
7003 case 4:
7004 COPY_COLORKEY_FX(DWORD);
7005 break;
7006 case 3:
7008 const BYTE *s;
7009 BYTE *d = dbuf, *dx;
7010 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
7012 sbuf = sbase + (sy >> 16) * slock.Pitch;
7013 dx = d;
7014 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
7016 DWORD pixel, dpixel = 0;
7017 s = sbuf + 3 * (sx>>16);
7018 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
7019 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
7020 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
7021 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
7023 dx[0] = (pixel ) & 0xff;
7024 dx[1] = (pixel >> 8) & 0xff;
7025 dx[2] = (pixel >> 16) & 0xff;
7027 dx += dstxinc;
7029 d += dstyinc;
7031 break;
7033 default:
7034 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
7035 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
7036 hr = WINED3DERR_NOTAVAILABLE;
7037 goto error;
7038 #undef COPY_COLORKEY_FX
7043 error:
7044 if (flags && FIXME_ON(d3d_surface))
7046 FIXME("\tUnsupported flags: %#x.\n", flags);
7049 release:
7050 wined3d_surface_unmap(dst_surface);
7051 if (src_surface && src_surface != dst_surface)
7052 wined3d_surface_unmap(src_surface);
7053 /* Release the converted surface, if any. */
7054 if (src_surface && src_surface != orig_src)
7055 wined3d_surface_decref(src_surface);
7057 return hr;
7060 /* Do not call while under the GL lock. */
7061 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7062 const RECT *dst_rect, const struct wined3d_color *color)
7064 static const RECT src_rect;
7065 WINEDDBLTFX BltFx;
7067 memset(&BltFx, 0, sizeof(BltFx));
7068 BltFx.dwSize = sizeof(BltFx);
7069 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7070 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7071 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7074 /* Do not call while under the GL lock. */
7075 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7076 struct wined3d_surface *surface, const RECT *rect, float depth)
7078 FIXME("Depth filling not implemented by cpu_blit.\n");
7079 return WINED3DERR_INVALIDCALL;
7082 const struct blit_shader cpu_blit = {
7083 cpu_blit_alloc,
7084 cpu_blit_free,
7085 cpu_blit_set,
7086 cpu_blit_unset,
7087 cpu_blit_supported,
7088 cpu_blit_color_fill,
7089 cpu_blit_depth_fill,
7092 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7093 UINT width, UINT height, UINT level, WINED3DMULTISAMPLE_TYPE multisample_type,
7094 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7095 WINED3DPOOL pool, DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops)
7097 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7098 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7099 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
7100 unsigned int resource_size;
7101 HRESULT hr;
7103 if (multisample_quality > 0)
7105 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7106 multisample_quality = 0;
7109 /* Quick lockable sanity check.
7110 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7111 * this function is too deep to need to care about things like this.
7112 * Levels need to be checked too, since they all affect what can be done. */
7113 switch (pool)
7115 case WINED3DPOOL_SCRATCH:
7116 if (!lockable)
7118 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7119 "which are mutually exclusive, setting lockable to TRUE.\n");
7120 lockable = TRUE;
7122 break;
7124 case WINED3DPOOL_SYSTEMMEM:
7125 if (!lockable)
7126 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7127 break;
7129 case WINED3DPOOL_MANAGED:
7130 if (usage & WINED3DUSAGE_DYNAMIC)
7131 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7132 break;
7134 case WINED3DPOOL_DEFAULT:
7135 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7136 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7137 break;
7139 default:
7140 FIXME("Unknown pool %#x.\n", pool);
7141 break;
7144 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7145 FIXME("Trying to create a render target that isn't in the default pool.\n");
7147 /* FIXME: Check that the format is supported by the device. */
7149 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7150 if (!resource_size)
7151 return WINED3DERR_INVALIDCALL;
7153 surface->surface_type = surface_type;
7155 switch (surface_type)
7157 case SURFACE_OPENGL:
7158 surface->surface_ops = &surface_ops;
7159 break;
7161 case SURFACE_GDI:
7162 surface->surface_ops = &gdi_surface_ops;
7163 break;
7165 default:
7166 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7167 return WINED3DERR_INVALIDCALL;
7170 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7171 multisample_type, multisample_quality, usage, pool, width, height, 1,
7172 resource_size, parent, parent_ops, &surface_resource_ops);
7173 if (FAILED(hr))
7175 WARN("Failed to initialize resource, returning %#x.\n", hr);
7176 return hr;
7179 /* "Standalone" surface. */
7180 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7182 surface->texture_level = level;
7183 list_init(&surface->overlays);
7185 /* Flags */
7186 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7187 if (flags & WINED3D_SURFACE_DISCARD)
7188 surface->flags |= SFLAG_DISCARD;
7189 if (flags & WINED3D_SURFACE_PIN_SYSMEM)
7190 surface->flags |= SFLAG_PIN_SYSMEM;
7191 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7192 surface->flags |= SFLAG_LOCKABLE;
7193 /* I'm not sure if this qualifies as a hack or as an optimization. It
7194 * seems reasonable to assume that lockable render targets will get
7195 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7196 * creation. However, the other reason we want to do this is that several
7197 * ddraw applications access surface memory while the surface isn't
7198 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7199 * future locks prevents these from crashing. */
7200 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7201 surface->flags |= SFLAG_DYNLOCK;
7203 /* Mark the texture as dirty so that it gets loaded first time around. */
7204 surface_add_dirty_rect(surface, NULL);
7205 list_init(&surface->renderbuffers);
7207 TRACE("surface %p, memory %p, size %u\n",
7208 surface, surface->resource.allocatedMemory, surface->resource.size);
7210 /* Call the private setup routine */
7211 hr = surface->surface_ops->surface_private_setup(surface);
7212 if (FAILED(hr))
7214 ERR("Private setup failed, returning %#x\n", hr);
7215 surface_cleanup(surface);
7216 return hr;
7219 /* Similar to lockable rendertargets above, creating the DIB section
7220 * during surface initialization prevents the sysmem pointer from changing
7221 * after a wined3d_surface_getdc() call. */
7222 if ((usage & WINED3DUSAGE_OWNDC) && !surface->hDC
7223 && SUCCEEDED(surface_create_dib_section(surface)))
7225 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
7226 surface->resource.heapMemory = NULL;
7227 surface->resource.allocatedMemory = surface->dib.bitmap_data;
7230 return hr;
7233 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7234 enum wined3d_format_id format_id, UINT level, DWORD usage, WINED3DPOOL pool,
7235 WINED3DMULTISAMPLE_TYPE multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7236 DWORD flags, void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7238 struct wined3d_surface *object;
7239 HRESULT hr;
7241 TRACE("device %p, width %u, height %u, format %s, level %u\n",
7242 device, width, height, debug_d3dformat(format_id), level);
7243 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7244 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7245 TRACE("surface_type %#x, flags %#x, parent %p, parent_ops %p.\n", surface_type, flags, parent, parent_ops);
7247 if (surface_type == SURFACE_OPENGL && !device->adapter)
7249 ERR("OpenGL surfaces are not available without OpenGL.\n");
7250 return WINED3DERR_NOTAVAILABLE;
7253 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7254 if (!object)
7256 ERR("Failed to allocate surface memory.\n");
7257 return WINED3DERR_OUTOFVIDEOMEMORY;
7260 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level,
7261 multisample_type, multisample_quality, device, usage, format_id, pool, flags, parent, parent_ops);
7262 if (FAILED(hr))
7264 WARN("Failed to initialize surface, returning %#x.\n", hr);
7265 HeapFree(GetProcessHeap(), 0, object);
7266 return hr;
7269 TRACE("Created surface %p.\n", object);
7270 *surface = object;
7272 return hr;