wined3d: Merge the IWineD3DSurface::Flip() implementations.
[wine/multimedia.git] / dlls / wined3d / surface.c
blobd5fe0481540189f44587110b055db614a672541e
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1997-2000 Marcus Meissner
5 * Copyright 1998-2000 Lionel Ulmer
6 * Copyright 2000-2001 TransGaming Technologies Inc.
7 * Copyright 2002-2005 Jason Edmeades
8 * Copyright 2002-2003 Raphael Junqueira
9 * Copyright 2004 Christian Costa
10 * Copyright 2005 Oliver Stieber
11 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
12 * Copyright 2007-2008 Henri Verbeet
13 * Copyright 2006-2008 Roderick Colenbrander
14 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
16 * This library is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Lesser General Public
18 * License as published by the Free Software Foundation; either
19 * version 2.1 of the License, or (at your option) any later version.
21 * This library is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Lesser General Public License for more details.
26 * You should have received a copy of the GNU Lesser General Public
27 * License along with this library; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "config.h"
32 #include "wine/port.h"
33 #include "wined3d_private.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
36 WINE_DECLARE_DEBUG_CHANNEL(d3d);
38 static void surface_cleanup(IWineD3DSurfaceImpl *This)
40 TRACE("(%p) : Cleaning up.\n", This);
42 if (This->texture_name || (This->flags & SFLAG_PBO) || !list_empty(&This->renderbuffers))
44 const struct wined3d_gl_info *gl_info;
45 renderbuffer_entry_t *entry, *entry2;
46 struct wined3d_context *context;
48 context = context_acquire(This->resource.device, NULL);
49 gl_info = context->gl_info;
51 ENTER_GL();
53 if (This->texture_name)
55 TRACE("Deleting texture %u.\n", This->texture_name);
56 glDeleteTextures(1, &This->texture_name);
59 if (This->flags & SFLAG_PBO)
61 TRACE("Deleting PBO %u.\n", This->pbo);
62 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
65 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry)
67 TRACE("Deleting renderbuffer %u.\n", entry->id);
68 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
69 HeapFree(GetProcessHeap(), 0, entry);
72 LEAVE_GL();
74 context_release(context);
77 if (This->flags & SFLAG_DIBSECTION)
79 /* Release the DC. */
80 SelectObject(This->hDC, This->dib.holdbitmap);
81 DeleteDC(This->hDC);
82 /* Release the DIB section. */
83 DeleteObject(This->dib.DIBsection);
84 This->dib.bitmap_data = NULL;
85 This->resource.allocatedMemory = NULL;
88 if (This->flags & SFLAG_USERPTR) IWineD3DSurface_SetMem((IWineD3DSurface *)This, NULL);
89 if (This->overlay_dest) list_remove(&This->overlay_entry);
91 HeapFree(GetProcessHeap(), 0, This->palette9);
93 resource_cleanup(&This->resource);
96 void surface_set_container(IWineD3DSurfaceImpl *surface, enum wined3d_container_type type, void *container)
98 TRACE("surface %p, container %p.\n", surface, container);
100 if (!container && type != WINED3D_CONTAINER_NONE)
101 ERR("Setting NULL container of type %#x.\n", type);
103 if (type == WINED3D_CONTAINER_SWAPCHAIN)
105 surface->get_drawable_size = get_drawable_size_swapchain;
107 else
109 switch (wined3d_settings.offscreen_rendering_mode)
111 case ORM_FBO:
112 surface->get_drawable_size = get_drawable_size_fbo;
113 break;
115 case ORM_BACKBUFFER:
116 surface->get_drawable_size = get_drawable_size_backbuffer;
117 break;
119 default:
120 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
121 return;
125 surface->container.type = type;
126 surface->container.u.base = container;
129 struct blt_info
131 GLenum binding;
132 GLenum bind_target;
133 enum tex_types tex_type;
134 GLfloat coords[4][3];
137 struct float_rect
139 float l;
140 float t;
141 float r;
142 float b;
145 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
147 f->l = ((r->left * 2.0f) / w) - 1.0f;
148 f->t = ((r->top * 2.0f) / h) - 1.0f;
149 f->r = ((r->right * 2.0f) / w) - 1.0f;
150 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
153 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
155 GLfloat (*coords)[3] = info->coords;
156 struct float_rect f;
158 switch (target)
160 default:
161 FIXME("Unsupported texture target %#x\n", target);
162 /* Fall back to GL_TEXTURE_2D */
163 case GL_TEXTURE_2D:
164 info->binding = GL_TEXTURE_BINDING_2D;
165 info->bind_target = GL_TEXTURE_2D;
166 info->tex_type = tex_2d;
167 coords[0][0] = (float)rect->left / w;
168 coords[0][1] = (float)rect->top / h;
169 coords[0][2] = 0.0f;
171 coords[1][0] = (float)rect->right / w;
172 coords[1][1] = (float)rect->top / h;
173 coords[1][2] = 0.0f;
175 coords[2][0] = (float)rect->left / w;
176 coords[2][1] = (float)rect->bottom / h;
177 coords[2][2] = 0.0f;
179 coords[3][0] = (float)rect->right / w;
180 coords[3][1] = (float)rect->bottom / h;
181 coords[3][2] = 0.0f;
182 break;
184 case GL_TEXTURE_RECTANGLE_ARB:
185 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
186 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
187 info->tex_type = tex_rect;
188 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
189 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
190 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
191 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
192 break;
194 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
195 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
196 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
197 info->tex_type = tex_cube;
198 cube_coords_float(rect, w, h, &f);
200 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
201 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
202 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
203 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
204 break;
206 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
207 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
208 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
209 info->tex_type = tex_cube;
210 cube_coords_float(rect, w, h, &f);
212 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
213 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
214 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
215 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
216 break;
218 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
219 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
220 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
221 info->tex_type = tex_cube;
222 cube_coords_float(rect, w, h, &f);
224 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
225 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
226 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
227 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
228 break;
230 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
231 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
232 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
233 info->tex_type = tex_cube;
234 cube_coords_float(rect, w, h, &f);
236 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
237 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
238 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
239 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
240 break;
242 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
243 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
244 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
245 info->tex_type = tex_cube;
246 cube_coords_float(rect, w, h, &f);
248 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
249 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
250 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
251 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
252 break;
254 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
255 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
256 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
257 info->tex_type = tex_cube;
258 cube_coords_float(rect, w, h, &f);
260 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
261 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
262 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
263 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
264 break;
268 static inline void surface_get_rect(IWineD3DSurfaceImpl *This, const RECT *rect_in, RECT *rect_out)
270 if (rect_in)
271 *rect_out = *rect_in;
272 else
274 rect_out->left = 0;
275 rect_out->top = 0;
276 rect_out->right = This->resource.width;
277 rect_out->bottom = This->resource.height;
281 /* GL locking and context activation is done by the caller */
282 void draw_textured_quad(IWineD3DSurfaceImpl *src_surface, const RECT *src_rect, const RECT *dst_rect, WINED3DTEXTUREFILTERTYPE Filter)
284 struct blt_info info;
286 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
288 glEnable(info.bind_target);
289 checkGLcall("glEnable(bind_target)");
291 /* Bind the texture */
292 glBindTexture(info.bind_target, src_surface->texture_name);
293 checkGLcall("glBindTexture");
295 /* Filtering for StretchRect */
296 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
297 wined3d_gl_mag_filter(magLookup, Filter));
298 checkGLcall("glTexParameteri");
299 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
300 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
301 checkGLcall("glTexParameteri");
302 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
303 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
304 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
305 checkGLcall("glTexEnvi");
307 /* Draw a quad */
308 glBegin(GL_TRIANGLE_STRIP);
309 glTexCoord3fv(info.coords[0]);
310 glVertex2i(dst_rect->left, dst_rect->top);
312 glTexCoord3fv(info.coords[1]);
313 glVertex2i(dst_rect->right, dst_rect->top);
315 glTexCoord3fv(info.coords[2]);
316 glVertex2i(dst_rect->left, dst_rect->bottom);
318 glTexCoord3fv(info.coords[3]);
319 glVertex2i(dst_rect->right, dst_rect->bottom);
320 glEnd();
322 /* Unbind the texture */
323 glBindTexture(info.bind_target, 0);
324 checkGLcall("glBindTexture(info->bind_target, 0)");
326 /* We changed the filtering settings on the texture. Inform the
327 * container about this to get the filters reset properly next draw. */
328 if (src_surface->container.type == WINED3D_CONTAINER_TEXTURE)
330 struct wined3d_texture *texture = src_surface->container.u.texture;
331 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
332 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
333 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
337 static HRESULT surface_create_dib_section(IWineD3DSurfaceImpl *surface)
339 const struct wined3d_format *format = surface->resource.format;
340 SYSTEM_INFO sysInfo;
341 BITMAPINFO *b_info;
342 int extraline = 0;
343 DWORD *masks;
344 UINT usage;
345 HDC dc;
347 TRACE("surface %p.\n", surface);
349 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
351 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
352 return WINED3DERR_INVALIDCALL;
355 switch (format->byte_count)
357 case 2:
358 case 4:
359 /* Allocate extra space to store the RGB bit masks. */
360 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
361 break;
363 case 3:
364 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
365 break;
367 default:
368 /* Allocate extra space for a palette. */
369 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
370 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
371 break;
374 if (!b_info)
375 return E_OUTOFMEMORY;
377 /* Some applications access the surface in via DWORDs, and do not take
378 * the necessary care at the end of the surface. So we need at least
379 * 4 extra bytes at the end of the surface. Check against the page size,
380 * if the last page used for the surface has at least 4 spare bytes we're
381 * safe, otherwise add an extra line to the DIB section. */
382 GetSystemInfo(&sysInfo);
383 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
385 extraline = 1;
386 TRACE("Adding an extra line to the DIB section.\n");
389 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
390 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
391 b_info->bmiHeader.biWidth = IWineD3DSurface_GetPitch((IWineD3DSurface *)surface) / format->byte_count;
392 b_info->bmiHeader.biHeight = -surface->resource.height - extraline;
393 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
394 * IWineD3DSurface_GetPitch((IWineD3DSurface *)surface);
395 b_info->bmiHeader.biPlanes = 1;
396 b_info->bmiHeader.biBitCount = format->byte_count * 8;
398 b_info->bmiHeader.biXPelsPerMeter = 0;
399 b_info->bmiHeader.biYPelsPerMeter = 0;
400 b_info->bmiHeader.biClrUsed = 0;
401 b_info->bmiHeader.biClrImportant = 0;
403 /* Get the bit masks */
404 masks = (DWORD *)b_info->bmiColors;
405 switch (surface->resource.format->id)
407 case WINED3DFMT_B8G8R8_UNORM:
408 usage = DIB_RGB_COLORS;
409 b_info->bmiHeader.biCompression = BI_RGB;
410 break;
412 case WINED3DFMT_B5G5R5X1_UNORM:
413 case WINED3DFMT_B5G5R5A1_UNORM:
414 case WINED3DFMT_B4G4R4A4_UNORM:
415 case WINED3DFMT_B4G4R4X4_UNORM:
416 case WINED3DFMT_B2G3R3_UNORM:
417 case WINED3DFMT_B2G3R3A8_UNORM:
418 case WINED3DFMT_R10G10B10A2_UNORM:
419 case WINED3DFMT_R8G8B8A8_UNORM:
420 case WINED3DFMT_R8G8B8X8_UNORM:
421 case WINED3DFMT_B10G10R10A2_UNORM:
422 case WINED3DFMT_B5G6R5_UNORM:
423 case WINED3DFMT_R16G16B16A16_UNORM:
424 usage = 0;
425 b_info->bmiHeader.biCompression = BI_BITFIELDS;
426 masks[0] = format->red_mask;
427 masks[1] = format->green_mask;
428 masks[2] = format->blue_mask;
429 break;
431 default:
432 /* Don't know palette */
433 b_info->bmiHeader.biCompression = BI_RGB;
434 usage = 0;
435 break;
438 if (!(dc = GetDC(0)))
440 HeapFree(GetProcessHeap(), 0, b_info);
441 return HRESULT_FROM_WIN32(GetLastError());
444 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
445 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
446 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
447 surface->dib.DIBsection = CreateDIBSection(dc, b_info, usage, &surface->dib.bitmap_data, 0, 0);
448 ReleaseDC(0, dc);
450 if (!surface->dib.DIBsection)
452 ERR("Failed to create DIB section.\n");
453 HeapFree(GetProcessHeap(), 0, b_info);
454 return HRESULT_FROM_WIN32(GetLastError());
457 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
458 /* Copy the existing surface to the dib section. */
459 if (surface->resource.allocatedMemory)
461 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
462 surface->resource.height * IWineD3DSurface_GetPitch((IWineD3DSurface *)surface));
464 else
466 /* This is to make maps read the GL texture although memory is allocated. */
467 surface->flags &= ~SFLAG_INSYSMEM;
469 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
471 HeapFree(GetProcessHeap(), 0, b_info);
473 /* Now allocate a DC. */
474 surface->hDC = CreateCompatibleDC(0);
475 surface->dib.holdbitmap = SelectObject(surface->hDC, surface->dib.DIBsection);
476 TRACE("Using wined3d palette %p.\n", surface->palette);
477 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
479 surface->flags |= SFLAG_DIBSECTION;
481 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
482 surface->resource.heapMemory = NULL;
484 return WINED3D_OK;
487 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *surface)
489 IWineD3DDeviceImpl *device = surface->resource.device;
490 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
492 TRACE("surface %p.\n", surface);
494 /* Performance optimization: Count how often a surface is locked, if it is
495 * locked regularly do not throw away the system memory copy. This avoids
496 * the need to download the surface from OpenGL all the time. The surface
497 * is still downloaded if the OpenGL texture is changed. */
498 if (!(surface->flags & SFLAG_DYNLOCK))
500 if (++surface->lockCount > MAXLOCKCOUNT)
502 TRACE("Surface is locked regularly, not freeing the system memory copy any more.\n");
503 surface->flags |= SFLAG_DYNLOCK;
507 /* Create a PBO for dynamically locked surfaces but don't do it for
508 * converted or NPOT surfaces. Also don't create a PBO for systemmem
509 * surfaces. */
510 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (surface->flags & SFLAG_DYNLOCK)
511 && !(surface->flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
512 && (surface->resource.pool != WINED3DPOOL_SYSTEMMEM))
514 struct wined3d_context *context;
515 GLenum error;
517 context = context_acquire(device, NULL);
518 ENTER_GL();
520 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
521 error = glGetError();
522 if (!surface->pbo || error != GL_NO_ERROR)
523 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
525 TRACE("Binding PBO %u.\n", surface->pbo);
527 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
528 checkGLcall("glBindBufferARB");
530 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
531 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
532 checkGLcall("glBufferDataARB");
534 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
535 checkGLcall("glBindBufferARB");
537 /* We don't need the system memory anymore and we can't even use it for PBOs. */
538 if (!(surface->flags & SFLAG_CLIENT))
540 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
541 surface->resource.heapMemory = NULL;
543 surface->resource.allocatedMemory = NULL;
544 surface->flags |= SFLAG_PBO;
545 LEAVE_GL();
546 context_release(context);
548 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
550 /* Whatever surface we have, make sure that there is memory allocated
551 * for the downloaded copy, or a PBO to map. */
552 if (!surface->resource.heapMemory)
553 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
555 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
556 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
558 if (surface->flags & SFLAG_INSYSMEM)
559 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
563 static void surface_evict_sysmem(IWineD3DSurfaceImpl *surface)
565 if (surface->flags & SFLAG_DONOTFREE)
566 return;
568 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
569 surface->resource.allocatedMemory = NULL;
570 surface->resource.heapMemory = NULL;
571 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
574 /* Context activation is done by the caller. */
575 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *surface,
576 const struct wined3d_gl_info *gl_info, BOOL srgb)
578 IWineD3DDeviceImpl *device = surface->resource.device;
579 DWORD active_sampler;
580 GLint active_texture;
582 /* We don't need a specific texture unit, but after binding the texture
583 * the current unit is dirty. Read the unit back instead of switching to
584 * 0, this avoids messing around with the state manager's GL states. The
585 * current texture unit should always be a valid one.
587 * To be more specific, this is tricky because we can implicitly be
588 * called from sampler() in state.c. This means we can't touch anything
589 * other than whatever happens to be the currently active texture, or we
590 * would risk marking already applied sampler states dirty again.
592 * TODO: Track the current active texture per GL context instead of using
593 * glGet(). */
595 ENTER_GL();
596 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
597 LEAVE_GL();
598 active_sampler = device->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
600 if (active_sampler != WINED3D_UNMAPPED_STAGE)
602 IWineD3DDeviceImpl_MarkStateDirty(device, STATE_SAMPLER(active_sampler));
604 surface_bind(surface, gl_info, srgb);
607 static void surface_force_reload(IWineD3DSurfaceImpl *surface)
609 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
612 static void surface_release_client_storage(IWineD3DSurfaceImpl *surface)
614 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
616 ENTER_GL();
617 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
618 if (surface->texture_name)
620 surface_bind_and_dirtify(surface, context->gl_info, FALSE);
621 glTexImage2D(surface->texture_target, surface->texture_level,
622 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
624 if (surface->texture_name_srgb)
626 surface_bind_and_dirtify(surface, context->gl_info, TRUE);
627 glTexImage2D(surface->texture_target, surface->texture_level,
628 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
630 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
631 LEAVE_GL();
633 context_release(context);
635 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
636 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
637 surface_force_reload(surface);
640 static HRESULT surface_private_setup(struct IWineD3DSurfaceImpl *surface)
642 /* TODO: Check against the maximum texture sizes supported by the video card. */
643 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
644 unsigned int pow2Width, pow2Height;
646 TRACE("surface %p.\n", surface);
648 surface->texture_name = 0;
649 surface->texture_target = GL_TEXTURE_2D;
651 /* Non-power2 support */
652 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
654 pow2Width = surface->resource.width;
655 pow2Height = surface->resource.height;
657 else
659 /* Find the nearest pow2 match */
660 pow2Width = pow2Height = 1;
661 while (pow2Width < surface->resource.width)
662 pow2Width <<= 1;
663 while (pow2Height < surface->resource.height)
664 pow2Height <<= 1;
666 surface->pow2Width = pow2Width;
667 surface->pow2Height = pow2Height;
669 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
671 /* TODO: Add support for non power two compressed textures. */
672 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
674 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
675 surface, surface->resource.width, surface->resource.height);
676 return WINED3DERR_NOTAVAILABLE;
680 if (pow2Width != surface->resource.width
681 || pow2Height != surface->resource.height)
683 surface->flags |= SFLAG_NONPOW2;
686 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
687 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
689 /* One of three options:
690 * 1: Do the same as we do with NPOT and scale the texture, (any
691 * texture ops would require the texture to be scaled which is
692 * potentially slow)
693 * 2: Set the texture to the maximum size (bad idea).
694 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
695 * 4: Create the surface, but allow it to be used only for DirectDraw
696 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
697 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
698 * the render target. */
699 if (surface->resource.pool == WINED3DPOOL_DEFAULT || surface->resource.pool == WINED3DPOOL_MANAGED)
701 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
702 return WINED3DERR_NOTAVAILABLE;
705 /* We should never use this surface in combination with OpenGL! */
706 TRACE("Creating an oversized surface: %ux%u.\n",
707 surface->pow2Width, surface->pow2Height);
709 else
711 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
712 * and EXT_PALETTED_TEXTURE is used in combination with texture
713 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
714 * EXT_PALETTED_TEXTURE doesn't work in combination with
715 * ARB_TEXTURE_RECTANGLE. */
716 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
717 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
718 && gl_info->supported[EXT_PALETTED_TEXTURE]
719 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
721 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
722 surface->pow2Width = surface->resource.width;
723 surface->pow2Height = surface->resource.height;
724 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
728 switch (wined3d_settings.offscreen_rendering_mode)
730 case ORM_FBO:
731 surface->get_drawable_size = get_drawable_size_fbo;
732 break;
734 case ORM_BACKBUFFER:
735 surface->get_drawable_size = get_drawable_size_backbuffer;
736 break;
738 default:
739 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
740 return WINED3DERR_INVALIDCALL;
743 surface->flags |= SFLAG_INSYSMEM;
745 return WINED3D_OK;
748 static void surface_realize_palette(IWineD3DSurfaceImpl *surface)
750 struct wined3d_palette *palette = surface->palette;
752 TRACE("surface %p.\n", surface);
754 if (!palette) return;
756 if (surface->resource.format->id == WINED3DFMT_P8_UINT
757 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
759 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
761 /* Make sure the texture is up to date. This call doesn't do
762 * anything if the texture is already up to date. */
763 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
765 /* We want to force a palette refresh, so mark the drawable as not being up to date */
766 if (!surface_is_offscreen(surface))
767 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
769 else
771 if (!(surface->flags & SFLAG_INSYSMEM))
773 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
774 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
776 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
780 if (surface->flags & SFLAG_DIBSECTION)
782 RGBQUAD col[256];
783 unsigned int i;
785 TRACE("Updating the DC's palette.\n");
787 for (i = 0; i < 256; ++i)
789 col[i].rgbRed = palette->palents[i].peRed;
790 col[i].rgbGreen = palette->palents[i].peGreen;
791 col[i].rgbBlue = palette->palents[i].peBlue;
792 col[i].rgbReserved = 0;
794 SetDIBColorTable(surface->hDC, 0, 256, col);
797 /* Propagate the changes to the drawable when we have a palette. */
798 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
799 surface_load_location(surface, SFLAG_INDRAWABLE, NULL);
802 static HRESULT surface_draw_overlay(IWineD3DSurfaceImpl *surface)
804 HRESULT hr;
806 /* If there's no destination surface there is nothing to do. */
807 if (!surface->overlay_dest)
808 return WINED3D_OK;
810 /* Blt calls ModifyLocation on the dest surface, which in turn calls
811 * DrawOverlay to update the overlay. Prevent an endless recursion. */
812 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
813 return WINED3D_OK;
815 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
816 hr = IWineD3DSurface_Blt((IWineD3DSurface *)surface->overlay_dest,
817 &surface->overlay_destrect, (IWineD3DSurface *)surface, &surface->overlay_srcrect,
818 WINEDDBLT_WAIT, NULL, WINED3DTEXF_LINEAR);
819 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
821 return hr;
824 static void surface_preload(IWineD3DSurfaceImpl *surface)
826 TRACE("surface %p.\n", surface);
828 surface_internal_preload(surface, SRGB_ANY);
831 static void surface_map(IWineD3DSurfaceImpl *surface, const RECT *rect, DWORD flags)
833 IWineD3DDeviceImpl *device = surface->resource.device;
834 const RECT *pass_rect = rect;
836 TRACE("surface %p, rect %s, flags %#x.\n",
837 surface, wine_dbgstr_rect(rect), flags);
839 if (flags & WINED3DLOCK_DISCARD)
841 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
842 surface_prepare_system_memory(surface);
843 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
845 else
847 /* surface_load_location() does not check if the rectangle specifies
848 * the full surface. Most callers don't need that, so do it here. */
849 if (rect && !rect->top && !rect->left
850 && rect->right == surface->resource.width
851 && rect->bottom == surface->resource.height)
852 pass_rect = NULL;
854 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
855 && ((surface->container.type == WINED3D_CONTAINER_SWAPCHAIN) || surface == device->render_targets[0])))
856 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
859 if (surface->flags & SFLAG_PBO)
861 const struct wined3d_gl_info *gl_info;
862 struct wined3d_context *context;
864 context = context_acquire(device, NULL);
865 gl_info = context->gl_info;
867 ENTER_GL();
868 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
869 checkGLcall("glBindBufferARB");
871 /* This shouldn't happen but could occur if some other function
872 * didn't handle the PBO properly. */
873 if (surface->resource.allocatedMemory)
874 ERR("The surface already has PBO memory allocated.\n");
876 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
877 checkGLcall("glMapBufferARB");
879 /* Make sure the PBO isn't set anymore in order not to break non-PBO
880 * calls. */
881 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
882 checkGLcall("glBindBufferARB");
884 LEAVE_GL();
885 context_release(context);
888 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
890 if (!rect)
891 surface_add_dirty_rect(surface, NULL);
892 else
894 WINED3DBOX b;
896 b.Left = rect->left;
897 b.Top = rect->top;
898 b.Right = rect->right;
899 b.Bottom = rect->bottom;
900 b.Front = 0;
901 b.Back = 1;
902 surface_add_dirty_rect(surface, &b);
907 static void surface_unmap(IWineD3DSurfaceImpl *surface)
909 IWineD3DDeviceImpl *device = surface->resource.device;
910 BOOL fullsurface;
912 TRACE("surface %p.\n", surface);
914 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
916 if (surface->flags & SFLAG_PBO)
918 const struct wined3d_gl_info *gl_info;
919 struct wined3d_context *context;
921 TRACE("Freeing PBO memory.\n");
923 context = context_acquire(device, NULL);
924 gl_info = context->gl_info;
926 ENTER_GL();
927 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
928 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
929 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
930 checkGLcall("glUnmapBufferARB");
931 LEAVE_GL();
932 context_release(context);
934 surface->resource.allocatedMemory = NULL;
937 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
939 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
941 TRACE("Not dirtified, nothing to do.\n");
942 goto done;
945 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
946 || (device->render_targets && surface == device->render_targets[0]))
948 if (wined3d_settings.rendertargetlock_mode == RTL_DISABLE)
950 static BOOL warned = FALSE;
951 if (!warned)
953 ERR("The application tries to write to the render target, but render target locking is disabled.\n");
954 warned = TRUE;
956 goto done;
959 if (!surface->dirtyRect.left && !surface->dirtyRect.top
960 && surface->dirtyRect.right == surface->resource.width
961 && surface->dirtyRect.bottom == surface->resource.height)
963 fullsurface = TRUE;
965 else
967 /* TODO: Proper partial rectangle tracking. */
968 fullsurface = FALSE;
969 surface->flags |= SFLAG_INSYSMEM;
972 surface_load_location(surface, SFLAG_INDRAWABLE, fullsurface ? NULL : &surface->dirtyRect);
974 /* Partial rectangle tracking is not commonly implemented, it is only
975 * done for render targets. INSYSMEM was set before to tell
976 * surface_load_location() where to read the rectangle from.
977 * Indrawable is set because all modifications from the partial
978 * sysmem copy are written back to the drawable, thus the surface is
979 * merged again in the drawable. The sysmem copy is not fully up to
980 * date because only a subrectangle was read in Map(). */
981 if (!fullsurface)
983 surface_modify_location(surface, SFLAG_INDRAWABLE, TRUE);
984 surface_evict_sysmem(surface);
987 surface->dirtyRect.left = surface->resource.width;
988 surface->dirtyRect.top = surface->resource.height;
989 surface->dirtyRect.right = 0;
990 surface->dirtyRect.bottom = 0;
992 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
994 FIXME("Depth / stencil buffer locking is not implemented.\n");
997 done:
998 /* Overlays have to be redrawn manually after changes with the GL implementation */
999 if (surface->overlay_dest)
1000 surface->surface_ops->surface_draw_overlay(surface);
1003 static HRESULT surface_getdc(IWineD3DSurfaceImpl *surface)
1005 WINED3DLOCKED_RECT lock;
1006 HRESULT hr;
1008 TRACE("surface %p.\n", surface);
1010 /* Create a DIB section if there isn't a dc yet. */
1011 if (!surface->hDC)
1013 if (surface->flags & SFLAG_CLIENT)
1015 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1016 surface_release_client_storage(surface);
1018 hr = surface_create_dib_section(surface);
1019 if (FAILED(hr))
1020 return WINED3DERR_INVALIDCALL;
1022 /* Use the DIB section from now on if we are not using a PBO. */
1023 if (!(surface->flags & SFLAG_PBO))
1024 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1027 /* Map the surface. */
1028 hr = IWineD3DSurface_Map((IWineD3DSurface *)surface, &lock, NULL, 0);
1029 if (FAILED(hr))
1030 ERR("Map failed, hr %#x.\n", hr);
1032 /* Sync the DIB with the PBO. This can't be done earlier because Map()
1033 * activates the allocatedMemory. */
1034 if (surface->flags & SFLAG_PBO)
1035 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->dib.bitmap_size);
1037 return hr;
1040 static HRESULT surface_flip(IWineD3DSurfaceImpl *surface, IWineD3DSurfaceImpl *override)
1042 TRACE("surface %p, override %p.\n", surface, override);
1044 /* Flipping is only supported on render targets and overlays. */
1045 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
1047 WARN("Tried to flip a non-render target, non-overlay surface.\n");
1048 return WINEDDERR_NOTFLIPPABLE;
1051 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1053 flip_surface(surface, override);
1055 /* Update the overlay if it is visible */
1056 if (surface->overlay_dest)
1057 return surface->surface_ops->surface_draw_overlay(surface);
1058 else
1059 return WINED3D_OK;
1062 return WINED3D_OK;
1065 static HRESULT surface_set_mem(IWineD3DSurfaceImpl *surface, void *mem)
1067 TRACE("surface %p, mem %p.\n", surface, mem);
1069 if (mem && mem != surface->resource.allocatedMemory)
1071 void *release = NULL;
1073 /* Do I have to copy the old surface content? */
1074 if (surface->flags & SFLAG_DIBSECTION)
1076 SelectObject(surface->hDC, surface->dib.holdbitmap);
1077 DeleteDC(surface->hDC);
1078 /* Release the DIB section. */
1079 DeleteObject(surface->dib.DIBsection);
1080 surface->dib.bitmap_data = NULL;
1081 surface->resource.allocatedMemory = NULL;
1082 surface->hDC = NULL;
1083 surface->flags &= ~SFLAG_DIBSECTION;
1085 else if (!(surface->flags & SFLAG_USERPTR))
1087 release = surface->resource.heapMemory;
1088 surface->resource.heapMemory = NULL;
1090 surface->resource.allocatedMemory = mem;
1091 surface->flags |= SFLAG_USERPTR;
1093 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
1094 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1096 /* For client textures OpenGL has to be notified. */
1097 if (surface->flags & SFLAG_CLIENT)
1098 surface_release_client_storage(surface);
1100 /* Now free the old memory if any. */
1101 HeapFree(GetProcessHeap(), 0, release);
1103 else if (surface->flags & SFLAG_USERPTR)
1105 /* Map and GetDC will re-create the dib section and allocated memory. */
1106 surface->resource.allocatedMemory = NULL;
1107 /* HeapMemory should be NULL already. */
1108 if (surface->resource.heapMemory)
1109 ERR("User pointer surface has heap memory allocated.\n");
1110 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
1112 if (surface->flags & SFLAG_CLIENT)
1113 surface_release_client_storage(surface);
1115 surface_prepare_system_memory(surface);
1116 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1119 return WINED3D_OK;
1122 /* Context activation is done by the caller. */
1123 static void surface_remove_pbo(IWineD3DSurfaceImpl *surface, const struct wined3d_gl_info *gl_info)
1125 if (!surface->resource.heapMemory)
1127 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1128 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1129 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1132 ENTER_GL();
1133 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1134 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1135 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1136 surface->resource.size, surface->resource.allocatedMemory));
1137 checkGLcall("glGetBufferSubDataARB");
1138 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1139 checkGLcall("glDeleteBuffersARB");
1140 LEAVE_GL();
1142 surface->pbo = 0;
1143 surface->flags &= ~SFLAG_PBO;
1146 /* Do not call while under the GL lock. */
1147 static void surface_unload(struct wined3d_resource *resource)
1149 IWineD3DSurfaceImpl *surface = surface_from_resource(resource);
1150 IWineD3DDeviceImpl *device = resource->device;
1151 const struct wined3d_gl_info *gl_info;
1152 renderbuffer_entry_t *entry, *entry2;
1153 struct wined3d_context *context;
1155 TRACE("surface %p.\n", surface);
1157 if (resource->pool == WINED3DPOOL_DEFAULT)
1159 /* Default pool resources are supposed to be destroyed before Reset is called.
1160 * Implicit resources stay however. So this means we have an implicit render target
1161 * or depth stencil. The content may be destroyed, but we still have to tear down
1162 * opengl resources, so we cannot leave early.
1164 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1165 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1166 * or the depth stencil into an FBO the texture or render buffer will be removed
1167 * and all flags get lost
1169 surface_init_sysmem(surface);
1171 else
1173 /* Load the surface into system memory */
1174 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1175 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
1177 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1178 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1179 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1181 context = context_acquire(device, NULL);
1182 gl_info = context->gl_info;
1184 /* Destroy PBOs, but load them into real sysmem before */
1185 if (surface->flags & SFLAG_PBO)
1186 surface_remove_pbo(surface, gl_info);
1188 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1189 * all application-created targets the application has to release the surface
1190 * before calling _Reset
1192 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, renderbuffer_entry_t, entry)
1194 ENTER_GL();
1195 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1196 LEAVE_GL();
1197 list_remove(&entry->entry);
1198 HeapFree(GetProcessHeap(), 0, entry);
1200 list_init(&surface->renderbuffers);
1201 surface->current_renderbuffer = NULL;
1203 /* If we're in a texture, the texture name belongs to the texture.
1204 * Otherwise, destroy it. */
1205 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1207 ENTER_GL();
1208 glDeleteTextures(1, &surface->texture_name);
1209 surface->texture_name = 0;
1210 glDeleteTextures(1, &surface->texture_name_srgb);
1211 surface->texture_name_srgb = 0;
1212 LEAVE_GL();
1215 context_release(context);
1217 resource_unload(resource);
1220 static const struct wined3d_resource_ops surface_resource_ops =
1222 surface_unload,
1225 static const struct wined3d_surface_ops surface_ops =
1227 surface_private_setup,
1228 surface_cleanup,
1229 surface_realize_palette,
1230 surface_draw_overlay,
1231 surface_preload,
1232 surface_map,
1233 surface_unmap,
1234 surface_getdc,
1235 surface_flip,
1236 surface_set_mem,
1239 /*****************************************************************************
1240 * Initializes the GDI surface, aka creates the DIB section we render to
1241 * The DIB section creation is done by calling GetDC, which will create the
1242 * section and releasing the dc to allow the app to use it. The dib section
1243 * will stay until the surface is released
1245 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1246 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1247 * avoid confusion in the shared surface code.
1249 * Returns:
1250 * WINED3D_OK on success
1251 * The return values of called methods on failure
1253 *****************************************************************************/
1254 static HRESULT gdi_surface_private_setup(IWineD3DSurfaceImpl *surface)
1256 HRESULT hr;
1258 TRACE("surface %p.\n", surface);
1260 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1262 ERR("Overlays not yet supported by GDI surfaces.\n");
1263 return WINED3DERR_INVALIDCALL;
1266 /* Sysmem textures have memory already allocated - release it,
1267 * this avoids an unnecessary memcpy. */
1268 hr = surface_create_dib_section(surface);
1269 if (SUCCEEDED(hr))
1271 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1272 surface->resource.heapMemory = NULL;
1273 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1276 /* We don't mind the nonpow2 stuff in GDI. */
1277 surface->pow2Width = surface->resource.width;
1278 surface->pow2Height = surface->resource.height;
1280 return WINED3D_OK;
1283 static void surface_gdi_cleanup(IWineD3DSurfaceImpl *surface)
1285 TRACE("surface %p.\n", surface);
1287 if (surface->flags & SFLAG_DIBSECTION)
1289 /* Release the DC. */
1290 SelectObject(surface->hDC, surface->dib.holdbitmap);
1291 DeleteDC(surface->hDC);
1292 /* Release the DIB section. */
1293 DeleteObject(surface->dib.DIBsection);
1294 surface->dib.bitmap_data = NULL;
1295 surface->resource.allocatedMemory = NULL;
1298 if (surface->flags & SFLAG_USERPTR)
1299 IWineD3DSurface_SetMem((IWineD3DSurface *)surface, NULL);
1300 if (surface->overlay_dest)
1301 list_remove(&surface->overlay_entry);
1303 HeapFree(GetProcessHeap(), 0, surface->palette9);
1305 resource_cleanup(&surface->resource);
1308 static void gdi_surface_realize_palette(IWineD3DSurfaceImpl *surface)
1310 struct wined3d_palette *palette = surface->palette;
1312 TRACE("surface %p.\n", surface);
1314 if (!palette) return;
1316 if (surface->flags & SFLAG_DIBSECTION)
1318 RGBQUAD col[256];
1319 unsigned int i;
1321 TRACE("Updating the DC's palette.\n");
1323 for (i = 0; i < 256; ++i)
1325 col[i].rgbRed = palette->palents[i].peRed;
1326 col[i].rgbGreen = palette->palents[i].peGreen;
1327 col[i].rgbBlue = palette->palents[i].peBlue;
1328 col[i].rgbReserved = 0;
1330 SetDIBColorTable(surface->hDC, 0, 256, col);
1333 /* Update the image because of the palette change. Some games like e.g.
1334 * Red Alert call SetEntries a lot to implement fading. */
1335 /* Tell the swapchain to update the screen. */
1336 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1338 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1339 if (surface == swapchain->front_buffer)
1341 x11_copy_to_screen(swapchain, NULL);
1346 static HRESULT gdi_surface_draw_overlay(IWineD3DSurfaceImpl *surface)
1348 FIXME("GDI surfaces can't draw overlays yet.\n");
1349 return E_FAIL;
1352 static void gdi_surface_preload(IWineD3DSurfaceImpl *surface)
1354 TRACE("surface %p.\n", surface);
1356 ERR("Preloading GDI surfaces is not supported.\n");
1359 static void gdi_surface_map(IWineD3DSurfaceImpl *surface, const RECT *rect, DWORD flags)
1361 TRACE("surface %p, rect %s, flags %#x.\n",
1362 surface, wine_dbgstr_rect(rect), flags);
1364 if (!surface->resource.allocatedMemory)
1366 /* This happens on gdi surfaces if the application set a user pointer
1367 * and resets it. Recreate the DIB section. */
1368 surface_create_dib_section(surface);
1369 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1373 static void gdi_surface_unmap(IWineD3DSurfaceImpl *surface)
1375 TRACE("surface %p.\n", surface);
1377 /* Tell the swapchain to update the screen. */
1378 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1380 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1381 if (surface == swapchain->front_buffer)
1383 x11_copy_to_screen(swapchain, &surface->lockedRect);
1387 memset(&surface->lockedRect, 0, sizeof(RECT));
1390 static HRESULT gdi_surface_getdc(IWineD3DSurfaceImpl *surface)
1392 WINED3DLOCKED_RECT lock;
1393 HRESULT hr;
1395 TRACE("surface %p.\n", surface);
1397 /* Should have a DIB section already. */
1398 if (!(surface->flags & SFLAG_DIBSECTION))
1400 WARN("DC not supported on this surface\n");
1401 return WINED3DERR_INVALIDCALL;
1404 /* Map the surface. */
1405 hr = IWineD3DSurface_Map((IWineD3DSurface *)surface, &lock, NULL, 0);
1406 if (FAILED(hr))
1407 ERR("Map failed, hr %#x.\n", hr);
1409 return hr;
1412 static HRESULT gdi_surface_flip(IWineD3DSurfaceImpl *surface, IWineD3DSurfaceImpl *override)
1414 TRACE("surface %p, override %p.\n", surface, override);
1416 return WINED3D_OK;
1419 static HRESULT gdi_surface_set_mem(IWineD3DSurfaceImpl *surface, void *mem)
1421 TRACE("surface %p, mem %p.\n", surface, mem);
1423 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
1424 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1426 ERR("Not supported on render targets.\n");
1427 return WINED3DERR_INVALIDCALL;
1430 if (mem && mem != surface->resource.allocatedMemory)
1432 void *release = NULL;
1434 /* Do I have to copy the old surface content? */
1435 if (surface->flags & SFLAG_DIBSECTION)
1437 SelectObject(surface->hDC, surface->dib.holdbitmap);
1438 DeleteDC(surface->hDC);
1439 /* Release the DIB section. */
1440 DeleteObject(surface->dib.DIBsection);
1441 surface->dib.bitmap_data = NULL;
1442 surface->resource.allocatedMemory = NULL;
1443 surface->hDC = NULL;
1444 surface->flags &= ~SFLAG_DIBSECTION;
1446 else if (!(surface->flags & SFLAG_USERPTR))
1448 release = surface->resource.allocatedMemory;
1450 surface->resource.allocatedMemory = mem;
1451 surface->flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
1453 /* Now free the old memory, if any. */
1454 HeapFree(GetProcessHeap(), 0, release);
1456 else if (surface->flags & SFLAG_USERPTR)
1458 /* Map() and GetDC() will re-create the dib section and allocated memory. */
1459 surface->resource.allocatedMemory = NULL;
1460 surface->flags &= ~SFLAG_USERPTR;
1463 return WINED3D_OK;
1466 static const struct wined3d_surface_ops gdi_surface_ops =
1468 gdi_surface_private_setup,
1469 surface_gdi_cleanup,
1470 gdi_surface_realize_palette,
1471 gdi_surface_draw_overlay,
1472 gdi_surface_preload,
1473 gdi_surface_map,
1474 gdi_surface_unmap,
1475 gdi_surface_getdc,
1476 gdi_surface_flip,
1477 gdi_surface_set_mem,
1480 void surface_set_texture_name(IWineD3DSurfaceImpl *surface, GLuint new_name, BOOL srgb)
1482 GLuint *name;
1483 DWORD flag;
1485 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
1487 if(srgb)
1489 name = &surface->texture_name_srgb;
1490 flag = SFLAG_INSRGBTEX;
1492 else
1494 name = &surface->texture_name;
1495 flag = SFLAG_INTEXTURE;
1498 if (!*name && new_name)
1500 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
1501 * surface has no texture name yet. See if we can get rid of this. */
1502 if (surface->flags & flag)
1503 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
1504 surface_modify_location(surface, flag, FALSE);
1507 *name = new_name;
1508 surface_force_reload(surface);
1511 void surface_set_texture_target(IWineD3DSurfaceImpl *surface, GLenum target)
1513 TRACE("surface %p, target %#x.\n", surface, target);
1515 if (surface->texture_target != target)
1517 if (target == GL_TEXTURE_RECTANGLE_ARB)
1519 surface->flags &= ~SFLAG_NORMCOORD;
1521 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
1523 surface->flags |= SFLAG_NORMCOORD;
1526 surface->texture_target = target;
1527 surface_force_reload(surface);
1530 /* Context activation is done by the caller. */
1531 void surface_bind(IWineD3DSurfaceImpl *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
1533 TRACE("surface %p, gl_info %p, srgb %#x.\n", surface, gl_info, srgb);
1535 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
1537 struct wined3d_texture *texture = surface->container.u.texture;
1539 TRACE("Passing to container (%p).\n", texture);
1540 texture->texture_ops->texture_bind(texture, gl_info, srgb);
1542 else
1544 if (surface->texture_level)
1546 ERR("Standalone surface %p is non-zero texture level %u.\n",
1547 surface, surface->texture_level);
1550 if (srgb)
1551 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
1553 ENTER_GL();
1555 if (!surface->texture_name)
1557 glGenTextures(1, &surface->texture_name);
1558 checkGLcall("glGenTextures");
1560 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
1562 glBindTexture(surface->texture_target, surface->texture_name);
1563 checkGLcall("glBindTexture");
1564 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1565 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1566 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
1567 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1568 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1569 checkGLcall("glTexParameteri");
1571 else
1573 glBindTexture(surface->texture_target, surface->texture_name);
1574 checkGLcall("glBindTexture");
1577 LEAVE_GL();
1581 /* This function checks if the primary render target uses the 8bit paletted format. */
1582 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
1584 if (device->render_targets && device->render_targets[0])
1586 IWineD3DSurfaceImpl *render_target = device->render_targets[0];
1587 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1588 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1589 return TRUE;
1591 return FALSE;
1594 /* This call just downloads data, the caller is responsible for binding the
1595 * correct texture. */
1596 /* Context activation is done by the caller. */
1597 static void surface_download_data(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info)
1599 const struct wined3d_format *format = This->resource.format;
1601 /* Only support read back of converted P8 surfaces */
1602 if (This->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1604 FIXME("Readback conversion not supported for format %s.\n", debug_d3dformat(format->id));
1605 return;
1608 ENTER_GL();
1610 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1612 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
1613 This, This->texture_level, format->glFormat, format->glType,
1614 This->resource.allocatedMemory);
1616 if (This->flags & SFLAG_PBO)
1618 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
1619 checkGLcall("glBindBufferARB");
1620 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target, This->texture_level, NULL));
1621 checkGLcall("glGetCompressedTexImageARB");
1622 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1623 checkGLcall("glBindBufferARB");
1625 else
1627 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target,
1628 This->texture_level, This->resource.allocatedMemory));
1629 checkGLcall("glGetCompressedTexImageARB");
1632 LEAVE_GL();
1633 } else {
1634 void *mem;
1635 GLenum gl_format = format->glFormat;
1636 GLenum gl_type = format->glType;
1637 int src_pitch = 0;
1638 int dst_pitch = 0;
1640 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
1641 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(This->resource.device))
1643 gl_format = GL_ALPHA;
1644 gl_type = GL_UNSIGNED_BYTE;
1647 if (This->flags & SFLAG_NONPOW2)
1649 unsigned char alignment = This->resource.device->surface_alignment;
1650 src_pitch = format->byte_count * This->pow2Width;
1651 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
1652 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1653 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
1654 } else {
1655 mem = This->resource.allocatedMemory;
1658 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1659 This, This->texture_level, gl_format, gl_type, mem);
1661 if (This->flags & SFLAG_PBO)
1663 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
1664 checkGLcall("glBindBufferARB");
1666 glGetTexImage(This->texture_target, This->texture_level, gl_format, gl_type, NULL);
1667 checkGLcall("glGetTexImage");
1669 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1670 checkGLcall("glBindBufferARB");
1671 } else {
1672 glGetTexImage(This->texture_target, This->texture_level, gl_format, gl_type, mem);
1673 checkGLcall("glGetTexImage");
1675 LEAVE_GL();
1677 if (This->flags & SFLAG_NONPOW2)
1679 const BYTE *src_data;
1680 BYTE *dst_data;
1681 UINT y;
1683 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1684 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1685 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1687 * We're doing this...
1689 * instead of boxing the texture :
1690 * |<-texture width ->| -->pow2width| /\
1691 * |111111111111111111| | |
1692 * |222 Texture 222222| boxed empty | texture height
1693 * |3333 Data 33333333| | |
1694 * |444444444444444444| | \/
1695 * ----------------------------------- |
1696 * | boxed empty | boxed empty | pow2height
1697 * | | | \/
1698 * -----------------------------------
1701 * we're repacking the data to the expected texture width
1703 * |<-texture width ->| -->pow2width| /\
1704 * |111111111111111111222222222222222| |
1705 * |222333333333333333333444444444444| texture height
1706 * |444444 | |
1707 * | | \/
1708 * | | |
1709 * | empty | pow2height
1710 * | | \/
1711 * -----------------------------------
1713 * == is the same as
1715 * |<-texture width ->| /\
1716 * |111111111111111111|
1717 * |222222222222222222|texture height
1718 * |333333333333333333|
1719 * |444444444444444444| \/
1720 * --------------------
1722 * this also means that any references to allocatedMemory should work with the data as if were a
1723 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
1725 * internally the texture is still stored in a boxed format so any references to textureName will
1726 * get a boxed texture with width pow2width and not a texture of width resource.width.
1728 * Performance should not be an issue, because applications normally do not lock the surfaces when
1729 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
1730 * and doesn't have to be re-read.
1732 src_data = mem;
1733 dst_data = This->resource.allocatedMemory;
1734 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
1735 for (y = 1; y < This->resource.height; ++y)
1737 /* skip the first row */
1738 src_data += src_pitch;
1739 dst_data += dst_pitch;
1740 memcpy(dst_data, src_data, dst_pitch);
1743 HeapFree(GetProcessHeap(), 0, mem);
1747 /* Surface has now been downloaded */
1748 This->flags |= SFLAG_INSYSMEM;
1751 /* This call just uploads data, the caller is responsible for binding the
1752 * correct texture. */
1753 /* Context activation is done by the caller. */
1754 static void surface_upload_data(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info,
1755 const struct wined3d_format *format, BOOL srgb, const GLvoid *data)
1757 GLsizei width = This->resource.width;
1758 GLsizei height = This->resource.height;
1759 GLenum internal;
1761 if (srgb)
1763 internal = format->glGammaInternal;
1765 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(This))
1767 internal = format->rtInternal;
1769 else
1771 internal = format->glInternal;
1774 TRACE("This %p, internal %#x, width %d, height %d, format %#x, type %#x, data %p.\n",
1775 This, internal, width, height, format->glFormat, format->glType, data);
1776 TRACE("target %#x, level %u, resource size %u.\n",
1777 This->texture_target, This->texture_level, This->resource.size);
1779 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
1781 ENTER_GL();
1783 if (This->flags & SFLAG_PBO)
1785 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1786 checkGLcall("glBindBufferARB");
1788 TRACE("(%p) pbo: %#x, data: %p.\n", This, This->pbo, data);
1789 data = NULL;
1792 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1794 TRACE("Calling glCompressedTexSubImage2DARB.\n");
1796 GL_EXTCALL(glCompressedTexSubImage2DARB(This->texture_target, This->texture_level,
1797 0, 0, width, height, internal, This->resource.size, data));
1798 checkGLcall("glCompressedTexSubImage2DARB");
1800 else
1802 TRACE("Calling glTexSubImage2D.\n");
1804 glTexSubImage2D(This->texture_target, This->texture_level,
1805 0, 0, width, height, format->glFormat, format->glType, data);
1806 checkGLcall("glTexSubImage2D");
1809 if (This->flags & SFLAG_PBO)
1811 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1812 checkGLcall("glBindBufferARB");
1815 LEAVE_GL();
1817 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1819 IWineD3DDeviceImpl *device = This->resource.device;
1820 unsigned int i;
1822 for (i = 0; i < device->context_count; ++i)
1824 context_surface_update(device->contexts[i], This);
1829 /* This call just allocates the texture, the caller is responsible for binding
1830 * the correct texture. */
1831 /* Context activation is done by the caller. */
1832 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info,
1833 const struct wined3d_format *format, BOOL srgb)
1835 BOOL enable_client_storage = FALSE;
1836 GLsizei width = This->pow2Width;
1837 GLsizei height = This->pow2Height;
1838 const BYTE *mem = NULL;
1839 GLenum internal;
1841 if (srgb)
1843 internal = format->glGammaInternal;
1845 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(This))
1847 internal = format->rtInternal;
1849 else
1851 internal = format->glInternal;
1854 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
1856 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",
1857 This, This->texture_target, This->texture_level, debug_d3dformat(format->id),
1858 internal, width, height, format->glFormat, format->glType);
1860 ENTER_GL();
1862 if (gl_info->supported[APPLE_CLIENT_STORAGE])
1864 if (This->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
1865 || !This->resource.allocatedMemory)
1867 /* In some cases we want to disable client storage.
1868 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
1869 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
1870 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
1871 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
1873 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
1874 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
1875 This->flags &= ~SFLAG_CLIENT;
1876 enable_client_storage = TRUE;
1878 else
1880 This->flags |= SFLAG_CLIENT;
1882 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
1883 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
1885 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1889 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
1891 GL_EXTCALL(glCompressedTexImage2DARB(This->texture_target, This->texture_level,
1892 internal, width, height, 0, This->resource.size, mem));
1893 checkGLcall("glCompressedTexImage2DARB");
1895 else
1897 glTexImage2D(This->texture_target, This->texture_level,
1898 internal, width, height, 0, format->glFormat, format->glType, mem);
1899 checkGLcall("glTexImage2D");
1902 if(enable_client_storage) {
1903 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
1904 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
1906 LEAVE_GL();
1909 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1910 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1911 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1912 /* GL locking is done by the caller */
1913 void surface_set_compatible_renderbuffer(IWineD3DSurfaceImpl *surface, IWineD3DSurfaceImpl *rt)
1915 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1916 renderbuffer_entry_t *entry;
1917 GLuint renderbuffer = 0;
1918 unsigned int src_width, src_height;
1919 unsigned int width, height;
1921 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1923 width = rt->pow2Width;
1924 height = rt->pow2Height;
1926 else
1928 width = surface->pow2Width;
1929 height = surface->pow2Height;
1932 src_width = surface->pow2Width;
1933 src_height = surface->pow2Height;
1935 /* A depth stencil smaller than the render target is not valid */
1936 if (width > src_width || height > src_height) return;
1938 /* Remove any renderbuffer set if the sizes match */
1939 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1940 || (width == src_width && height == src_height))
1942 surface->current_renderbuffer = NULL;
1943 return;
1946 /* Look if we've already got a renderbuffer of the correct dimensions */
1947 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, renderbuffer_entry_t, entry)
1949 if (entry->width == width && entry->height == height)
1951 renderbuffer = entry->id;
1952 surface->current_renderbuffer = entry;
1953 break;
1957 if (!renderbuffer)
1959 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1960 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1961 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1962 surface->resource.format->glInternal, width, height);
1964 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
1965 entry->width = width;
1966 entry->height = height;
1967 entry->id = renderbuffer;
1968 list_add_head(&surface->renderbuffers, &entry->entry);
1970 surface->current_renderbuffer = entry;
1973 checkGLcall("set_compatible_renderbuffer");
1976 GLenum surface_get_gl_buffer(IWineD3DSurfaceImpl *surface)
1978 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
1980 TRACE("surface %p.\n", surface);
1982 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
1984 ERR("Surface %p is not on a swapchain.\n", surface);
1985 return GL_NONE;
1988 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
1990 if (swapchain->render_to_fbo)
1992 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1993 return GL_COLOR_ATTACHMENT0;
1995 TRACE("Returning GL_BACK\n");
1996 return GL_BACK;
1998 else if (surface == swapchain->front_buffer)
2000 TRACE("Returning GL_FRONT\n");
2001 return GL_FRONT;
2004 FIXME("Higher back buffer, returning GL_BACK\n");
2005 return GL_BACK;
2008 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2009 void surface_add_dirty_rect(IWineD3DSurfaceImpl *surface, const WINED3DBOX *dirty_rect)
2011 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2013 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2014 /* No partial locking for textures yet. */
2015 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2017 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2018 if (dirty_rect)
2020 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->Left);
2021 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->Top);
2022 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->Right);
2023 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->Bottom);
2025 else
2027 surface->dirtyRect.left = 0;
2028 surface->dirtyRect.top = 0;
2029 surface->dirtyRect.right = surface->resource.width;
2030 surface->dirtyRect.bottom = surface->resource.height;
2033 /* if the container is a texture then mark it dirty. */
2034 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2036 TRACE("Passing to container.\n");
2037 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2041 static BOOL surface_convert_color_to_float(IWineD3DSurfaceImpl *surface, DWORD color, WINED3DCOLORVALUE *float_color)
2043 const struct wined3d_format *format = surface->resource.format;
2044 IWineD3DDeviceImpl *device = surface->resource.device;
2046 switch (format->id)
2048 case WINED3DFMT_P8_UINT:
2049 if (surface->palette)
2051 float_color->r = surface->palette->palents[color].peRed / 255.0f;
2052 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
2053 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
2055 else
2057 float_color->r = 0.0f;
2058 float_color->g = 0.0f;
2059 float_color->b = 0.0f;
2061 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
2062 break;
2064 case WINED3DFMT_B5G6R5_UNORM:
2065 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
2066 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
2067 float_color->b = (color & 0x1f) / 31.0f;
2068 float_color->a = 1.0f;
2069 break;
2071 case WINED3DFMT_B8G8R8_UNORM:
2072 case WINED3DFMT_B8G8R8X8_UNORM:
2073 float_color->r = D3DCOLOR_R(color);
2074 float_color->g = D3DCOLOR_G(color);
2075 float_color->b = D3DCOLOR_B(color);
2076 float_color->a = 1.0f;
2077 break;
2079 case WINED3DFMT_B8G8R8A8_UNORM:
2080 float_color->r = D3DCOLOR_R(color);
2081 float_color->g = D3DCOLOR_G(color);
2082 float_color->b = D3DCOLOR_B(color);
2083 float_color->a = D3DCOLOR_A(color);
2084 break;
2086 default:
2087 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
2088 return FALSE;
2091 return TRUE;
2094 static BOOL surface_convert_depth_to_float(IWineD3DSurfaceImpl *surface, DWORD depth, float *float_depth)
2096 const struct wined3d_format *format = surface->resource.format;
2098 switch (format->id)
2100 case WINED3DFMT_S1_UINT_D15_UNORM:
2101 *float_depth = depth / (float)0x00007fff;
2102 break;
2104 case WINED3DFMT_D16_UNORM:
2105 *float_depth = depth / (float)0x0000ffff;
2106 break;
2108 case WINED3DFMT_D24_UNORM_S8_UINT:
2109 case WINED3DFMT_X8D24_UNORM:
2110 *float_depth = depth / (float)0x00ffffff;
2111 break;
2113 case WINED3DFMT_D32_UNORM:
2114 *float_depth = depth / (float)0xffffffff;
2115 break;
2117 default:
2118 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
2119 return FALSE;
2122 return TRUE;
2125 HRESULT surface_load(IWineD3DSurfaceImpl *surface, BOOL srgb)
2127 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2129 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2131 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2133 ERR("Not supported on scratch surfaces.\n");
2134 return WINED3DERR_INVALIDCALL;
2137 if (!(surface->flags & flag))
2139 TRACE("Reloading because surface is dirty\n");
2141 /* Reload if either the texture and sysmem have different ideas about the
2142 * color key, or the actual key values changed. */
2143 else if (!(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2144 || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2145 && (surface->glCKey.dwColorSpaceLowValue != surface->SrcBltCKey.dwColorSpaceLowValue
2146 || surface->glCKey.dwColorSpaceHighValue != surface->SrcBltCKey.dwColorSpaceHighValue)))
2148 TRACE("Reloading because of color keying\n");
2149 /* To perform the color key conversion we need a sysmem copy of
2150 * the surface. Make sure we have it. */
2152 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2153 /* Make sure the texture is reloaded because of the color key change,
2154 * this kills performance though :( */
2155 /* TODO: This is not necessarily needed with hw palettized texture support. */
2156 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2158 else
2160 TRACE("surface is already in texture\n");
2161 return WINED3D_OK;
2164 /* No partial locking for textures yet. */
2165 surface_load_location(surface, flag, NULL);
2166 surface_evict_sysmem(surface);
2168 return WINED3D_OK;
2171 /* See also float_16_to_32() in wined3d_private.h */
2172 static inline unsigned short float_32_to_16(const float *in)
2174 int exp = 0;
2175 float tmp = fabs(*in);
2176 unsigned int mantissa;
2177 unsigned short ret;
2179 /* Deal with special numbers */
2180 if (*in == 0.0f)
2181 return 0x0000;
2182 if (isnan(*in))
2183 return 0x7c01;
2184 if (isinf(*in))
2185 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2187 if (tmp < powf(2, 10))
2191 tmp = tmp * 2.0f;
2192 exp--;
2193 } while (tmp < powf(2, 10));
2195 else if (tmp >= powf(2, 11))
2199 tmp /= 2.0f;
2200 exp++;
2201 } while (tmp >= powf(2, 11));
2204 mantissa = (unsigned int)tmp;
2205 if (tmp - mantissa >= 0.5f)
2206 ++mantissa; /* Round to nearest, away from zero. */
2208 exp += 10; /* Normalize the mantissa. */
2209 exp += 15; /* Exponent is encoded with excess 15. */
2211 if (exp > 30) /* too big */
2213 ret = 0x7c00; /* INF */
2215 else if (exp <= 0)
2217 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2218 while (exp <= 0)
2220 mantissa = mantissa >> 1;
2221 ++exp;
2223 ret = mantissa & 0x3ff;
2225 else
2227 ret = (exp << 10) | (mantissa & 0x3ff);
2230 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2231 return ret;
2234 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_QueryInterface(IWineD3DSurface *iface,
2235 REFIID riid, void **object)
2237 TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object);
2239 if (IsEqualGUID(riid, &IID_IWineD3DSurface)
2240 || IsEqualGUID(riid, &IID_IUnknown))
2242 IUnknown_AddRef(iface);
2243 *object = iface;
2244 return S_OK;
2247 WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));
2249 *object = NULL;
2250 return E_NOINTERFACE;
2253 static ULONG WINAPI IWineD3DBaseSurfaceImpl_AddRef(IWineD3DSurface *iface)
2255 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2256 ULONG refcount;
2258 TRACE("Surface %p, container %p of type %#x.\n",
2259 surface, surface->container.u.base, surface->container.type);
2261 switch (surface->container.type)
2263 case WINED3D_CONTAINER_TEXTURE:
2264 return wined3d_texture_incref(surface->container.u.texture);
2266 case WINED3D_CONTAINER_SWAPCHAIN:
2267 return wined3d_swapchain_incref(surface->container.u.swapchain);
2269 default:
2270 ERR("Unhandled container type %#x.\n", surface->container.type);
2271 case WINED3D_CONTAINER_NONE:
2272 break;
2275 refcount = InterlockedIncrement(&surface->resource.ref);
2276 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2278 return refcount;
2281 /* Do not call while under the GL lock. */
2282 static ULONG WINAPI IWineD3DBaseSurfaceImpl_Release(IWineD3DSurface *iface)
2284 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2285 ULONG refcount;
2287 TRACE("Surface %p, container %p of type %#x.\n",
2288 surface, surface->container.u.base, surface->container.type);
2290 switch (surface->container.type)
2292 case WINED3D_CONTAINER_TEXTURE:
2293 return wined3d_texture_decref(surface->container.u.texture);
2295 case WINED3D_CONTAINER_SWAPCHAIN:
2296 return wined3d_swapchain_decref(surface->container.u.swapchain);
2298 default:
2299 ERR("Unhandled container type %#x.\n", surface->container.type);
2300 case WINED3D_CONTAINER_NONE:
2301 break;
2304 refcount = InterlockedDecrement(&surface->resource.ref);
2305 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2307 if (!refcount)
2309 surface_cleanup(surface);
2310 surface->surface_ops->surface_cleanup(surface);
2311 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2313 TRACE("Destroyed surface %p.\n", surface);
2314 HeapFree(GetProcessHeap(), 0, surface);
2317 return refcount;
2320 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_SetPrivateData(IWineD3DSurface *iface,
2321 REFGUID riid, const void *data, DWORD data_size, DWORD flags)
2323 return resource_set_private_data(&((IWineD3DSurfaceImpl *)iface)->resource, riid, data, data_size, flags);
2326 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_GetPrivateData(IWineD3DSurface *iface,
2327 REFGUID guid, void *data, DWORD *data_size)
2329 return resource_get_private_data(&((IWineD3DSurfaceImpl *)iface)->resource, guid, data, data_size);
2332 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_FreePrivateData(IWineD3DSurface *iface, REFGUID refguid)
2334 return resource_free_private_data(&((IWineD3DSurfaceImpl *)iface)->resource, refguid);
2337 static DWORD WINAPI IWineD3DBaseSurfaceImpl_SetPriority(IWineD3DSurface *iface, DWORD priority)
2339 return resource_set_priority(&((IWineD3DSurfaceImpl *)iface)->resource, priority);
2342 static DWORD WINAPI IWineD3DBaseSurfaceImpl_GetPriority(IWineD3DSurface *iface)
2344 return resource_get_priority(&((IWineD3DSurfaceImpl *)iface)->resource);
2347 static void WINAPI IWineD3DBaseSurfaceImpl_PreLoad(IWineD3DSurface *iface)
2349 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2351 TRACE("iface %p.\n", iface);
2353 surface->surface_ops->surface_preload(surface);
2356 static void * WINAPI IWineD3DBaseSurfaceImpl_GetParent(IWineD3DSurface *iface)
2358 TRACE("iface %p.\n", iface);
2360 return ((IWineD3DSurfaceImpl *)iface)->resource.parent;
2363 static struct wined3d_resource * WINAPI IWineD3DBaseSurfaceImpl_GetResource(IWineD3DSurface *iface)
2365 TRACE("iface %p.\n", iface);
2367 return &((IWineD3DSurfaceImpl *)iface)->resource;
2370 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_GetBltStatus(IWineD3DSurface *iface, DWORD flags)
2372 TRACE("iface %p, flags %#x.\n", iface, flags);
2374 switch (flags)
2376 case WINEDDGBS_CANBLT:
2377 case WINEDDGBS_ISBLTDONE:
2378 return WINED3D_OK;
2380 default:
2381 return WINED3DERR_INVALIDCALL;
2385 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_GetFlipStatus(IWineD3DSurface *iface, DWORD flags)
2387 /* XXX: DDERR_INVALIDSURFACETYPE */
2389 TRACE("iface %p, flags %#x.\n", iface, flags);
2391 switch (flags)
2393 case WINEDDGFS_CANFLIP:
2394 case WINEDDGFS_ISFLIPDONE:
2395 return WINED3D_OK;
2397 default:
2398 return WINED3DERR_INVALIDCALL;
2402 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_IsLost(IWineD3DSurface *iface)
2404 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2406 TRACE("iface %p.\n", iface);
2408 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2409 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2412 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_Restore(IWineD3DSurface *iface)
2414 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2416 TRACE("iface %p.\n", iface);
2418 /* So far we don't lose anything :) */
2419 surface->flags &= ~SFLAG_LOST;
2420 return WINED3D_OK;
2423 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_SetPalette(IWineD3DSurface *iface, struct wined3d_palette *palette)
2425 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2427 TRACE("iface %p, palette %p.\n", iface, palette);
2429 if (surface->palette == palette)
2431 TRACE("Nop palette change.\n");
2432 return WINED3D_OK;
2435 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
2436 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
2438 surface->palette = palette;
2440 if (palette)
2442 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2443 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
2445 surface->surface_ops->surface_realize_palette(surface);
2448 return WINED3D_OK;
2451 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_SetColorKey(IWineD3DSurface *iface,
2452 DWORD flags, const WINEDDCOLORKEY *color_key)
2454 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2456 TRACE("iface %p, flags %#x, color_key %p.\n", iface, flags, color_key);
2458 if (flags & WINEDDCKEY_COLORSPACE)
2460 FIXME(" colorkey value not supported (%08x) !\n", flags);
2461 return WINED3DERR_INVALIDCALL;
2464 /* Dirtify the surface, but only if a key was changed. */
2465 if (color_key)
2467 switch (flags & ~WINEDDCKEY_COLORSPACE)
2469 case WINEDDCKEY_DESTBLT:
2470 surface->DestBltCKey = *color_key;
2471 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
2472 break;
2474 case WINEDDCKEY_DESTOVERLAY:
2475 surface->DestOverlayCKey = *color_key;
2476 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
2477 break;
2479 case WINEDDCKEY_SRCOVERLAY:
2480 surface->SrcOverlayCKey = *color_key;
2481 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
2482 break;
2484 case WINEDDCKEY_SRCBLT:
2485 surface->SrcBltCKey = *color_key;
2486 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
2487 break;
2490 else
2492 switch (flags & ~WINEDDCKEY_COLORSPACE)
2494 case WINEDDCKEY_DESTBLT:
2495 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
2496 break;
2498 case WINEDDCKEY_DESTOVERLAY:
2499 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
2500 break;
2502 case WINEDDCKEY_SRCOVERLAY:
2503 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
2504 break;
2506 case WINEDDCKEY_SRCBLT:
2507 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
2508 break;
2512 return WINED3D_OK;
2515 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_GetPalette(IWineD3DSurface *iface, struct wined3d_palette **palette)
2517 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *) iface;
2519 TRACE("iface %p, palette %p.\n", iface, palette);
2521 *palette = surface->palette;
2523 return WINED3D_OK;
2526 static DWORD WINAPI IWineD3DBaseSurfaceImpl_GetPitch(IWineD3DSurface *iface)
2528 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2529 const struct wined3d_format *format = surface->resource.format;
2530 DWORD pitch;
2532 TRACE("iface %p.\n", iface);
2534 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
2536 /* Since compressed formats are block based, pitch means the amount of
2537 * bytes to the next row of block rather than the next row of pixels. */
2538 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
2539 pitch = row_block_count * format->block_byte_count;
2541 else
2543 unsigned char alignment = surface->resource.device->surface_alignment;
2544 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
2545 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2548 TRACE("Returning %u.\n", pitch);
2550 return pitch;
2553 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_SetMem(IWineD3DSurface *iface, void *mem)
2555 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2557 TRACE("iface %p, mem %p.\n", iface, mem);
2559 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
2561 WARN("Surface is locked or the DC is in use.\n");
2562 return WINED3DERR_INVALIDCALL;
2565 return surface->surface_ops->surface_set_mem(surface, mem);
2568 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_SetOverlayPosition(IWineD3DSurface *iface, LONG x, LONG y)
2570 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2571 LONG w, h;
2573 TRACE("iface %p, x %d, y %d.\n", iface, x, y);
2575 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2577 WARN("Not an overlay surface.\n");
2578 return WINEDDERR_NOTAOVERLAYSURFACE;
2581 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2582 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2583 surface->overlay_destrect.left = x;
2584 surface->overlay_destrect.top = y;
2585 surface->overlay_destrect.right = x + w;
2586 surface->overlay_destrect.bottom = y + h;
2588 surface->surface_ops->surface_draw_overlay(surface);
2590 return WINED3D_OK;
2593 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_GetOverlayPosition(IWineD3DSurface *iface, LONG *x, LONG *y)
2595 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2597 TRACE("iface %p, x %p, y %p.\n", iface, x, y);
2599 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2601 TRACE("Not an overlay surface.\n");
2602 return WINEDDERR_NOTAOVERLAYSURFACE;
2605 if (!surface->overlay_dest)
2607 TRACE("Overlay not visible.\n");
2608 *x = 0;
2609 *y = 0;
2610 return WINEDDERR_OVERLAYNOTVISIBLE;
2613 *x = surface->overlay_destrect.left;
2614 *y = surface->overlay_destrect.top;
2616 TRACE("Returning position %d, %d.\n", *x, *y);
2618 return WINED3D_OK;
2621 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder(IWineD3DSurface *iface,
2622 DWORD flags, IWineD3DSurface *ref)
2624 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2626 FIXME("iface %p, flags %#x, ref %p stub!\n", iface, flags, ref);
2628 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2630 TRACE("Not an overlay surface.\n");
2631 return WINEDDERR_NOTAOVERLAYSURFACE;
2634 return WINED3D_OK;
2637 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_UpdateOverlay(IWineD3DSurface *iface, const RECT *src_rect,
2638 IWineD3DSurface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2640 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2641 IWineD3DSurfaceImpl *Dst = (IWineD3DSurfaceImpl *)dst_surface;
2643 TRACE("iface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2644 iface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2646 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2648 WARN("Not an overlay surface.\n");
2649 return WINEDDERR_NOTAOVERLAYSURFACE;
2651 else if (!dst_surface)
2653 WARN("Dest surface is NULL.\n");
2654 return WINED3DERR_INVALIDCALL;
2657 if (src_rect)
2659 surface->overlay_srcrect = *src_rect;
2661 else
2663 surface->overlay_srcrect.left = 0;
2664 surface->overlay_srcrect.top = 0;
2665 surface->overlay_srcrect.right = surface->resource.width;
2666 surface->overlay_srcrect.bottom = surface->resource.height;
2669 if (dst_rect)
2671 surface->overlay_destrect = *dst_rect;
2673 else
2675 surface->overlay_destrect.left = 0;
2676 surface->overlay_destrect.top = 0;
2677 surface->overlay_destrect.right = Dst ? Dst->resource.width : 0;
2678 surface->overlay_destrect.bottom = Dst ? Dst->resource.height : 0;
2681 if (surface->overlay_dest && (surface->overlay_dest != Dst || flags & WINEDDOVER_HIDE))
2683 list_remove(&surface->overlay_entry);
2686 if (flags & WINEDDOVER_SHOW)
2688 if (surface->overlay_dest != Dst)
2690 surface->overlay_dest = Dst;
2691 list_add_tail(&Dst->overlays, &surface->overlay_entry);
2694 else if (flags & WINEDDOVER_HIDE)
2696 /* tests show that the rectangles are erased on hide */
2697 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2698 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2699 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2700 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2701 surface->overlay_dest = NULL;
2704 surface->surface_ops->surface_draw_overlay(surface);
2706 return WINED3D_OK;
2709 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_SetClipper(IWineD3DSurface *iface, struct wined3d_clipper *clipper)
2711 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2713 TRACE("iface %p, clipper %p.\n", iface, clipper);
2715 surface->clipper = clipper;
2717 return WINED3D_OK;
2720 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_GetClipper(IWineD3DSurface *iface, struct wined3d_clipper **clipper)
2722 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2724 TRACE("iface %p, clipper %p.\n", iface, clipper);
2726 *clipper = surface->clipper;
2727 if (*clipper)
2728 wined3d_clipper_incref(*clipper);
2730 return WINED3D_OK;
2733 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_SetFormat(IWineD3DSurface *iface, enum wined3d_format_id format_id)
2735 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
2736 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
2738 TRACE("iface %p, format %s.\n", iface, debug_d3dformat(format_id));
2740 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
2742 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
2743 return WINED3DERR_INVALIDCALL;
2746 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
2747 surface->pow2Width, surface->pow2Height);
2748 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
2749 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2750 surface->resource.format = format;
2752 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
2753 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
2754 format->glFormat, format->glInternal, format->glType);
2756 return WINED3D_OK;
2759 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2760 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2762 unsigned short *dst_s;
2763 const float *src_f;
2764 unsigned int x, y;
2766 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2768 for (y = 0; y < h; ++y)
2770 src_f = (const float *)(src + y * pitch_in);
2771 dst_s = (unsigned short *) (dst + y * pitch_out);
2772 for (x = 0; x < w; ++x)
2774 dst_s[x] = float_32_to_16(src_f + x);
2779 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2780 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2782 static const unsigned char convert_5to8[] =
2784 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2785 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2786 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2787 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2789 static const unsigned char convert_6to8[] =
2791 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2792 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2793 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2794 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2795 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2796 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2797 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2798 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2800 unsigned int x, y;
2802 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2804 for (y = 0; y < h; ++y)
2806 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2807 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2808 for (x = 0; x < w; ++x)
2810 WORD pixel = src_line[x];
2811 dst_line[x] = 0xff000000
2812 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2813 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2814 | convert_5to8[(pixel & 0x001f)];
2819 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2820 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2822 unsigned int x, y;
2824 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2826 for (y = 0; y < h; ++y)
2828 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2829 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2831 for (x = 0; x < w; ++x)
2833 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2838 static inline BYTE cliptobyte(int x)
2840 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2843 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2844 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2846 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2847 unsigned int x, y;
2849 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2851 for (y = 0; y < h; ++y)
2853 const BYTE *src_line = src + y * pitch_in;
2854 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2855 for (x = 0; x < w; ++x)
2857 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2858 * C = Y - 16; D = U - 128; E = V - 128;
2859 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2860 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2861 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2862 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2863 * U and V are shared between the pixels. */
2864 if (!(x & 1)) /* For every even pixel, read new U and V. */
2866 d = (int) src_line[1] - 128;
2867 e = (int) src_line[3] - 128;
2868 r2 = 409 * e + 128;
2869 g2 = - 100 * d - 208 * e + 128;
2870 b2 = 516 * d + 128;
2872 c2 = 298 * ((int) src_line[0] - 16);
2873 dst_line[x] = 0xff000000
2874 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2875 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2876 | cliptobyte((c2 + b2) >> 8); /* blue */
2877 /* Scale RGB values to 0..255 range,
2878 * then clip them if still not in range (may be negative),
2879 * then shift them within DWORD if necessary. */
2880 src_line += 2;
2885 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2886 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2888 unsigned int x, y;
2889 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2891 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2893 for (y = 0; y < h; ++y)
2895 const BYTE *src_line = src + y * pitch_in;
2896 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2897 for (x = 0; x < w; ++x)
2899 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2900 * C = Y - 16; D = U - 128; E = V - 128;
2901 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2902 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2903 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2904 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2905 * U and V are shared between the pixels. */
2906 if (!(x & 1)) /* For every even pixel, read new U and V. */
2908 d = (int) src_line[1] - 128;
2909 e = (int) src_line[3] - 128;
2910 r2 = 409 * e + 128;
2911 g2 = - 100 * d - 208 * e + 128;
2912 b2 = 516 * d + 128;
2914 c2 = 298 * ((int) src_line[0] - 16);
2915 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2916 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2917 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2918 /* Scale RGB values to 0..255 range,
2919 * then clip them if still not in range (may be negative),
2920 * then shift them within DWORD if necessary. */
2921 src_line += 2;
2926 struct d3dfmt_convertor_desc
2928 enum wined3d_format_id from, to;
2929 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2932 static const struct d3dfmt_convertor_desc convertors[] =
2934 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2935 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2936 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2937 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2938 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2941 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
2942 enum wined3d_format_id to)
2944 unsigned int i;
2946 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
2948 if (convertors[i].from == from && convertors[i].to == to)
2949 return &convertors[i];
2952 return NULL;
2955 /*****************************************************************************
2956 * surface_convert_format
2958 * Creates a duplicate of a surface in a different format. Is used by Blt to
2959 * blit between surfaces with different formats.
2961 * Parameters
2962 * source: Source surface
2963 * fmt: Requested destination format
2965 *****************************************************************************/
2966 static IWineD3DSurfaceImpl *surface_convert_format(IWineD3DSurfaceImpl *source, enum wined3d_format_id to_fmt)
2968 const struct d3dfmt_convertor_desc *conv;
2969 WINED3DLOCKED_RECT lock_src, lock_dst;
2970 IWineD3DSurface *ret = NULL;
2971 HRESULT hr;
2973 conv = find_convertor(source->resource.format->id, to_fmt);
2974 if (!conv)
2976 FIXME("Cannot find a conversion function from format %s to %s.\n",
2977 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2978 return NULL;
2981 IWineD3DDevice_CreateSurface((IWineD3DDevice *)source->resource.device, source->resource.width,
2982 source->resource.height, to_fmt, TRUE /* lockable */, TRUE /* discard */, 0 /* level */,
2983 0 /* usage */, WINED3DPOOL_SCRATCH, WINED3DMULTISAMPLE_NONE /* TODO: Multisampled conversion */,
2984 0 /* MultiSampleQuality */, source->surface_type, NULL /* parent */, &wined3d_null_parent_ops, &ret);
2985 if (!ret)
2987 ERR("Failed to create a destination surface for conversion.\n");
2988 return NULL;
2991 memset(&lock_src, 0, sizeof(lock_src));
2992 memset(&lock_dst, 0, sizeof(lock_dst));
2994 hr = IWineD3DSurface_Map((IWineD3DSurface *)source, &lock_src, NULL, WINED3DLOCK_READONLY);
2995 if (FAILED(hr))
2997 ERR("Failed to lock the source surface.\n");
2998 IWineD3DSurface_Release(ret);
2999 return NULL;
3001 hr = IWineD3DSurface_Map(ret, &lock_dst, NULL, WINED3DLOCK_READONLY);
3002 if (FAILED(hr))
3004 ERR("Failed to lock the destination surface.\n");
3005 IWineD3DSurface_Unmap((IWineD3DSurface *)source);
3006 IWineD3DSurface_Release(ret);
3007 return NULL;
3010 conv->convert(lock_src.pBits, lock_dst.pBits, lock_src.Pitch, lock_dst.Pitch,
3011 source->resource.width, source->resource.height);
3013 IWineD3DSurface_Unmap(ret);
3014 IWineD3DSurface_Unmap((IWineD3DSurface *)source);
3016 return (IWineD3DSurfaceImpl *)ret;
3019 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3020 unsigned int bpp, UINT pitch, DWORD color)
3022 BYTE *first;
3023 int x, y;
3025 /* Do first row */
3027 #define COLORFILL_ROW(type) \
3028 do { \
3029 type *d = (type *)buf; \
3030 for (x = 0; x < width; ++x) \
3031 d[x] = (type)color; \
3032 } while(0)
3034 switch (bpp)
3036 case 1:
3037 COLORFILL_ROW(BYTE);
3038 break;
3040 case 2:
3041 COLORFILL_ROW(WORD);
3042 break;
3044 case 3:
3046 BYTE *d = buf;
3047 for (x = 0; x < width; ++x, d += 3)
3049 d[0] = (color ) & 0xFF;
3050 d[1] = (color >> 8) & 0xFF;
3051 d[2] = (color >> 16) & 0xFF;
3053 break;
3055 case 4:
3056 COLORFILL_ROW(DWORD);
3057 break;
3059 default:
3060 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3061 return WINED3DERR_NOTAVAILABLE;
3064 #undef COLORFILL_ROW
3066 /* Now copy first row. */
3067 first = buf;
3068 for (y = 1; y < height; ++y)
3070 buf += pitch;
3071 memcpy(buf, first, width * bpp);
3074 return WINED3D_OK;
3077 /*****************************************************************************
3078 * IWineD3DSurface::Blt, SW emulation version
3080 * Performs a blit to a surface, with or without a source surface.
3081 * This is the main functionality of DirectDraw
3082 *****************************************************************************/
3083 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_Blt(IWineD3DSurface *iface,
3084 const RECT *dst_rect, IWineD3DSurface *src_surface, const RECT *src_rect,
3085 DWORD flags, const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
3087 IWineD3DSurfaceImpl *dst_surface = (IWineD3DSurfaceImpl *)iface;
3088 IWineD3DSurfaceImpl *src = (IWineD3DSurfaceImpl *)src_surface;
3089 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
3090 const struct wined3d_format *src_format, *dst_format;
3091 WINED3DLOCKED_RECT dlock, slock;
3092 HRESULT ret = WINED3D_OK;
3093 const BYTE *sbuf;
3094 RECT xdst,xsrc;
3095 BYTE *dbuf;
3096 int x, y;
3098 TRACE("iface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
3099 iface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3100 flags, fx, debug_d3dtexturefiltertype(filter));
3102 if ((dst_surface->flags & SFLAG_LOCKED) || (src && (src->flags & SFLAG_LOCKED)))
3104 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3105 return WINEDDERR_SURFACEBUSY;
3108 /* First check for the validity of source / destination rectangles.
3109 * This was verified using a test application and by MSDN. */
3110 if (src_rect)
3112 if (src)
3114 if (src_rect->right < src_rect->left || src_rect->bottom < src_rect->top
3115 || src_rect->left > src->resource.width || src_rect->left < 0
3116 || src_rect->top > src->resource.height || src_rect->top < 0
3117 || src_rect->right > src->resource.width || src_rect->right < 0
3118 || src_rect->bottom > src->resource.height || src_rect->bottom < 0)
3120 WARN("Application gave us bad source rectangle for Blt.\n");
3121 return WINEDDERR_INVALIDRECT;
3124 if (!src_rect->right || !src_rect->bottom
3125 || src_rect->left == (int)src->resource.width
3126 || src_rect->top == (int)src->resource.height)
3128 TRACE("Nothing to be done.\n");
3129 return WINED3D_OK;
3133 xsrc = *src_rect;
3135 else if (src)
3137 xsrc.left = 0;
3138 xsrc.top = 0;
3139 xsrc.right = src->resource.width;
3140 xsrc.bottom = src->resource.height;
3142 else
3144 memset(&xsrc, 0, sizeof(xsrc));
3147 if (dst_rect)
3149 /* For the Destination rect, it can be out of bounds on the condition
3150 * that a clipper is set for the given surface. */
3151 if (!dst_surface->clipper && (dst_rect->right < dst_rect->left || dst_rect->bottom < dst_rect->top
3152 || dst_rect->left > dst_surface->resource.width || dst_rect->left < 0
3153 || dst_rect->top > dst_surface->resource.height || dst_rect->top < 0
3154 || dst_rect->right > dst_surface->resource.width || dst_rect->right < 0
3155 || dst_rect->bottom > dst_surface->resource.height || dst_rect->bottom < 0))
3157 WARN("Application gave us bad destination rectangle for Blt without a clipper set.\n");
3158 return WINEDDERR_INVALIDRECT;
3161 if (dst_rect->right <= 0 || dst_rect->bottom <= 0
3162 || dst_rect->left >= (int)dst_surface->resource.width
3163 || dst_rect->top >= (int)dst_surface->resource.height)
3165 TRACE("Nothing to be done.\n");
3166 return WINED3D_OK;
3169 if (!src)
3171 RECT full_rect;
3173 full_rect.left = 0;
3174 full_rect.top = 0;
3175 full_rect.right = dst_surface->resource.width;
3176 full_rect.bottom = dst_surface->resource.height;
3177 IntersectRect(&xdst, &full_rect, dst_rect);
3179 else
3181 BOOL clip_horiz, clip_vert;
3183 xdst = *dst_rect;
3184 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
3185 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
3187 if (clip_vert || clip_horiz)
3189 /* Now check if this is a special case or not... */
3190 if ((flags & WINEDDBLT_DDFX)
3191 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
3192 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
3194 WARN("Out of screen rectangle in special case. Not handled right now.\n");
3195 return WINED3D_OK;
3198 if (clip_horiz)
3200 if (xdst.left < 0)
3202 xsrc.left -= xdst.left;
3203 xdst.left = 0;
3205 if (xdst.right > dst_surface->resource.width)
3207 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
3208 xdst.right = (int)dst_surface->resource.width;
3212 if (clip_vert)
3214 if (xdst.top < 0)
3216 xsrc.top -= xdst.top;
3217 xdst.top = 0;
3219 if (xdst.bottom > dst_surface->resource.height)
3221 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
3222 xdst.bottom = (int)dst_surface->resource.height;
3226 /* And check if after clipping something is still to be done... */
3227 if ((xdst.right <= 0) || (xdst.bottom <= 0)
3228 || (xdst.left >= (int)dst_surface->resource.width)
3229 || (xdst.top >= (int)dst_surface->resource.height)
3230 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
3231 || (xsrc.left >= (int)src->resource.width)
3232 || (xsrc.top >= (int)src->resource.height))
3234 TRACE("Nothing to be done after clipping.\n");
3235 return WINED3D_OK;
3240 else
3242 xdst.left = 0;
3243 xdst.top = 0;
3244 xdst.right = dst_surface->resource.width;
3245 xdst.bottom = dst_surface->resource.height;
3248 if (src == dst_surface)
3250 IWineD3DSurface_Map(iface, &dlock, NULL, 0);
3251 slock = dlock;
3252 src_format = dst_surface->resource.format;
3253 dst_format = src_format;
3255 else
3257 dst_format = dst_surface->resource.format;
3258 if (src)
3260 if (dst_surface->resource.format->id != src->resource.format->id)
3262 src = surface_convert_format(src, dst_format->id);
3263 if (!src)
3265 /* The conv function writes a FIXME */
3266 WARN("Cannot convert source surface format to dest format\n");
3267 goto release;
3270 IWineD3DSurface_Map((IWineD3DSurface *)src, &slock, NULL, WINED3DLOCK_READONLY);
3271 src_format = src->resource.format;
3273 else
3275 src_format = dst_format;
3277 if (dst_rect)
3278 IWineD3DSurface_Map(iface, &dlock, &xdst, 0);
3279 else
3280 IWineD3DSurface_Map(iface, &dlock, NULL, 0);
3283 if (!fx || !(fx->dwDDFX)) flags &= ~WINEDDBLT_DDFX;
3285 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_FOURCC)
3287 if (!dst_rect || src == dst_surface)
3289 memcpy(dlock.pBits, slock.pBits, dst_surface->resource.size);
3290 goto release;
3294 bpp = dst_surface->resource.format->byte_count;
3295 srcheight = xsrc.bottom - xsrc.top;
3296 srcwidth = xsrc.right - xsrc.left;
3297 dstheight = xdst.bottom - xdst.top;
3298 dstwidth = xdst.right - xdst.left;
3299 width = (xdst.right - xdst.left) * bpp;
3301 if (dst_rect && src != dst_surface)
3302 dbuf = dlock.pBits;
3303 else
3304 dbuf = (BYTE*)dlock.pBits+(xdst.top*dlock.Pitch)+(xdst.left*bpp);
3306 if (flags & WINEDDBLT_WAIT)
3308 flags &= ~WINEDDBLT_WAIT;
3310 if (flags & WINEDDBLT_ASYNC)
3312 static BOOL displayed = FALSE;
3313 if (!displayed)
3314 FIXME("Can't handle WINEDDBLT_ASYNC flag right now.\n");
3315 displayed = TRUE;
3316 flags &= ~WINEDDBLT_ASYNC;
3318 if (flags & WINEDDBLT_DONOTWAIT)
3320 /* WINEDDBLT_DONOTWAIT appeared in DX7 */
3321 static BOOL displayed = FALSE;
3322 if (!displayed)
3323 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag right now.\n");
3324 displayed = TRUE;
3325 flags &= ~WINEDDBLT_DONOTWAIT;
3328 /* First, all the 'source-less' blits */
3329 if (flags & WINEDDBLT_COLORFILL)
3331 ret = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dlock.Pitch, fx->u5.dwFillColor);
3332 flags &= ~WINEDDBLT_COLORFILL;
3335 if (flags & WINEDDBLT_DEPTHFILL)
3337 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
3339 if (flags & WINEDDBLT_ROP)
3341 /* Catch some degenerate cases here. */
3342 switch (fx->dwROP)
3344 case BLACKNESS:
3345 ret = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,0);
3346 break;
3347 case 0xAA0029: /* No-op */
3348 break;
3349 case WHITENESS:
3350 ret = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,~0);
3351 break;
3352 case SRCCOPY: /* Well, we do that below? */
3353 break;
3354 default:
3355 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
3356 goto error;
3358 flags &= ~WINEDDBLT_ROP;
3360 if (flags & WINEDDBLT_DDROPS)
3362 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
3364 /* Now the 'with source' blits. */
3365 if (src)
3367 const BYTE *sbase;
3368 int sx, xinc, sy, yinc;
3370 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
3371 goto release;
3373 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
3374 && (srcwidth != dstwidth || srcheight != dstheight))
3376 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
3377 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
3380 sbase = (BYTE*)slock.pBits+(xsrc.top*slock.Pitch)+xsrc.left*bpp;
3381 xinc = (srcwidth << 16) / dstwidth;
3382 yinc = (srcheight << 16) / dstheight;
3384 if (!flags)
3386 /* No effects, we can cheat here. */
3387 if (dstwidth == srcwidth)
3389 if (dstheight == srcheight)
3391 /* No stretching in either direction. This needs to be as
3392 * fast as possible. */
3393 sbuf = sbase;
3395 /* Check for overlapping surfaces. */
3396 if (src != dst_surface || xdst.top < xsrc.top
3397 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
3399 /* No overlap, or dst above src, so copy from top downwards. */
3400 for (y = 0; y < dstheight; ++y)
3402 memcpy(dbuf, sbuf, width);
3403 sbuf += slock.Pitch;
3404 dbuf += dlock.Pitch;
3407 else if (xdst.top > xsrc.top)
3409 /* Copy from bottom upwards. */
3410 sbuf += (slock.Pitch*dstheight);
3411 dbuf += (dlock.Pitch*dstheight);
3412 for (y = 0; y < dstheight; ++y)
3414 sbuf -= slock.Pitch;
3415 dbuf -= dlock.Pitch;
3416 memcpy(dbuf, sbuf, width);
3419 else
3421 /* Src and dst overlapping on the same line, use memmove. */
3422 for (y = 0; y < dstheight; ++y)
3424 memmove(dbuf, sbuf, width);
3425 sbuf += slock.Pitch;
3426 dbuf += dlock.Pitch;
3430 else
3432 /* Stretching in y direction only. */
3433 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
3435 sbuf = sbase + (sy >> 16) * slock.Pitch;
3436 memcpy(dbuf, sbuf, width);
3437 dbuf += dlock.Pitch;
3441 else
3443 /* Stretching in X direction. */
3444 int last_sy = -1;
3445 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
3447 sbuf = sbase + (sy >> 16) * slock.Pitch;
3449 if ((sy >> 16) == (last_sy >> 16))
3451 /* This source row is the same as last source row -
3452 * Copy the already stretched row. */
3453 memcpy(dbuf, dbuf - dlock.Pitch, width);
3455 else
3457 #define STRETCH_ROW(type) \
3458 do { \
3459 const type *s = (const type *)sbuf; \
3460 type *d = (type *)dbuf; \
3461 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
3462 d[x] = s[sx >> 16]; \
3463 } while(0)
3465 switch(bpp)
3467 case 1:
3468 STRETCH_ROW(BYTE);
3469 break;
3470 case 2:
3471 STRETCH_ROW(WORD);
3472 break;
3473 case 4:
3474 STRETCH_ROW(DWORD);
3475 break;
3476 case 3:
3478 const BYTE *s;
3479 BYTE *d = dbuf;
3480 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
3482 DWORD pixel;
3484 s = sbuf + 3 * (sx >> 16);
3485 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3486 d[0] = (pixel ) & 0xff;
3487 d[1] = (pixel >> 8) & 0xff;
3488 d[2] = (pixel >> 16) & 0xff;
3489 d += 3;
3491 break;
3493 default:
3494 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
3495 ret = WINED3DERR_NOTAVAILABLE;
3496 goto error;
3498 #undef STRETCH_ROW
3500 dbuf += dlock.Pitch;
3501 last_sy = sy;
3505 else
3507 LONG dstyinc = dlock.Pitch, dstxinc = bpp;
3508 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
3509 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
3510 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
3512 /* The color keying flags are checked for correctness in ddraw */
3513 if (flags & WINEDDBLT_KEYSRC)
3515 keylow = src->SrcBltCKey.dwColorSpaceLowValue;
3516 keyhigh = src->SrcBltCKey.dwColorSpaceHighValue;
3518 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
3520 keylow = fx->ddckSrcColorkey.dwColorSpaceLowValue;
3521 keyhigh = fx->ddckSrcColorkey.dwColorSpaceHighValue;
3524 if (flags & WINEDDBLT_KEYDEST)
3526 /* Destination color keys are taken from the source surface! */
3527 destkeylow = src->DestBltCKey.dwColorSpaceLowValue;
3528 destkeyhigh = src->DestBltCKey.dwColorSpaceHighValue;
3530 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
3532 destkeylow = fx->ddckDestColorkey.dwColorSpaceLowValue;
3533 destkeyhigh = fx->ddckDestColorkey.dwColorSpaceHighValue;
3536 if (bpp == 1)
3538 keymask = 0xff;
3540 else
3542 keymask = src_format->red_mask
3543 | src_format->green_mask
3544 | src_format->blue_mask;
3546 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
3549 if (flags & WINEDDBLT_DDFX)
3551 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
3552 LONG tmpxy;
3553 dTopLeft = dbuf;
3554 dTopRight = dbuf + ((dstwidth - 1) * bpp);
3555 dBottomLeft = dTopLeft + ((dstheight - 1) * dlock.Pitch);
3556 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
3558 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
3560 /* I don't think we need to do anything about this flag */
3561 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
3563 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
3565 tmp = dTopRight;
3566 dTopRight = dTopLeft;
3567 dTopLeft = tmp;
3568 tmp = dBottomRight;
3569 dBottomRight = dBottomLeft;
3570 dBottomLeft = tmp;
3571 dstxinc = dstxinc * -1;
3573 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
3575 tmp = dTopLeft;
3576 dTopLeft = dBottomLeft;
3577 dBottomLeft = tmp;
3578 tmp = dTopRight;
3579 dTopRight = dBottomRight;
3580 dBottomRight = tmp;
3581 dstyinc = dstyinc * -1;
3583 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
3585 /* I don't think we need to do anything about this flag */
3586 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
3588 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
3590 tmp = dBottomRight;
3591 dBottomRight = dTopLeft;
3592 dTopLeft = tmp;
3593 tmp = dBottomLeft;
3594 dBottomLeft = dTopRight;
3595 dTopRight = tmp;
3596 dstxinc = dstxinc * -1;
3597 dstyinc = dstyinc * -1;
3599 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
3601 tmp = dTopLeft;
3602 dTopLeft = dBottomLeft;
3603 dBottomLeft = dBottomRight;
3604 dBottomRight = dTopRight;
3605 dTopRight = tmp;
3606 tmpxy = dstxinc;
3607 dstxinc = dstyinc;
3608 dstyinc = tmpxy;
3609 dstxinc = dstxinc * -1;
3611 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
3613 tmp = dTopLeft;
3614 dTopLeft = dTopRight;
3615 dTopRight = dBottomRight;
3616 dBottomRight = dBottomLeft;
3617 dBottomLeft = tmp;
3618 tmpxy = dstxinc;
3619 dstxinc = dstyinc;
3620 dstyinc = tmpxy;
3621 dstyinc = dstyinc * -1;
3623 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
3625 /* I don't think we need to do anything about this flag */
3626 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
3628 dbuf = dTopLeft;
3629 flags &= ~(WINEDDBLT_DDFX);
3632 #define COPY_COLORKEY_FX(type) \
3633 do { \
3634 const type *s; \
3635 type *d = (type *)dbuf, *dx, tmp; \
3636 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
3638 s = (const type *)(sbase + (sy >> 16) * slock.Pitch); \
3639 dx = d; \
3640 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
3642 tmp = s[sx >> 16]; \
3643 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
3644 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
3646 dx[0] = tmp; \
3648 dx = (type *)(((BYTE *)dx) + dstxinc); \
3650 d = (type *)(((BYTE *)d) + dstyinc); \
3652 } while(0)
3654 switch (bpp)
3656 case 1:
3657 COPY_COLORKEY_FX(BYTE);
3658 break;
3659 case 2:
3660 COPY_COLORKEY_FX(WORD);
3661 break;
3662 case 4:
3663 COPY_COLORKEY_FX(DWORD);
3664 break;
3665 case 3:
3667 const BYTE *s;
3668 BYTE *d = dbuf, *dx;
3669 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
3671 sbuf = sbase + (sy >> 16) * slock.Pitch;
3672 dx = d;
3673 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
3675 DWORD pixel, dpixel = 0;
3676 s = sbuf + 3 * (sx>>16);
3677 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3678 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
3679 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
3680 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
3682 dx[0] = (pixel ) & 0xff;
3683 dx[1] = (pixel >> 8) & 0xff;
3684 dx[2] = (pixel >> 16) & 0xff;
3686 dx += dstxinc;
3688 d += dstyinc;
3690 break;
3692 default:
3693 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
3694 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
3695 ret = WINED3DERR_NOTAVAILABLE;
3696 goto error;
3697 #undef COPY_COLORKEY_FX
3702 error:
3703 if (flags && FIXME_ON(d3d_surface))
3705 FIXME("\tUnsupported flags: %#x.\n", flags);
3708 release:
3709 IWineD3DSurface_Unmap(iface);
3710 if (src && src != dst_surface)
3711 IWineD3DSurface_Unmap((IWineD3DSurface *)src);
3712 /* Release the converted surface, if any. */
3713 if (src && src_surface != (IWineD3DSurface *)src)
3714 IWineD3DSurface_Release((IWineD3DSurface *)src);
3715 return ret;
3718 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_BltFast(IWineD3DSurface *iface,
3719 DWORD dstx, DWORD dsty, IWineD3DSurface *src_surface, const RECT *rsrc, DWORD trans)
3721 IWineD3DSurfaceImpl *dst_surface = (IWineD3DSurfaceImpl *)iface;
3722 IWineD3DSurfaceImpl *src = (IWineD3DSurfaceImpl *)src_surface;
3723 const struct wined3d_format *src_format, *dst_format;
3724 RECT lock_src, lock_dst, lock_union;
3725 WINED3DLOCKED_RECT dlock, slock;
3726 HRESULT ret = WINED3D_OK;
3727 int bpp, w, h, x, y;
3728 const BYTE *sbuf;
3729 BYTE *dbuf;
3730 RECT rsrc2;
3732 TRACE("iface %p, dst_x %u, dst_y %u, src_surface %p, src_rect %s, flags %#x.\n",
3733 iface, dstx, dsty, src_surface, wine_dbgstr_rect(rsrc), trans);
3735 if ((dst_surface->flags & SFLAG_LOCKED) || (src->flags & SFLAG_LOCKED))
3737 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3738 return WINEDDERR_SURFACEBUSY;
3741 if (!rsrc)
3743 WARN("rsrc is NULL!\n");
3744 rsrc2.left = 0;
3745 rsrc2.top = 0;
3746 rsrc2.right = src->resource.width;
3747 rsrc2.bottom = src->resource.height;
3748 rsrc = &rsrc2;
3751 /* Check source rect for validity. Copied from normal Blt. Fixes Baldur's Gate. */
3752 if ((rsrc->bottom > src->resource.height) || (rsrc->bottom < 0)
3753 || (rsrc->top > src->resource.height) || (rsrc->top < 0)
3754 || (rsrc->left > src->resource.width) || (rsrc->left < 0)
3755 || (rsrc->right > src->resource.width) || (rsrc->right < 0)
3756 || (rsrc->right < rsrc->left) || (rsrc->bottom < rsrc->top))
3758 WARN("Application gave us bad source rectangle for BltFast.\n");
3759 return WINEDDERR_INVALIDRECT;
3762 h = rsrc->bottom - rsrc->top;
3763 if (h > dst_surface->resource.height-dsty)
3764 h = dst_surface->resource.height-dsty;
3765 if (h > src->resource.height-rsrc->top)
3766 h = src->resource.height-rsrc->top;
3767 if (h <= 0)
3768 return WINEDDERR_INVALIDRECT;
3770 w = rsrc->right - rsrc->left;
3771 if (w > dst_surface->resource.width-dstx)
3772 w = dst_surface->resource.width-dstx;
3773 if (w > src->resource.width-rsrc->left)
3774 w = src->resource.width-rsrc->left;
3775 if (w <= 0)
3776 return WINEDDERR_INVALIDRECT;
3778 /* Now compute the locking rectangle... */
3779 lock_src.left = rsrc->left;
3780 lock_src.top = rsrc->top;
3781 lock_src.right = lock_src.left + w;
3782 lock_src.bottom = lock_src.top + h;
3784 lock_dst.left = dstx;
3785 lock_dst.top = dsty;
3786 lock_dst.right = dstx + w;
3787 lock_dst.bottom = dsty + h;
3789 bpp = dst_surface->resource.format->byte_count;
3791 /* We need to lock the surfaces, or we won't get refreshes when done. */
3792 if (src == dst_surface)
3794 int pitch;
3796 UnionRect(&lock_union, &lock_src, &lock_dst);
3798 /* Lock the union of the two rectangles */
3799 ret = IWineD3DSurface_Map(iface, &dlock, &lock_union, 0);
3800 if (FAILED(ret))
3801 goto error;
3803 pitch = dlock.Pitch;
3804 slock.Pitch = dlock.Pitch;
3806 /* Since slock was originally copied from this surface's description, we can just reuse it. */
3807 sbuf = dst_surface->resource.allocatedMemory + lock_src.top * pitch + lock_src.left * bpp;
3808 dbuf = dst_surface->resource.allocatedMemory + lock_dst.top * pitch + lock_dst.left * bpp;
3809 src_format = src->resource.format;
3810 dst_format = src_format;
3812 else
3814 ret = IWineD3DSurface_Map(src_surface, &slock, &lock_src, WINED3DLOCK_READONLY);
3815 if (FAILED(ret))
3816 goto error;
3817 ret = IWineD3DSurface_Map(iface, &dlock, &lock_dst, 0);
3818 if (FAILED(ret))
3819 goto error;
3821 sbuf = slock.pBits;
3822 dbuf = dlock.pBits;
3823 TRACE("Dst is at %p, Src is at %p.\n", dbuf, sbuf);
3825 src_format = src->resource.format;
3826 dst_format = dst_surface->resource.format;
3829 /* Handle compressed surfaces first... */
3830 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_COMPRESSED)
3832 UINT row_block_count;
3834 TRACE("compressed -> compressed copy\n");
3835 if (trans)
3836 FIXME("trans arg not supported when a compressed surface is involved\n");
3837 if (dstx || dsty)
3838 FIXME("offset for destination surface is not supported\n");
3839 if (src->resource.format->id != dst_surface->resource.format->id)
3841 FIXME("compressed -> compressed copy only supported for the same type of surface\n");
3842 ret = WINED3DERR_WRONGTEXTUREFORMAT;
3843 goto error;
3846 row_block_count = (w + dst_format->block_width - 1) / dst_format->block_width;
3847 for (y = 0; y < h; y += dst_format->block_height)
3849 memcpy(dbuf, sbuf, row_block_count * dst_format->block_byte_count);
3850 dbuf += dlock.Pitch;
3851 sbuf += slock.Pitch;
3854 goto error;
3856 if ((src_format->flags & WINED3DFMT_FLAG_COMPRESSED) && !(dst_format->flags & WINED3DFMT_FLAG_COMPRESSED))
3858 /* TODO: Use the libtxc_dxtn.so shared library to do software
3859 * decompression. */
3860 ERR("Software decompression not supported.\n");
3861 goto error;
3864 if (trans & (WINEDDBLTFAST_SRCCOLORKEY | WINEDDBLTFAST_DESTCOLORKEY))
3866 DWORD keylow, keyhigh;
3867 DWORD mask = src->resource.format->red_mask
3868 | src->resource.format->green_mask
3869 | src->resource.format->blue_mask;
3871 /* For some 8-bit formats like L8 and P8 color masks don't make sense */
3872 if (!mask && bpp == 1)
3873 mask = 0xff;
3875 TRACE("Color keyed copy.\n");
3876 if (trans & WINEDDBLTFAST_SRCCOLORKEY)
3878 keylow = src->SrcBltCKey.dwColorSpaceLowValue;
3879 keyhigh = src->SrcBltCKey.dwColorSpaceHighValue;
3881 else
3883 /* I'm not sure if this is correct. */
3884 FIXME("WINEDDBLTFAST_DESTCOLORKEY not fully supported yet.\n");
3885 keylow = dst_surface->DestBltCKey.dwColorSpaceLowValue;
3886 keyhigh = dst_surface->DestBltCKey.dwColorSpaceHighValue;
3889 #define COPYBOX_COLORKEY(type) \
3890 do { \
3891 const type *s = (const type *)sbuf; \
3892 type *d = (type *)dbuf; \
3893 type tmp; \
3894 for (y = 0; y < h; y++) \
3896 for (x = 0; x < w; x++) \
3898 tmp = s[x]; \
3899 if ((tmp & mask) < keylow || (tmp & mask) > keyhigh) d[x] = tmp; \
3901 s = (const type *)((const BYTE *)s + slock.Pitch); \
3902 d = (type *)((BYTE *)d + dlock.Pitch); \
3904 } while(0)
3906 switch (bpp)
3908 case 1:
3909 COPYBOX_COLORKEY(BYTE);
3910 break;
3911 case 2:
3912 COPYBOX_COLORKEY(WORD);
3913 break;
3914 case 4:
3915 COPYBOX_COLORKEY(DWORD);
3916 break;
3917 case 3:
3919 const BYTE *s;
3920 DWORD tmp;
3921 BYTE *d;
3922 s = sbuf;
3923 d = dbuf;
3924 for (y = 0; y < h; ++y)
3926 for (x = 0; x < w * 3; x += 3)
3928 tmp = (DWORD)s[x] + ((DWORD)s[x + 1] << 8) + ((DWORD)s[x + 2] << 16);
3929 if (tmp < keylow || tmp > keyhigh)
3931 d[x + 0] = s[x + 0];
3932 d[x + 1] = s[x + 1];
3933 d[x + 2] = s[x + 2];
3936 s += slock.Pitch;
3937 d += dlock.Pitch;
3939 break;
3941 default:
3942 FIXME("Source color key blitting not supported for bpp %u.\n", bpp * 8);
3943 ret = WINED3DERR_NOTAVAILABLE;
3944 goto error;
3946 #undef COPYBOX_COLORKEY
3947 TRACE("Copy done.\n");
3949 else
3951 int width = w * bpp;
3952 INT sbufpitch, dbufpitch;
3954 TRACE("No color key copy.\n");
3955 /* Handle overlapping surfaces. */
3956 if (sbuf < dbuf)
3958 sbuf += (h - 1) * slock.Pitch;
3959 dbuf += (h - 1) * dlock.Pitch;
3960 sbufpitch = -slock.Pitch;
3961 dbufpitch = -dlock.Pitch;
3963 else
3965 sbufpitch = slock.Pitch;
3966 dbufpitch = dlock.Pitch;
3968 for (y = 0; y < h; ++y)
3970 /* This is pretty easy, a line for line memcpy. */
3971 memmove(dbuf, sbuf, width);
3972 sbuf += sbufpitch;
3973 dbuf += dbufpitch;
3975 TRACE("Copy done.\n");
3978 error:
3979 if (src == dst_surface)
3981 IWineD3DSurface_Unmap(iface);
3983 else
3985 IWineD3DSurface_Unmap(iface);
3986 IWineD3DSurface_Unmap(src_surface);
3989 return ret;
3992 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_Unmap(IWineD3DSurface *iface)
3994 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
3996 TRACE("iface %p.\n", iface);
3998 if (!(surface->flags & SFLAG_LOCKED))
4000 WARN("Trying to unmap unmapped surface.\n");
4001 return WINEDDERR_NOTLOCKED;
4003 surface->flags &= ~SFLAG_LOCKED;
4005 surface->surface_ops->surface_unmap(surface);
4007 return WINED3D_OK;
4010 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_Map(IWineD3DSurface *iface,
4011 WINED3DLOCKED_RECT *locked_rect, const RECT *rect, DWORD flags)
4013 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
4015 TRACE("iface %p, locked_rect %p, rect %s, flags %#x.\n",
4016 iface, locked_rect, wine_dbgstr_rect(rect), flags);
4018 if (surface->flags & SFLAG_LOCKED)
4020 WARN("Surface is already mapped.\n");
4021 return WINED3DERR_INVALIDCALL;
4023 surface->flags |= SFLAG_LOCKED;
4025 if (!(surface->flags & SFLAG_LOCKABLE))
4026 WARN("Trying to lock unlockable surface.\n");
4028 surface->surface_ops->surface_map(surface, rect, flags);
4030 locked_rect->Pitch = IWineD3DSurface_GetPitch(iface);
4032 if (!rect)
4034 locked_rect->pBits = surface->resource.allocatedMemory;
4035 surface->lockedRect.left = 0;
4036 surface->lockedRect.top = 0;
4037 surface->lockedRect.right = surface->resource.width;
4038 surface->lockedRect.bottom = surface->resource.height;
4040 else
4042 const struct wined3d_format *format = surface->resource.format;
4044 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
4046 /* Compressed textures are block based, so calculate the offset of
4047 * the block that contains the top-left pixel of the locked rectangle. */
4048 locked_rect->pBits = surface->resource.allocatedMemory
4049 + ((rect->top / format->block_height) * locked_rect->Pitch)
4050 + ((rect->left / format->block_width) * format->block_byte_count);
4052 else
4054 locked_rect->pBits = surface->resource.allocatedMemory
4055 + (locked_rect->Pitch * rect->top)
4056 + (rect->left * format->byte_count);
4058 surface->lockedRect.left = rect->left;
4059 surface->lockedRect.top = rect->top;
4060 surface->lockedRect.right = rect->right;
4061 surface->lockedRect.bottom = rect->bottom;
4064 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
4065 TRACE("Returning memory %p, pitch %u.\n", locked_rect->pBits, locked_rect->Pitch);
4067 return WINED3D_OK;
4070 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *dc)
4072 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
4073 HRESULT hr;
4075 TRACE("iface %p, dc %p.\n", iface, dc);
4077 if (surface->flags & SFLAG_USERPTR)
4079 ERR("Not supported on surfaces with application-provided memory.\n");
4080 return WINEDDERR_NODC;
4083 /* Give more detailed info for ddraw. */
4084 if (surface->flags & SFLAG_DCINUSE)
4085 return WINEDDERR_DCALREADYCREATED;
4087 /* Can't GetDC if the surface is locked. */
4088 if (surface->flags & SFLAG_LOCKED)
4089 return WINED3DERR_INVALIDCALL;
4091 hr = surface->surface_ops->surface_getdc(surface);
4092 if (FAILED(hr))
4093 return hr;
4095 if (surface->resource.format->id == WINED3DFMT_P8_UINT
4096 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
4098 /* GetDC on palettized formats is unsupported in D3D9, and the method
4099 * is missing in D3D8, so this should only be used for DX <=7
4100 * surfaces (with non-device palettes). */
4101 const PALETTEENTRY *pal = NULL;
4103 if (surface->palette)
4105 pal = surface->palette->palents;
4107 else
4109 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
4110 IWineD3DSurfaceImpl *dds_primary = swapchain->front_buffer;
4112 if (dds_primary && dds_primary->palette)
4113 pal = dds_primary->palette->palents;
4116 if (pal)
4118 RGBQUAD col[256];
4119 unsigned int i;
4121 for (i = 0; i < 256; ++i)
4123 col[i].rgbRed = pal[i].peRed;
4124 col[i].rgbGreen = pal[i].peGreen;
4125 col[i].rgbBlue = pal[i].peBlue;
4126 col[i].rgbReserved = 0;
4128 SetDIBColorTable(surface->hDC, 0, 256, col);
4132 surface->flags |= SFLAG_DCINUSE;
4134 *dc = surface->hDC;
4135 TRACE("Returning dc %p.\n", *dc);
4137 return WINED3D_OK;
4140 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC dc)
4142 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
4144 TRACE("iface %p, dc %p.\n", iface, dc);
4146 if (!(surface->flags & SFLAG_DCINUSE))
4147 return WINEDDERR_NODC;
4149 if (surface->hDC != dc)
4151 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
4152 dc, surface->hDC);
4153 return WINEDDERR_NODC;
4156 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
4158 /* Copy the contents of the DIB over to the PBO. */
4159 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->dib.bitmap_size);
4162 /* We locked first, so unlock now. */
4163 IWineD3DSurface_Unmap(iface);
4165 surface->flags &= ~SFLAG_DCINUSE;
4167 return WINED3D_OK;
4170 static HRESULT WINAPI IWineD3DBaseSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD flags)
4172 IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
4173 struct wined3d_swapchain *swapchain;
4174 HRESULT hr;
4176 TRACE("iface %p, override %p, flags %#x.\n", iface, override, flags);
4178 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
4180 ERR("Flipped surface is not on a swapchain.\n");
4181 return WINEDDERR_NOTFLIPPABLE;
4183 swapchain = surface->container.u.swapchain;
4185 hr = surface->surface_ops->surface_flip(surface, (IWineD3DSurfaceImpl *)override);
4186 if (FAILED(hr))
4187 return hr;
4189 /* Just overwrite the swapchain presentation interval. This is ok because
4190 * only ddraw apps can call Flip, and only d3d8 and d3d9 applications
4191 * specify the presentation interval. */
4192 if (!(flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)))
4193 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
4194 else if (flags & WINEDDFLIP_NOVSYNC)
4195 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
4196 else if (flags & WINEDDFLIP_INTERVAL2)
4197 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
4198 else if (flags & WINEDDFLIP_INTERVAL3)
4199 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
4200 else
4201 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
4203 return wined3d_swapchain_present(swapchain, NULL, NULL, swapchain->win_handle, NULL, 0);
4206 /* ****************************************************
4207 IWineD3DSurface IWineD3DResource parts follow
4208 **************************************************** */
4210 /* Do not call while under the GL lock. */
4211 void surface_internal_preload(IWineD3DSurfaceImpl *surface, enum WINED3DSRGB srgb)
4213 IWineD3DDeviceImpl *device = surface->resource.device;
4215 TRACE("iface %p, srgb %#x.\n", surface, srgb);
4217 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4219 struct wined3d_texture *texture = surface->container.u.texture;
4221 TRACE("Passing to container (%p).\n", texture);
4222 texture->texture_ops->texture_preload(texture, srgb);
4224 else
4226 struct wined3d_context *context = NULL;
4228 TRACE("(%p) : About to load surface\n", surface);
4230 if (!device->isInDraw) context = context_acquire(device, NULL);
4232 if (surface->resource.format->id == WINED3DFMT_P8_UINT
4233 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
4235 if (palette9_changed(surface))
4237 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
4238 /* TODO: This is not necessarily needed with hw palettized texture support */
4239 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
4240 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
4241 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
4245 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
4247 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
4249 /* Tell opengl to try and keep this texture in video ram (well mostly) */
4250 GLclampf tmp;
4251 tmp = 0.9f;
4252 ENTER_GL();
4253 glPrioritizeTextures(1, &surface->texture_name, &tmp);
4254 LEAVE_GL();
4257 if (context) context_release(context);
4261 BOOL surface_init_sysmem(IWineD3DSurfaceImpl *surface)
4263 if (!surface->resource.allocatedMemory)
4265 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
4266 surface->resource.size + RESOURCE_ALIGNMENT);
4267 if (!surface->resource.heapMemory)
4269 ERR("Out of memory\n");
4270 return FALSE;
4272 surface->resource.allocatedMemory =
4273 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
4275 else
4277 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
4280 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
4282 return TRUE;
4285 /* ******************************************************
4286 IWineD3DSurface IWineD3DSurface parts follow
4287 ****************************************************** */
4289 /* Read the framebuffer back into the surface */
4290 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, const RECT *rect, void *dest, UINT pitch)
4292 IWineD3DDeviceImpl *device = This->resource.device;
4293 const struct wined3d_gl_info *gl_info;
4294 struct wined3d_context *context;
4295 BYTE *mem;
4296 GLint fmt;
4297 GLint type;
4298 BYTE *row, *top, *bottom;
4299 int i;
4300 BOOL bpp;
4301 RECT local_rect;
4302 BOOL srcIsUpsideDown;
4303 GLint rowLen = 0;
4304 GLint skipPix = 0;
4305 GLint skipRow = 0;
4307 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
4308 static BOOL warned = FALSE;
4309 if(!warned) {
4310 ERR("The application tries to lock the render target, but render target locking is disabled\n");
4311 warned = TRUE;
4313 return;
4316 context = context_acquire(device, This);
4317 context_apply_blit_state(context, device);
4318 gl_info = context->gl_info;
4320 ENTER_GL();
4322 /* Select the correct read buffer, and give some debug output.
4323 * There is no need to keep track of the current read buffer or reset it, every part of the code
4324 * that reads sets the read buffer as desired.
4326 if (surface_is_offscreen(This))
4328 /* Mapping the primary render target which is not on a swapchain.
4329 * Read from the back buffer. */
4330 TRACE("Mapping offscreen render target.\n");
4331 glReadBuffer(device->offscreenBuffer);
4332 srcIsUpsideDown = TRUE;
4334 else
4336 /* Onscreen surfaces are always part of a swapchain */
4337 GLenum buffer = surface_get_gl_buffer(This);
4338 TRACE("Mapping %#x buffer.\n", buffer);
4339 glReadBuffer(buffer);
4340 checkGLcall("glReadBuffer");
4341 srcIsUpsideDown = FALSE;
4344 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
4345 if(!rect) {
4346 local_rect.left = 0;
4347 local_rect.top = 0;
4348 local_rect.right = This->resource.width;
4349 local_rect.bottom = This->resource.height;
4351 else
4353 local_rect = *rect;
4355 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
4357 switch (This->resource.format->id)
4359 case WINED3DFMT_P8_UINT:
4361 if (primary_render_target_is_p8(device))
4363 /* In case of P8 render targets the index is stored in the alpha component */
4364 fmt = GL_ALPHA;
4365 type = GL_UNSIGNED_BYTE;
4366 mem = dest;
4367 bpp = This->resource.format->byte_count;
4368 } else {
4369 /* GL can't return palettized data, so read ARGB pixels into a
4370 * separate block of memory and convert them into palettized format
4371 * in software. Slow, but if the app means to use palettized render
4372 * targets and locks it...
4374 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4375 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4376 * for the color channels when palettizing the colors.
4378 fmt = GL_RGB;
4379 type = GL_UNSIGNED_BYTE;
4380 pitch *= 3;
4381 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
4382 if(!mem) {
4383 ERR("Out of memory\n");
4384 LEAVE_GL();
4385 return;
4387 bpp = This->resource.format->byte_count * 3;
4390 break;
4392 default:
4393 mem = dest;
4394 fmt = This->resource.format->glFormat;
4395 type = This->resource.format->glType;
4396 bpp = This->resource.format->byte_count;
4399 if (This->flags & SFLAG_PBO)
4401 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
4402 checkGLcall("glBindBufferARB");
4403 if (mem)
4405 ERR("mem not null for pbo -- unexpected\n");
4406 mem = NULL;
4410 /* Save old pixel store pack state */
4411 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4412 checkGLcall("glGetIntegerv");
4413 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4414 checkGLcall("glGetIntegerv");
4415 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4416 checkGLcall("glGetIntegerv");
4418 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4419 glPixelStorei(GL_PACK_ROW_LENGTH, This->resource.width);
4420 checkGLcall("glPixelStorei");
4421 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4422 checkGLcall("glPixelStorei");
4423 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4424 checkGLcall("glPixelStorei");
4426 glReadPixels(local_rect.left, !srcIsUpsideDown ? (This->resource.height - local_rect.bottom) : local_rect.top,
4427 local_rect.right - local_rect.left,
4428 local_rect.bottom - local_rect.top,
4429 fmt, type, mem);
4430 checkGLcall("glReadPixels");
4432 /* Reset previous pixel store pack state */
4433 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4434 checkGLcall("glPixelStorei");
4435 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4436 checkGLcall("glPixelStorei");
4437 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4438 checkGLcall("glPixelStorei");
4440 if (This->flags & SFLAG_PBO)
4442 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4443 checkGLcall("glBindBufferARB");
4445 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4446 * to get a pointer to it and perform the flipping in software. This is a lot
4447 * faster than calling glReadPixels for each line. In case we want more speed
4448 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4449 if(!srcIsUpsideDown) {
4450 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
4451 checkGLcall("glBindBufferARB");
4453 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4454 checkGLcall("glMapBufferARB");
4458 /* TODO: Merge this with the palettization loop below for P8 targets */
4459 if(!srcIsUpsideDown) {
4460 UINT len, off;
4461 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4462 Flip the lines in software */
4463 len = (local_rect.right - local_rect.left) * bpp;
4464 off = local_rect.left * bpp;
4466 row = HeapAlloc(GetProcessHeap(), 0, len);
4467 if(!row) {
4468 ERR("Out of memory\n");
4469 if (This->resource.format->id == WINED3DFMT_P8_UINT) HeapFree(GetProcessHeap(), 0, mem);
4470 LEAVE_GL();
4471 return;
4474 top = mem + pitch * local_rect.top;
4475 bottom = mem + pitch * (local_rect.bottom - 1);
4476 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4477 memcpy(row, top + off, len);
4478 memcpy(top + off, bottom + off, len);
4479 memcpy(bottom + off, row, len);
4480 top += pitch;
4481 bottom -= pitch;
4483 HeapFree(GetProcessHeap(), 0, row);
4485 /* Unmap the temp PBO buffer */
4486 if (This->flags & SFLAG_PBO)
4488 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4489 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4493 LEAVE_GL();
4494 context_release(context);
4496 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
4497 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
4498 * the same color but we have no choice.
4499 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
4501 if (This->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4503 const PALETTEENTRY *pal = NULL;
4504 DWORD width = pitch / 3;
4505 int x, y, c;
4507 if(This->palette) {
4508 pal = This->palette->palents;
4509 } else {
4510 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4511 HeapFree(GetProcessHeap(), 0, mem);
4512 return ;
4515 for(y = local_rect.top; y < local_rect.bottom; y++) {
4516 for(x = local_rect.left; x < local_rect.right; x++) {
4517 /* start lines pixels */
4518 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4519 const BYTE *green = blue + 1;
4520 const BYTE *red = green + 1;
4522 for(c = 0; c < 256; c++) {
4523 if(*red == pal[c].peRed &&
4524 *green == pal[c].peGreen &&
4525 *blue == pal[c].peBlue)
4527 *((BYTE *) dest + y * width + x) = c;
4528 break;
4533 HeapFree(GetProcessHeap(), 0, mem);
4537 /* Read the framebuffer contents into a texture */
4538 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
4540 IWineD3DDeviceImpl *device = This->resource.device;
4541 const struct wined3d_gl_info *gl_info;
4542 struct wined3d_context *context;
4544 if (!surface_is_offscreen(This))
4546 /* We would need to flip onscreen surfaces, but there's no efficient
4547 * way to do that here. It makes more sense for the caller to
4548 * explicitly go through sysmem. */
4549 ERR("Not supported for onscreen targets.\n");
4550 return;
4553 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
4554 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
4555 * states in the stateblock, and no driver was found yet that had bugs in that regard.
4557 context = context_acquire(device, This);
4558 gl_info = context->gl_info;
4560 surface_prepare_texture(This, gl_info, srgb);
4561 surface_bind_and_dirtify(This, gl_info, srgb);
4563 TRACE("Reading back offscreen render target %p.\n", This);
4565 ENTER_GL();
4567 glReadBuffer(device->offscreenBuffer);
4568 checkGLcall("glReadBuffer");
4570 glCopyTexSubImage2D(This->texture_target, This->texture_level,
4571 0, 0, 0, 0, This->resource.width, This->resource.height);
4572 checkGLcall("glCopyTexSubImage2D");
4574 LEAVE_GL();
4576 context_release(context);
4579 /* Context activation is done by the caller. */
4580 static void surface_prepare_texture_internal(IWineD3DSurfaceImpl *surface,
4581 const struct wined3d_gl_info *gl_info, BOOL srgb)
4583 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4584 CONVERT_TYPES convert;
4585 struct wined3d_format format;
4587 if (surface->flags & alloc_flag) return;
4589 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4590 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4591 else surface->flags &= ~SFLAG_CONVERTED;
4593 surface_bind_and_dirtify(surface, gl_info, srgb);
4594 surface_allocate_surface(surface, gl_info, &format, srgb);
4595 surface->flags |= alloc_flag;
4598 /* Context activation is done by the caller. */
4599 void surface_prepare_texture(IWineD3DSurfaceImpl *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
4601 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4603 struct wined3d_texture *texture = surface->container.u.texture;
4604 UINT sub_count = texture->level_count * texture->layer_count;
4605 UINT i;
4607 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4609 for (i = 0; i < sub_count; ++i)
4611 IWineD3DSurfaceImpl *s = surface_from_resource(texture->sub_resources[i]);
4612 surface_prepare_texture_internal(s, gl_info, srgb);
4615 return;
4618 surface_prepare_texture_internal(surface, gl_info, srgb);
4621 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *surface,
4622 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4624 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *)surface); /* target is argb, 4 byte */
4625 IWineD3DDeviceImpl *device = surface->resource.device;
4626 const struct wined3d_gl_info *gl_info;
4627 struct wined3d_context *context;
4628 RECT local_rect;
4629 UINT w, h;
4631 surface_get_rect(surface, rect, &local_rect);
4633 mem += local_rect.top * pitch + local_rect.left * bpp;
4634 w = local_rect.right - local_rect.left;
4635 h = local_rect.bottom - local_rect.top;
4637 /* Activate the correct context for the render target */
4638 context = context_acquire(device, surface);
4639 context_apply_blit_state(context, device);
4640 gl_info = context->gl_info;
4642 ENTER_GL();
4644 if (!surface_is_offscreen(surface))
4646 GLenum buffer = surface_get_gl_buffer(surface);
4647 TRACE("Unlocking %#x buffer.\n", buffer);
4648 context_set_draw_buffer(context, buffer);
4650 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4651 glPixelZoom(1.0f, -1.0f);
4653 else
4655 /* Primary offscreen render target */
4656 TRACE("Offscreen render target.\n");
4657 context_set_draw_buffer(context, device->offscreenBuffer);
4659 glPixelZoom(1.0f, 1.0f);
4662 glRasterPos3i(local_rect.left, local_rect.top, 1);
4663 checkGLcall("glRasterPos3i");
4665 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4666 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4668 if (surface->flags & SFLAG_PBO)
4670 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4671 checkGLcall("glBindBufferARB");
4674 glDrawPixels(w, h, fmt, type, mem);
4675 checkGLcall("glDrawPixels");
4677 if (surface->flags & SFLAG_PBO)
4679 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4680 checkGLcall("glBindBufferARB");
4683 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4684 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4686 LEAVE_GL();
4688 if (wined3d_settings.strict_draw_ordering
4689 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4690 && surface->container.u.swapchain->front_buffer == surface))
4691 wglFlush();
4693 context_release(context);
4696 /* ******************************************************
4697 IWineD3DSurface Internal (No mapping to directx api) parts follow
4698 ****************************************************** */
4700 HRESULT d3dfmt_get_conv(IWineD3DSurfaceImpl *This, BOOL need_alpha_ck,
4701 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4703 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
4704 IWineD3DDeviceImpl *device = This->resource.device;
4705 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4706 BOOL blit_supported = FALSE;
4708 /* Copy the default values from the surface. Below we might perform fixups */
4709 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4710 *format = *This->resource.format;
4711 *convert = NO_CONVERSION;
4713 /* Ok, now look if we have to do any conversion */
4714 switch (This->resource.format->id)
4716 case WINED3DFMT_P8_UINT:
4717 /* Below the call to blit_supported is disabled for Wine 1.2
4718 * because the function isn't operating correctly yet. At the
4719 * moment 8-bit blits are handled in software and if certain GL
4720 * extensions are around, surface conversion is performed at
4721 * upload time. The blit_supported call recognizes it as a
4722 * destination fixup. This type of upload 'fixup' and 8-bit to
4723 * 8-bit blits need to be handled by the blit_shader.
4724 * TODO: get rid of this #if 0. */
4725 #if 0
4726 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4727 &rect, This->resource.usage, This->resource.pool, This->resource.format,
4728 &rect, This->resource.usage, This->resource.pool, This->resource.format);
4729 #endif
4730 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4732 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4733 * texturing. Further also use conversion in case of color keying.
4734 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4735 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4736 * conflicts with this.
4738 if (!((blit_supported && device->render_targets && This == device->render_targets[0]))
4739 || colorkey_active || !use_texturing)
4741 format->glFormat = GL_RGBA;
4742 format->glInternal = GL_RGBA;
4743 format->glType = GL_UNSIGNED_BYTE;
4744 format->conv_byte_count = 4;
4745 if (colorkey_active)
4746 *convert = CONVERT_PALETTED_CK;
4747 else
4748 *convert = CONVERT_PALETTED;
4750 break;
4752 case WINED3DFMT_B2G3R3_UNORM:
4753 /* **********************
4754 GL_UNSIGNED_BYTE_3_3_2
4755 ********************** */
4756 if (colorkey_active) {
4757 /* This texture format will never be used.. So do not care about color keying
4758 up until the point in time it will be needed :-) */
4759 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4761 break;
4763 case WINED3DFMT_B5G6R5_UNORM:
4764 if (colorkey_active)
4766 *convert = CONVERT_CK_565;
4767 format->glFormat = GL_RGBA;
4768 format->glInternal = GL_RGB5_A1;
4769 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4770 format->conv_byte_count = 2;
4772 break;
4774 case WINED3DFMT_B5G5R5X1_UNORM:
4775 if (colorkey_active)
4777 *convert = CONVERT_CK_5551;
4778 format->glFormat = GL_BGRA;
4779 format->glInternal = GL_RGB5_A1;
4780 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4781 format->conv_byte_count = 2;
4783 break;
4785 case WINED3DFMT_B8G8R8_UNORM:
4786 if (colorkey_active)
4788 *convert = CONVERT_CK_RGB24;
4789 format->glFormat = GL_RGBA;
4790 format->glInternal = GL_RGBA8;
4791 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4792 format->conv_byte_count = 4;
4794 break;
4796 case WINED3DFMT_B8G8R8X8_UNORM:
4797 if (colorkey_active)
4799 *convert = CONVERT_RGB32_888;
4800 format->glFormat = GL_RGBA;
4801 format->glInternal = GL_RGBA8;
4802 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4803 format->conv_byte_count = 4;
4805 break;
4807 default:
4808 break;
4811 return WINED3D_OK;
4814 void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey)
4816 IWineD3DDeviceImpl *device = This->resource.device;
4817 struct wined3d_palette *pal = This->palette;
4818 BOOL index_in_alpha = FALSE;
4819 unsigned int i;
4821 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4822 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4823 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4824 * duplicate entries. Store the color key in the unused alpha component to speed the
4825 * download up and to make conversion unneeded. */
4826 index_in_alpha = primary_render_target_is_p8(device);
4828 if (!pal)
4830 UINT dxVersion = device->wined3d->dxVersion;
4832 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
4833 if (dxVersion <= 7)
4835 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4836 if (index_in_alpha)
4838 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4839 * there's no palette at this time. */
4840 for (i = 0; i < 256; i++) table[i][3] = i;
4843 else
4845 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
4846 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
4847 * capability flag is present (wine does advertise this capability) */
4848 for (i = 0; i < 256; ++i)
4850 table[i][0] = device->palettes[device->currentPalette][i].peRed;
4851 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
4852 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
4853 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
4857 else
4859 TRACE("Using surface palette %p\n", pal);
4860 /* Get the surface's palette */
4861 for (i = 0; i < 256; ++i)
4863 table[i][0] = pal->palents[i].peRed;
4864 table[i][1] = pal->palents[i].peGreen;
4865 table[i][2] = pal->palents[i].peBlue;
4867 /* When index_in_alpha is set the palette index is stored in the
4868 * alpha component. In case of a readback we can then read
4869 * GL_ALPHA. Color keying is handled in BltOverride using a
4870 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4871 * color key itself is passed to glAlphaFunc in other cases the
4872 * alpha component of pixels that should be masked away is set to 0. */
4873 if (index_in_alpha)
4875 table[i][3] = i;
4877 else if (colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue)
4878 && (i <= This->SrcBltCKey.dwColorSpaceHighValue))
4880 table[i][3] = 0x00;
4882 else if (pal->flags & WINEDDPCAPS_ALPHA)
4884 table[i][3] = pal->palents[i].peFlags;
4886 else
4888 table[i][3] = 0xFF;
4894 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4895 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
4897 const BYTE *source;
4898 BYTE *dest;
4899 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
4901 switch (convert) {
4902 case NO_CONVERSION:
4904 memcpy(dst, src, pitch * height);
4905 break;
4907 case CONVERT_PALETTED:
4908 case CONVERT_PALETTED_CK:
4910 BYTE table[256][4];
4911 unsigned int x, y;
4913 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
4915 for (y = 0; y < height; y++)
4917 source = src + pitch * y;
4918 dest = dst + outpitch * y;
4919 /* This is an 1 bpp format, using the width here is fine */
4920 for (x = 0; x < width; x++) {
4921 BYTE color = *source++;
4922 *dest++ = table[color][0];
4923 *dest++ = table[color][1];
4924 *dest++ = table[color][2];
4925 *dest++ = table[color][3];
4929 break;
4931 case CONVERT_CK_565:
4933 /* Converting the 565 format in 5551 packed to emulate color-keying.
4935 Note : in all these conversion, it would be best to average the averaging
4936 pixels to get the color of the pixel that will be color-keyed to
4937 prevent 'color bleeding'. This will be done later on if ever it is
4938 too visible.
4940 Note2: Nvidia documents say that their driver does not support alpha + color keying
4941 on the same surface and disables color keying in such a case
4943 unsigned int x, y;
4944 const WORD *Source;
4945 WORD *Dest;
4947 TRACE("Color keyed 565\n");
4949 for (y = 0; y < height; y++) {
4950 Source = (const WORD *)(src + y * pitch);
4951 Dest = (WORD *) (dst + y * outpitch);
4952 for (x = 0; x < width; x++ ) {
4953 WORD color = *Source++;
4954 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4955 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
4956 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
4957 *Dest |= 0x0001;
4959 Dest++;
4963 break;
4965 case CONVERT_CK_5551:
4967 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4968 unsigned int x, y;
4969 const WORD *Source;
4970 WORD *Dest;
4971 TRACE("Color keyed 5551\n");
4972 for (y = 0; y < height; y++) {
4973 Source = (const WORD *)(src + y * pitch);
4974 Dest = (WORD *) (dst + y * outpitch);
4975 for (x = 0; x < width; x++ ) {
4976 WORD color = *Source++;
4977 *Dest = color;
4978 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
4979 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
4980 *Dest |= (1 << 15);
4982 else {
4983 *Dest &= ~(1 << 15);
4985 Dest++;
4989 break;
4991 case CONVERT_CK_RGB24:
4993 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4994 unsigned int x, y;
4995 for (y = 0; y < height; y++)
4997 source = src + pitch * y;
4998 dest = dst + outpitch * y;
4999 for (x = 0; x < width; x++) {
5000 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
5001 DWORD dstcolor = color << 8;
5002 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
5003 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
5004 dstcolor |= 0xff;
5006 *(DWORD*)dest = dstcolor;
5007 source += 3;
5008 dest += 4;
5012 break;
5014 case CONVERT_RGB32_888:
5016 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
5017 unsigned int x, y;
5018 for (y = 0; y < height; y++)
5020 source = src + pitch * y;
5021 dest = dst + outpitch * y;
5022 for (x = 0; x < width; x++) {
5023 DWORD color = 0xffffff & *(const DWORD*)source;
5024 DWORD dstcolor = color << 8;
5025 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
5026 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
5027 dstcolor |= 0xff;
5029 *(DWORD*)dest = dstcolor;
5030 source += 4;
5031 dest += 4;
5035 break;
5037 default:
5038 ERR("Unsupported conversion type %#x.\n", convert);
5040 return WINED3D_OK;
5043 BOOL palette9_changed(IWineD3DSurfaceImpl *This)
5045 IWineD3DDeviceImpl *device = This->resource.device;
5047 if (This->palette || (This->resource.format->id != WINED3DFMT_P8_UINT
5048 && This->resource.format->id != WINED3DFMT_P8_UINT_A8_UNORM))
5050 /* If a ddraw-style palette is attached assume no d3d9 palette change.
5051 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
5053 return FALSE;
5056 if (This->palette9)
5058 if (!memcmp(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
5060 return FALSE;
5062 } else {
5063 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
5065 memcpy(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
5066 return TRUE;
5069 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
5071 /* Flip the surface contents */
5072 /* Flip the DC */
5074 HDC tmp;
5075 tmp = front->hDC;
5076 front->hDC = back->hDC;
5077 back->hDC = tmp;
5080 /* Flip the DIBsection */
5082 HBITMAP tmp;
5083 BOOL hasDib = front->flags & SFLAG_DIBSECTION;
5084 tmp = front->dib.DIBsection;
5085 front->dib.DIBsection = back->dib.DIBsection;
5086 back->dib.DIBsection = tmp;
5088 if (back->flags & SFLAG_DIBSECTION) front->flags |= SFLAG_DIBSECTION;
5089 else front->flags &= ~SFLAG_DIBSECTION;
5090 if (hasDib) back->flags |= SFLAG_DIBSECTION;
5091 else back->flags &= ~SFLAG_DIBSECTION;
5094 /* Flip the surface data */
5096 void* tmp;
5098 tmp = front->dib.bitmap_data;
5099 front->dib.bitmap_data = back->dib.bitmap_data;
5100 back->dib.bitmap_data = tmp;
5102 tmp = front->resource.allocatedMemory;
5103 front->resource.allocatedMemory = back->resource.allocatedMemory;
5104 back->resource.allocatedMemory = tmp;
5106 tmp = front->resource.heapMemory;
5107 front->resource.heapMemory = back->resource.heapMemory;
5108 back->resource.heapMemory = tmp;
5111 /* Flip the PBO */
5113 GLuint tmp_pbo = front->pbo;
5114 front->pbo = back->pbo;
5115 back->pbo = tmp_pbo;
5118 /* client_memory should not be different, but just in case */
5120 BOOL tmp;
5121 tmp = front->dib.client_memory;
5122 front->dib.client_memory = back->dib.client_memory;
5123 back->dib.client_memory = tmp;
5126 /* Flip the opengl texture */
5128 GLuint tmp;
5130 tmp = back->texture_name;
5131 back->texture_name = front->texture_name;
5132 front->texture_name = tmp;
5134 tmp = back->texture_name_srgb;
5135 back->texture_name_srgb = front->texture_name_srgb;
5136 front->texture_name_srgb = tmp;
5138 resource_unload(&back->resource);
5139 resource_unload(&front->resource);
5143 DWORD tmp_flags = back->flags;
5144 back->flags = front->flags;
5145 front->flags = tmp_flags;
5149 /* Does a direct frame buffer -> texture copy. Stretching is done
5150 * with single pixel copy calls
5152 static void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *dst_surface, IWineD3DSurfaceImpl *src_surface,
5153 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
5155 IWineD3DDeviceImpl *device = dst_surface->resource.device;
5156 float xrel, yrel;
5157 UINT row;
5158 struct wined3d_context *context;
5159 BOOL upsidedown = FALSE;
5160 RECT dst_rect = *dst_rect_in;
5162 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
5163 * glCopyTexSubImage is a bit picky about the parameters we pass to it
5165 if(dst_rect.top > dst_rect.bottom) {
5166 UINT tmp = dst_rect.bottom;
5167 dst_rect.bottom = dst_rect.top;
5168 dst_rect.top = tmp;
5169 upsidedown = TRUE;
5172 context = context_acquire(device, src_surface);
5173 context_apply_blit_state(context, device);
5174 surface_internal_preload(dst_surface, SRGB_RGB);
5175 ENTER_GL();
5177 /* Bind the target texture */
5178 glBindTexture(dst_surface->texture_target, dst_surface->texture_name);
5179 checkGLcall("glBindTexture");
5180 if (surface_is_offscreen(src_surface))
5182 TRACE("Reading from an offscreen target\n");
5183 upsidedown = !upsidedown;
5184 glReadBuffer(device->offscreenBuffer);
5186 else
5188 glReadBuffer(surface_get_gl_buffer(src_surface));
5190 checkGLcall("glReadBuffer");
5192 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
5193 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
5195 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
5197 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
5199 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
5200 ERR("Texture filtering not supported in direct blit\n");
5203 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
5204 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
5206 ERR("Texture filtering not supported in direct blit\n");
5209 if (upsidedown
5210 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
5211 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
5213 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
5215 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
5216 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
5217 src_rect->left, src_surface->resource.height - src_rect->bottom,
5218 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5220 else
5222 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
5223 /* I have to process this row by row to swap the image,
5224 * otherwise it would be upside down, so stretching in y direction
5225 * doesn't cost extra time
5227 * However, stretching in x direction can be avoided if not necessary
5229 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
5230 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
5232 /* Well, that stuff works, but it's very slow.
5233 * find a better way instead
5235 UINT col;
5237 for (col = dst_rect.left; col < dst_rect.right; ++col)
5239 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
5240 dst_rect.left + col /* x offset */, row /* y offset */,
5241 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
5244 else
5246 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
5247 dst_rect.left /* x offset */, row /* y offset */,
5248 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
5252 checkGLcall("glCopyTexSubImage2D");
5254 LEAVE_GL();
5255 context_release(context);
5257 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5258 * path is never entered
5260 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5263 /* Uses the hardware to stretch and flip the image */
5264 static void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *dst_surface, IWineD3DSurfaceImpl *src_surface,
5265 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
5267 IWineD3DDeviceImpl *device = dst_surface->resource.device;
5268 struct wined3d_swapchain *src_swapchain = NULL;
5269 GLuint src, backup = 0;
5270 float left, right, top, bottom; /* Texture coordinates */
5271 UINT fbwidth = src_surface->resource.width;
5272 UINT fbheight = src_surface->resource.height;
5273 struct wined3d_context *context;
5274 GLenum drawBuffer = GL_BACK;
5275 GLenum texture_target;
5276 BOOL noBackBufferBackup;
5277 BOOL src_offscreen;
5278 BOOL upsidedown = FALSE;
5279 RECT dst_rect = *dst_rect_in;
5281 TRACE("Using hwstretch blit\n");
5282 /* Activate the Proper context for reading from the source surface, set it up for blitting */
5283 context = context_acquire(device, src_surface);
5284 context_apply_blit_state(context, device);
5285 surface_internal_preload(dst_surface, SRGB_RGB);
5287 src_offscreen = surface_is_offscreen(src_surface);
5288 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
5289 if (!noBackBufferBackup && !src_surface->texture_name)
5291 /* Get it a description */
5292 surface_internal_preload(src_surface, SRGB_RGB);
5294 ENTER_GL();
5296 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
5297 * This way we don't have to wait for the 2nd readback to finish to leave this function.
5299 if (context->aux_buffers >= 2)
5301 /* Got more than one aux buffer? Use the 2nd aux buffer */
5302 drawBuffer = GL_AUX1;
5304 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
5306 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
5307 drawBuffer = GL_AUX0;
5310 if(noBackBufferBackup) {
5311 glGenTextures(1, &backup);
5312 checkGLcall("glGenTextures");
5313 glBindTexture(GL_TEXTURE_2D, backup);
5314 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
5315 texture_target = GL_TEXTURE_2D;
5316 } else {
5317 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
5318 * we are reading from the back buffer, the backup can be used as source texture
5320 texture_target = src_surface->texture_target;
5321 glBindTexture(texture_target, src_surface->texture_name);
5322 checkGLcall("glBindTexture(texture_target, src_surface->texture_name)");
5323 glEnable(texture_target);
5324 checkGLcall("glEnable(texture_target)");
5326 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
5327 src_surface->flags &= ~SFLAG_INTEXTURE;
5330 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
5331 * glCopyTexSubImage is a bit picky about the parameters we pass to it
5333 if(dst_rect.top > dst_rect.bottom) {
5334 UINT tmp = dst_rect.bottom;
5335 dst_rect.bottom = dst_rect.top;
5336 dst_rect.top = tmp;
5337 upsidedown = TRUE;
5340 if (src_offscreen)
5342 TRACE("Reading from an offscreen target\n");
5343 upsidedown = !upsidedown;
5344 glReadBuffer(device->offscreenBuffer);
5346 else
5348 glReadBuffer(surface_get_gl_buffer(src_surface));
5351 /* TODO: Only back up the part that will be overwritten */
5352 glCopyTexSubImage2D(texture_target, 0,
5353 0, 0 /* read offsets */,
5354 0, 0,
5355 fbwidth,
5356 fbheight);
5358 checkGLcall("glCopyTexSubImage2D");
5360 /* No issue with overriding these - the sampler is dirty due to blit usage */
5361 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
5362 wined3d_gl_mag_filter(magLookup, Filter));
5363 checkGLcall("glTexParameteri");
5364 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
5365 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
5366 checkGLcall("glTexParameteri");
5368 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5369 src_swapchain = src_surface->container.u.swapchain;
5370 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
5372 src = backup ? backup : src_surface->texture_name;
5374 else
5376 glReadBuffer(GL_FRONT);
5377 checkGLcall("glReadBuffer(GL_FRONT)");
5379 glGenTextures(1, &src);
5380 checkGLcall("glGenTextures(1, &src)");
5381 glBindTexture(GL_TEXTURE_2D, src);
5382 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
5384 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5385 * out for power of 2 sizes
5387 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5388 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5389 checkGLcall("glTexImage2D");
5390 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5391 0, 0 /* read offsets */,
5392 0, 0,
5393 fbwidth,
5394 fbheight);
5396 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5397 checkGLcall("glTexParameteri");
5398 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5399 checkGLcall("glTexParameteri");
5401 glReadBuffer(GL_BACK);
5402 checkGLcall("glReadBuffer(GL_BACK)");
5404 if(texture_target != GL_TEXTURE_2D) {
5405 glDisable(texture_target);
5406 glEnable(GL_TEXTURE_2D);
5407 texture_target = GL_TEXTURE_2D;
5410 checkGLcall("glEnd and previous");
5412 left = src_rect->left;
5413 right = src_rect->right;
5415 if (!upsidedown)
5417 top = src_surface->resource.height - src_rect->top;
5418 bottom = src_surface->resource.height - src_rect->bottom;
5420 else
5422 top = src_surface->resource.height - src_rect->bottom;
5423 bottom = src_surface->resource.height - src_rect->top;
5426 if (src_surface->flags & SFLAG_NORMCOORD)
5428 left /= src_surface->pow2Width;
5429 right /= src_surface->pow2Width;
5430 top /= src_surface->pow2Height;
5431 bottom /= src_surface->pow2Height;
5434 /* draw the source texture stretched and upside down. The correct surface is bound already */
5435 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5436 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5438 context_set_draw_buffer(context, drawBuffer);
5439 glReadBuffer(drawBuffer);
5441 glBegin(GL_QUADS);
5442 /* bottom left */
5443 glTexCoord2f(left, bottom);
5444 glVertex2i(0, 0);
5446 /* top left */
5447 glTexCoord2f(left, top);
5448 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5450 /* top right */
5451 glTexCoord2f(right, top);
5452 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5454 /* bottom right */
5455 glTexCoord2f(right, bottom);
5456 glVertex2i(dst_rect.right - dst_rect.left, 0);
5457 glEnd();
5458 checkGLcall("glEnd and previous");
5460 if (texture_target != dst_surface->texture_target)
5462 glDisable(texture_target);
5463 glEnable(dst_surface->texture_target);
5464 texture_target = dst_surface->texture_target;
5467 /* Now read the stretched and upside down image into the destination texture */
5468 glBindTexture(texture_target, dst_surface->texture_name);
5469 checkGLcall("glBindTexture");
5470 glCopyTexSubImage2D(texture_target,
5472 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5473 0, 0, /* We blitted the image to the origin */
5474 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5475 checkGLcall("glCopyTexSubImage2D");
5477 if(drawBuffer == GL_BACK) {
5478 /* Write the back buffer backup back */
5479 if(backup) {
5480 if(texture_target != GL_TEXTURE_2D) {
5481 glDisable(texture_target);
5482 glEnable(GL_TEXTURE_2D);
5483 texture_target = GL_TEXTURE_2D;
5485 glBindTexture(GL_TEXTURE_2D, backup);
5486 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
5488 else
5490 if (texture_target != src_surface->texture_target)
5492 glDisable(texture_target);
5493 glEnable(src_surface->texture_target);
5494 texture_target = src_surface->texture_target;
5496 glBindTexture(src_surface->texture_target, src_surface->texture_name);
5497 checkGLcall("glBindTexture(src_surface->texture_target, src_surface->texture_name)");
5500 glBegin(GL_QUADS);
5501 /* top left */
5502 glTexCoord2f(0.0f, 0.0f);
5503 glVertex2i(0, fbheight);
5505 /* bottom left */
5506 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5507 glVertex2i(0, 0);
5509 /* bottom right */
5510 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5511 (float)fbheight / (float)src_surface->pow2Height);
5512 glVertex2i(fbwidth, 0);
5514 /* top right */
5515 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5516 glVertex2i(fbwidth, fbheight);
5517 glEnd();
5519 glDisable(texture_target);
5520 checkGLcall("glDisable(texture_target)");
5522 /* Cleanup */
5523 if (src != src_surface->texture_name && src != backup)
5525 glDeleteTextures(1, &src);
5526 checkGLcall("glDeleteTextures(1, &src)");
5528 if(backup) {
5529 glDeleteTextures(1, &backup);
5530 checkGLcall("glDeleteTextures(1, &backup)");
5533 LEAVE_GL();
5535 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5537 context_release(context);
5539 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5540 * path is never entered
5542 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5545 /* Until the blit_shader is ready, define some prototypes here. */
5546 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
5547 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
5548 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format);
5550 /* Front buffer coordinates are always full screen coordinates, but our GL
5551 * drawable is limited to the window's client area. The sysmem and texture
5552 * copies do have the full screen size. Note that GL has a bottom-left
5553 * origin, while D3D has a top-left origin. */
5554 void surface_translate_drawable_coords(IWineD3DSurfaceImpl *surface, HWND window, RECT *rect)
5556 UINT drawable_height;
5558 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5559 && surface == surface->container.u.swapchain->front_buffer)
5561 POINT offset = {0, 0};
5562 RECT windowsize;
5564 ScreenToClient(window, &offset);
5565 OffsetRect(rect, offset.x, offset.y);
5567 GetClientRect(window, &windowsize);
5568 drawable_height = windowsize.bottom - windowsize.top;
5570 else
5572 drawable_height = surface->resource.height;
5575 rect->top = drawable_height - rect->top;
5576 rect->bottom = drawable_height - rect->bottom;
5579 static BOOL surface_is_full_rect(IWineD3DSurfaceImpl *surface, const RECT *r)
5581 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
5582 return FALSE;
5583 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
5584 return FALSE;
5585 return TRUE;
5588 /* blit between surface locations. onscreen on different swapchains is not supported.
5589 * depth / stencil is not supported. */
5590 static void surface_blt_fbo(IWineD3DDeviceImpl *device, const WINED3DTEXTUREFILTERTYPE filter,
5591 IWineD3DSurfaceImpl *src_surface, DWORD src_location, const RECT *src_rect_in,
5592 IWineD3DSurfaceImpl *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
5594 const struct wined3d_gl_info *gl_info;
5595 struct wined3d_context *context;
5596 RECT src_rect, dst_rect;
5597 GLenum gl_filter;
5599 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
5600 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
5601 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
5602 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
5603 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
5605 src_rect = *src_rect_in;
5606 dst_rect = *dst_rect_in;
5608 switch (filter)
5610 case WINED3DTEXF_LINEAR:
5611 gl_filter = GL_LINEAR;
5612 break;
5614 default:
5615 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
5616 case WINED3DTEXF_NONE:
5617 case WINED3DTEXF_POINT:
5618 gl_filter = GL_NEAREST;
5619 break;
5622 if (src_location == SFLAG_INDRAWABLE && surface_is_offscreen(src_surface))
5623 src_location = SFLAG_INTEXTURE;
5624 if (dst_location == SFLAG_INDRAWABLE && surface_is_offscreen(dst_surface))
5625 dst_location = SFLAG_INTEXTURE;
5627 /* Make sure the locations are up-to-date. Loading the destination
5628 * surface isn't required if the entire surface is overwritten. (And is
5629 * in fact harmful if we're being called by surface_load_location() with
5630 * the purpose of loading the destination surface.) */
5631 surface_load_location(src_surface, src_location, NULL);
5632 if (!surface_is_full_rect(dst_surface, &dst_rect))
5633 surface_load_location(dst_surface, dst_location, NULL);
5635 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
5636 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
5637 else context = context_acquire(device, NULL);
5639 if (!context->valid)
5641 context_release(context);
5642 WARN("Invalid context, skipping blit.\n");
5643 return;
5646 gl_info = context->gl_info;
5648 if (src_location == SFLAG_INDRAWABLE)
5650 GLenum buffer = surface_get_gl_buffer(src_surface);
5652 TRACE("Source surface %p is onscreen.\n", src_surface);
5654 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
5656 ENTER_GL();
5657 context_bind_fbo(context, GL_READ_FRAMEBUFFER, NULL);
5658 glReadBuffer(buffer);
5659 checkGLcall("glReadBuffer()");
5661 else
5663 TRACE("Source surface %p is offscreen.\n", src_surface);
5664 ENTER_GL();
5665 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
5666 glReadBuffer(GL_COLOR_ATTACHMENT0);
5667 checkGLcall("glReadBuffer()");
5669 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
5670 LEAVE_GL();
5672 if (dst_location == SFLAG_INDRAWABLE)
5674 GLenum buffer = surface_get_gl_buffer(dst_surface);
5676 TRACE("Destination surface %p is onscreen.\n", dst_surface);
5678 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5680 ENTER_GL();
5681 context_bind_fbo(context, GL_DRAW_FRAMEBUFFER, NULL);
5682 context_set_draw_buffer(context, buffer);
5684 else
5686 TRACE("Destination surface %p is offscreen.\n", dst_surface);
5688 ENTER_GL();
5689 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
5690 context_set_draw_buffer(context, GL_COLOR_ATTACHMENT0);
5692 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
5694 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
5695 IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_COLORWRITEENABLE));
5696 IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_COLORWRITEENABLE1));
5697 IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_COLORWRITEENABLE2));
5698 IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_COLORWRITEENABLE3));
5700 glDisable(GL_SCISSOR_TEST);
5701 IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
5703 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
5704 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
5705 checkGLcall("glBlitFramebuffer()");
5707 LEAVE_GL();
5709 if (wined3d_settings.strict_draw_ordering
5710 || (dst_location == SFLAG_INDRAWABLE
5711 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
5712 wglFlush();
5714 context_release(context);
5717 static void wined3d_surface_depth_blt_fbo(IWineD3DDeviceImpl *device, IWineD3DSurfaceImpl *src_surface,
5718 const RECT *src_rect, IWineD3DSurfaceImpl *dst_surface, const RECT *dst_rect)
5720 const struct wined3d_gl_info *gl_info;
5721 struct wined3d_context *context;
5722 DWORD src_mask, dst_mask;
5723 GLbitfield gl_mask;
5725 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
5726 device, src_surface, wine_dbgstr_rect(src_rect),
5727 dst_surface, wine_dbgstr_rect(dst_rect));
5729 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5730 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5732 if (src_mask != dst_mask)
5734 ERR("Incompatible formats %s and %s.\n",
5735 debug_d3dformat(src_surface->resource.format->id),
5736 debug_d3dformat(dst_surface->resource.format->id));
5737 return;
5740 if (!src_mask)
5742 ERR("Not a depth / stencil format: %s.\n",
5743 debug_d3dformat(src_surface->resource.format->id));
5744 return;
5747 gl_mask = 0;
5748 if (src_mask & WINED3DFMT_FLAG_DEPTH)
5749 gl_mask |= GL_DEPTH_BUFFER_BIT;
5750 if (src_mask & WINED3DFMT_FLAG_STENCIL)
5751 gl_mask |= GL_STENCIL_BUFFER_BIT;
5753 /* Make sure the locations are up-to-date. Loading the destination
5754 * surface isn't required if the entire surface is overwritten. */
5755 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
5756 if (!surface_is_full_rect(dst_surface, dst_rect))
5757 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
5759 context = context_acquire(device, NULL);
5760 if (!context->valid)
5762 context_release(context);
5763 WARN("Invalid context, skipping blit.\n");
5764 return;
5767 gl_info = context->gl_info;
5769 ENTER_GL();
5771 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
5772 glReadBuffer(GL_NONE);
5773 checkGLcall("glReadBuffer()");
5774 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
5776 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
5777 context_set_draw_buffer(context, GL_NONE);
5778 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
5780 if (gl_mask & GL_DEPTH_BUFFER_BIT)
5782 glDepthMask(GL_TRUE);
5783 IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_ZWRITEENABLE));
5785 if (gl_mask & GL_STENCIL_BUFFER_BIT)
5787 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
5789 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
5790 IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_TWOSIDEDSTENCILMODE));
5792 glStencilMask(~0U);
5793 IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_STENCILWRITEMASK));
5796 glDisable(GL_SCISSOR_TEST);
5797 IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
5799 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
5800 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
5801 checkGLcall("glBlitFramebuffer()");
5803 LEAVE_GL();
5805 if (wined3d_settings.strict_draw_ordering)
5806 wglFlush(); /* Flush to ensure ordering across contexts. */
5808 context_release(context);
5811 static void surface_blt_to_drawable(IWineD3DDeviceImpl *device,
5812 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5813 IWineD3DSurfaceImpl *src_surface, const RECT *src_rect_in,
5814 IWineD3DSurfaceImpl *dst_surface, const RECT *dst_rect_in)
5816 struct wined3d_context *context;
5817 RECT src_rect, dst_rect;
5819 src_rect = *src_rect_in;
5820 dst_rect = *dst_rect_in;
5822 /* Make sure the surface is up-to-date. This should probably use
5823 * surface_load_location() and worry about the destination surface too,
5824 * unless we're overwriting it completely. */
5825 surface_internal_preload(src_surface, SRGB_RGB);
5827 /* Activate the destination context, set it up for blitting */
5828 context = context_acquire(device, dst_surface);
5829 context_apply_blit_state(context, device);
5831 if (!surface_is_offscreen(dst_surface))
5832 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5834 device->blitter->set_shader(device->blit_priv, context->gl_info, src_surface);
5836 ENTER_GL();
5838 if (color_key)
5840 glEnable(GL_ALPHA_TEST);
5841 checkGLcall("glEnable(GL_ALPHA_TEST)");
5843 /* When the primary render target uses P8, the alpha component
5844 * contains the palette index. Which means that the colorkey is one of
5845 * the palette entries. In other cases pixels that should be masked
5846 * away have alpha set to 0. */
5847 if (primary_render_target_is_p8(device))
5848 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
5849 else
5850 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5851 checkGLcall("glAlphaFunc");
5853 else
5855 glDisable(GL_ALPHA_TEST);
5856 checkGLcall("glDisable(GL_ALPHA_TEST)");
5859 draw_textured_quad(src_surface, &src_rect, &dst_rect, filter);
5861 if (color_key)
5863 glDisable(GL_ALPHA_TEST);
5864 checkGLcall("glDisable(GL_ALPHA_TEST)");
5867 LEAVE_GL();
5869 /* Leave the opengl state valid for blitting */
5870 device->blitter->unset_shader(context->gl_info);
5872 if (wined3d_settings.strict_draw_ordering
5873 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5874 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5875 wglFlush(); /* Flush to ensure ordering across contexts. */
5877 context_release(context);
5880 /* Do not call while under the GL lock. */
5881 HRESULT surface_color_fill(IWineD3DSurfaceImpl *s, const RECT *rect, const WINED3DCOLORVALUE *color)
5883 IWineD3DDeviceImpl *device = s->resource.device;
5884 const struct blit_shader *blitter;
5886 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5887 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5888 if (!blitter)
5890 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5891 return WINED3DERR_INVALIDCALL;
5894 return blitter->color_fill(device, s, rect, color);
5897 /* Not called from the VTable */
5898 /* Do not call while under the GL lock. */
5899 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *dst_surface, const RECT *DestRect,
5900 IWineD3DSurfaceImpl *src_surface, const RECT *SrcRect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5901 WINED3DTEXTUREFILTERTYPE Filter)
5903 IWineD3DDeviceImpl *device = dst_surface->resource.device;
5904 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5905 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5906 RECT dst_rect, src_rect;
5908 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5909 dst_surface, wine_dbgstr_rect(DestRect), src_surface, wine_dbgstr_rect(SrcRect),
5910 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5912 /* Get the swapchain. One of the surfaces has to be a primary surface */
5913 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5915 WARN("Destination is in sysmem, rejecting gl blt\n");
5916 return WINED3DERR_INVALIDCALL;
5919 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5920 dstSwapchain = dst_surface->container.u.swapchain;
5922 if (src_surface)
5924 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5926 WARN("Src is in sysmem, rejecting gl blt\n");
5927 return WINED3DERR_INVALIDCALL;
5930 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5931 srcSwapchain = src_surface->container.u.swapchain;
5934 /* Early sort out of cases where no render target is used */
5935 if (!dstSwapchain && !srcSwapchain
5936 && src_surface != device->render_targets[0]
5937 && dst_surface != device->render_targets[0])
5939 TRACE("No surface is render target, not using hardware blit.\n");
5940 return WINED3DERR_INVALIDCALL;
5943 /* No destination color keying supported */
5944 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5946 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5947 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5948 return WINED3DERR_INVALIDCALL;
5951 surface_get_rect(dst_surface, DestRect, &dst_rect);
5952 if (src_surface) surface_get_rect(src_surface, SrcRect, &src_rect);
5954 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
5955 if (dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->back_buffers
5956 && dst_surface == dstSwapchain->front_buffer
5957 && src_surface == dstSwapchain->back_buffers[0])
5959 /* Half-Life does a Blt from the back buffer to the front buffer,
5960 * Full surface size, no flags... Use present instead
5962 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
5965 /* Check rects - IWineD3DDevice_Present doesn't handle them */
5966 while(1)
5968 TRACE("Looking if a Present can be done...\n");
5969 /* Source Rectangle must be full surface */
5970 if (src_rect.left || src_rect.top
5971 || src_rect.right != src_surface->resource.width
5972 || src_rect.bottom != src_surface->resource.height)
5974 TRACE("No, Source rectangle doesn't match\n");
5975 break;
5978 /* No stretching may occur */
5979 if(src_rect.right != dst_rect.right - dst_rect.left ||
5980 src_rect.bottom != dst_rect.bottom - dst_rect.top) {
5981 TRACE("No, stretching is done\n");
5982 break;
5985 /* Destination must be full surface or match the clipping rectangle */
5986 if (dst_surface->clipper && dst_surface->clipper->hWnd)
5988 RECT cliprect;
5989 POINT pos[2];
5990 GetClientRect(dst_surface->clipper->hWnd, &cliprect);
5991 pos[0].x = dst_rect.left;
5992 pos[0].y = dst_rect.top;
5993 pos[1].x = dst_rect.right;
5994 pos[1].y = dst_rect.bottom;
5995 MapWindowPoints(GetDesktopWindow(), dst_surface->clipper->hWnd, pos, 2);
5997 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
5998 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
6000 TRACE("No, dest rectangle doesn't match(clipper)\n");
6001 TRACE("Clip rect at %s\n", wine_dbgstr_rect(&cliprect));
6002 TRACE("Blt dest: %s\n", wine_dbgstr_rect(&dst_rect));
6003 break;
6006 else if (dst_rect.left || dst_rect.top
6007 || dst_rect.right != dst_surface->resource.width
6008 || dst_rect.bottom != dst_surface->resource.height)
6010 TRACE("No, dest rectangle doesn't match(surface size)\n");
6011 break;
6014 TRACE("Yes\n");
6016 /* These flags are unimportant for the flag check, remove them */
6017 if (!(flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)))
6019 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
6021 /* The idea behind this is that a glReadPixels and a glDrawPixels call
6022 * take very long, while a flip is fast.
6023 * This applies to Half-Life, which does such Blts every time it finished
6024 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
6025 * menu. This is also used by all apps when they do windowed rendering
6027 * The problem is that flipping is not really the same as copying. After a
6028 * Blt the front buffer is a copy of the back buffer, and the back buffer is
6029 * untouched. Therefore it's necessary to override the swap effect
6030 * and to set it back after the flip.
6032 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
6033 * testcases.
6036 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
6037 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
6039 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead.\n");
6040 wined3d_swapchain_present(dstSwapchain, NULL, NULL, dstSwapchain->win_handle, NULL, 0);
6042 dstSwapchain->presentParms.SwapEffect = orig_swap;
6044 return WINED3D_OK;
6046 break;
6049 TRACE("Unsupported blit between buffers on the same swapchain\n");
6050 return WINED3DERR_INVALIDCALL;
6051 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
6052 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
6053 return WINED3DERR_INVALIDCALL;
6054 } else if(dstSwapchain && srcSwapchain) {
6055 FIXME("Implement hardware blit between two different swapchains\n");
6056 return WINED3DERR_INVALIDCALL;
6058 else if (dstSwapchain)
6060 /* Handled with regular texture -> swapchain blit */
6061 if (src_surface == device->render_targets[0])
6062 TRACE("Blit from active render target to a swapchain\n");
6064 else if (srcSwapchain && dst_surface == device->render_targets[0])
6066 FIXME("Implement blit from a swapchain to the active render target\n");
6067 return WINED3DERR_INVALIDCALL;
6070 if ((srcSwapchain || src_surface == device->render_targets[0]) && !dstSwapchain)
6072 /* Blit from render target to texture */
6073 BOOL stretchx;
6075 /* P8 read back is not implemented */
6076 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
6077 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
6079 TRACE("P8 read back not supported by frame buffer to texture blit\n");
6080 return WINED3DERR_INVALIDCALL;
6083 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
6085 TRACE("Color keying not supported by frame buffer to texture blit\n");
6086 return WINED3DERR_INVALIDCALL;
6087 /* Destination color key is checked above */
6090 if(dst_rect.right - dst_rect.left != src_rect.right - src_rect.left) {
6091 stretchx = TRUE;
6092 } else {
6093 stretchx = FALSE;
6096 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
6097 * flip the image nor scale it.
6099 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
6100 * -> If the app wants a image width an unscaled width, copy it line per line
6101 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
6102 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
6103 * back buffer. This is slower than reading line per line, thus not used for flipping
6104 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
6105 * pixel by pixel
6107 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
6108 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
6109 * backends. */
6110 if (fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6111 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6112 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6114 surface_blt_fbo(device, Filter,
6115 src_surface, SFLAG_INDRAWABLE, &src_rect,
6116 dst_surface, SFLAG_INDRAWABLE, &dst_rect);
6117 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
6119 else if (!stretchx || dst_rect.right - dst_rect.left > src_surface->resource.width
6120 || dst_rect.bottom - dst_rect.top > src_surface->resource.height)
6122 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
6123 fb_copy_to_texture_direct(dst_surface, src_surface, &src_rect, &dst_rect, Filter);
6124 } else {
6125 TRACE("Using hardware stretching to flip / stretch the texture\n");
6126 fb_copy_to_texture_hwstretch(dst_surface, src_surface, &src_rect, &dst_rect, Filter);
6129 if (!(dst_surface->flags & SFLAG_DONOTFREE))
6131 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
6132 dst_surface->resource.allocatedMemory = NULL;
6133 dst_surface->resource.heapMemory = NULL;
6135 else
6137 dst_surface->flags &= ~SFLAG_INSYSMEM;
6140 return WINED3D_OK;
6142 else if (src_surface)
6144 /* Blit from offscreen surface to render target */
6145 DWORD oldCKeyFlags = src_surface->CKeyFlags;
6146 WINEDDCOLORKEY oldBltCKey = src_surface->SrcBltCKey;
6148 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
6150 if (!(flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
6151 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6152 &src_rect, src_surface->resource.usage, src_surface->resource.pool,
6153 src_surface->resource.format,
6154 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool,
6155 dst_surface->resource.format))
6157 TRACE("Using surface_blt_fbo.\n");
6158 /* The source is always a texture, but never the currently active render target, and the texture
6159 * contents are never upside down. */
6160 surface_blt_fbo(device, Filter,
6161 src_surface, SFLAG_INDRAWABLE, &src_rect,
6162 dst_surface, SFLAG_INDRAWABLE, &dst_rect);
6163 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
6164 return WINED3D_OK;
6167 if (!(flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
6168 && arbfp_blit.blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6169 &src_rect, src_surface->resource.usage, src_surface->resource.pool,
6170 src_surface->resource.format,
6171 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool,
6172 dst_surface->resource.format))
6174 return arbfp_blit_surface(device, src_surface, &src_rect, dst_surface, &dst_rect,
6175 WINED3D_BLIT_OP_COLOR_BLIT, Filter);
6178 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6179 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6180 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6182 FIXME("Unsupported blit operation falling back to software\n");
6183 return WINED3DERR_INVALIDCALL;
6186 /* Color keying: Check if we have to do a color keyed blt,
6187 * and if not check if a color key is activated.
6189 * Just modify the color keying parameters in the surface and restore them afterwards
6190 * The surface keeps track of the color key last used to load the opengl surface.
6191 * PreLoad will catch the change to the flags and color key and reload if necessary.
6193 if (flags & WINEDDBLT_KEYSRC)
6195 /* Use color key from surface */
6197 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6199 /* Use color key from DDBltFx */
6200 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
6201 src_surface->SrcBltCKey = DDBltFx->ddckSrcColorkey;
6203 else
6205 /* Do not use color key */
6206 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
6209 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
6210 src_surface, &src_rect, dst_surface, &dst_rect);
6212 /* Restore the color key parameters */
6213 src_surface->CKeyFlags = oldCKeyFlags;
6214 src_surface->SrcBltCKey = oldBltCKey;
6216 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
6218 return WINED3D_OK;
6220 else
6222 /* Source-Less Blit to render target */
6223 if (flags & WINEDDBLT_COLORFILL)
6225 WINED3DCOLORVALUE color;
6227 TRACE("Colorfill\n");
6229 /* The color as given in the Blt function is in the surface format. */
6230 if (!surface_convert_color_to_float(dst_surface, DDBltFx->u5.dwFillColor, &color))
6231 return WINED3DERR_INVALIDCALL;
6233 return surface_color_fill(dst_surface, &dst_rect, &color);
6237 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
6238 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
6239 return WINED3DERR_INVALIDCALL;
6242 /* Do not call while under the GL lock. */
6243 static HRESULT wined3d_surface_depth_fill(IWineD3DSurfaceImpl *surface, const RECT *rect, float depth)
6245 const struct wined3d_resource *resource = &surface->resource;
6246 IWineD3DDeviceImpl *device = resource->device;
6247 const struct blit_shader *blitter;
6249 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
6250 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
6251 if (!blitter)
6253 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
6254 return WINED3DERR_INVALIDCALL;
6257 return blitter->depth_fill(device, surface, rect, depth);
6260 static HRESULT wined3d_surface_depth_blt(IWineD3DSurfaceImpl *src_surface, const RECT *src_rect,
6261 IWineD3DSurfaceImpl *dst_surface, const RECT *dst_rect)
6263 IWineD3DDeviceImpl *device = src_surface->resource.device;
6265 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
6266 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
6267 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
6268 return WINED3DERR_INVALIDCALL;
6270 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
6272 surface_modify_ds_location(dst_surface, SFLAG_DS_OFFSCREEN,
6273 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
6274 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
6276 return WINED3D_OK;
6279 /* Do not call while under the GL lock. */
6280 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect,
6281 IWineD3DSurface *src_surface, const RECT *SrcRect, DWORD flags,
6282 const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter)
6284 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
6285 IWineD3DSurfaceImpl *src = (IWineD3DSurfaceImpl *)src_surface;
6286 IWineD3DDeviceImpl *device = This->resource.device;
6287 DWORD src_ds_flags, dst_ds_flags;
6289 TRACE("iface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6290 iface, wine_dbgstr_rect(DestRect), src_surface, wine_dbgstr_rect(SrcRect),
6291 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
6292 TRACE("Usage is %s.\n", debug_d3dusage(This->resource.usage));
6294 if ((This->flags & SFLAG_LOCKED) || (src && (src->flags & SFLAG_LOCKED)))
6296 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
6297 return WINEDDERR_SURFACEBUSY;
6300 dst_ds_flags = This->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
6301 if (src)
6302 src_ds_flags = src->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
6303 else
6304 src_ds_flags = 0;
6306 if (src_ds_flags || dst_ds_flags)
6308 if (flags & WINEDDBLT_DEPTHFILL)
6310 float depth;
6311 RECT rect;
6313 TRACE("Depth fill.\n");
6315 surface_get_rect(This, DestRect, &rect);
6317 if (!surface_convert_depth_to_float(This, DDBltFx->u5.dwFillDepth, &depth))
6318 return WINED3DERR_INVALIDCALL;
6320 if (SUCCEEDED(wined3d_surface_depth_fill(This, &rect, depth)))
6321 return WINED3D_OK;
6323 else
6325 RECT src_rect, dst_rect;
6327 /* Accessing depth / stencil surfaces is supposed to fail while in
6328 * a scene, except for fills, which seem to work. */
6329 if (device->inScene)
6331 WARN("Rejecting depth / stencil access while in scene.\n");
6332 return WINED3DERR_INVALIDCALL;
6335 if (src_ds_flags != dst_ds_flags)
6337 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
6338 return WINED3DERR_INVALIDCALL;
6341 if (SrcRect && (SrcRect->top || SrcRect->left
6342 || SrcRect->bottom != src->resource.height
6343 || SrcRect->right != src->resource.width))
6345 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
6346 wine_dbgstr_rect(SrcRect));
6347 return WINED3DERR_INVALIDCALL;
6350 if (DestRect && (DestRect->top || DestRect->left
6351 || DestRect->bottom != This->resource.height
6352 || DestRect->right != This->resource.width))
6354 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
6355 wine_dbgstr_rect(SrcRect));
6356 return WINED3DERR_INVALIDCALL;
6359 if (src->resource.height != This->resource.height
6360 || src->resource.width != This->resource.width)
6362 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
6363 return WINED3DERR_INVALIDCALL;
6366 surface_get_rect(src, SrcRect, &src_rect);
6367 surface_get_rect(This, DestRect, &dst_rect);
6369 if (SUCCEEDED(wined3d_surface_depth_blt(src, &src_rect, This, &dst_rect)))
6370 return WINED3D_OK;
6374 /* Special cases for RenderTargets */
6375 if ((This->resource.usage & WINED3DUSAGE_RENDERTARGET)
6376 || (src && (src->resource.usage & WINED3DUSAGE_RENDERTARGET)))
6378 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(This, DestRect, src, SrcRect, flags, DDBltFx, Filter)))
6379 return WINED3D_OK;
6382 /* For the rest call the X11 surface implementation.
6383 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
6384 * other Blts are rather rare. */
6385 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, src_surface, SrcRect, flags, DDBltFx, Filter);
6388 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
6389 IWineD3DSurface *src_surface, const RECT *rsrc, DWORD trans)
6391 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
6392 IWineD3DSurfaceImpl *src = (IWineD3DSurfaceImpl *)src_surface;
6393 IWineD3DDeviceImpl *device = This->resource.device;
6395 TRACE("iface %p, dst_x %u, dst_y %u, src_surface %p, src_rect %s, flags %#x.\n",
6396 iface, dstx, dsty, src_surface, wine_dbgstr_rect(rsrc), trans);
6398 if ((This->flags & SFLAG_LOCKED) || (src->flags & SFLAG_LOCKED))
6400 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
6401 return WINEDDERR_SURFACEBUSY;
6404 if (device->inScene && (This == device->depth_stencil || src == device->depth_stencil))
6406 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
6407 return WINED3DERR_INVALIDCALL;
6410 /* Special cases for RenderTargets */
6411 if ((This->resource.usage & WINED3DUSAGE_RENDERTARGET)
6412 || (src->resource.usage & WINED3DUSAGE_RENDERTARGET))
6415 RECT SrcRect, DstRect;
6416 DWORD flags = 0;
6418 surface_get_rect(src, rsrc, &SrcRect);
6420 DstRect.left = dstx;
6421 DstRect.top=dsty;
6422 DstRect.right = dstx + SrcRect.right - SrcRect.left;
6423 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
6425 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
6426 if (trans & WINEDDBLTFAST_SRCCOLORKEY)
6427 flags |= WINEDDBLT_KEYSRC;
6428 if (trans & WINEDDBLTFAST_DESTCOLORKEY)
6429 flags |= WINEDDBLT_KEYDEST;
6430 if (trans & WINEDDBLTFAST_WAIT)
6431 flags |= WINEDDBLT_WAIT;
6432 if (trans & WINEDDBLTFAST_DONOTWAIT)
6433 flags |= WINEDDBLT_DONOTWAIT;
6435 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(This,
6436 &DstRect, src, &SrcRect, flags, NULL, WINED3DTEXF_POINT)))
6437 return WINED3D_OK;
6440 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, src_surface, rsrc, trans);
6443 /* GL locking is done by the caller */
6444 static void surface_depth_blt(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info,
6445 GLuint texture, GLsizei w, GLsizei h, GLenum target)
6447 IWineD3DDeviceImpl *device = This->resource.device;
6448 GLint compare_mode = GL_NONE;
6449 struct blt_info info;
6450 GLint old_binding = 0;
6451 RECT rect;
6453 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
6455 glDisable(GL_CULL_FACE);
6456 glDisable(GL_BLEND);
6457 glDisable(GL_ALPHA_TEST);
6458 glDisable(GL_SCISSOR_TEST);
6459 glDisable(GL_STENCIL_TEST);
6460 glEnable(GL_DEPTH_TEST);
6461 glDepthFunc(GL_ALWAYS);
6462 glDepthMask(GL_TRUE);
6463 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
6464 glViewport(0, This->pow2Height - h, w, h);
6466 SetRect(&rect, 0, h, w, 0);
6467 surface_get_blt_info(target, &rect, This->pow2Width, This->pow2Height, &info);
6468 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
6469 glGetIntegerv(info.binding, &old_binding);
6470 glBindTexture(info.bind_target, texture);
6471 if (gl_info->supported[ARB_SHADOW])
6473 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
6474 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
6477 device->shader_backend->shader_select_depth_blt(device->shader_priv,
6478 gl_info, info.tex_type, &This->ds_current_size);
6480 glBegin(GL_TRIANGLE_STRIP);
6481 glTexCoord3fv(info.coords[0]);
6482 glVertex2f(-1.0f, -1.0f);
6483 glTexCoord3fv(info.coords[1]);
6484 glVertex2f(1.0f, -1.0f);
6485 glTexCoord3fv(info.coords[2]);
6486 glVertex2f(-1.0f, 1.0f);
6487 glTexCoord3fv(info.coords[3]);
6488 glVertex2f(1.0f, 1.0f);
6489 glEnd();
6491 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
6492 glBindTexture(info.bind_target, old_binding);
6494 glPopAttrib();
6496 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
6499 void surface_modify_ds_location(IWineD3DSurfaceImpl *surface,
6500 DWORD location, UINT w, UINT h)
6502 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
6504 if (location & ~SFLAG_DS_LOCATIONS)
6505 FIXME("Invalid location (%#x) specified.\n", location);
6507 surface->ds_current_size.cx = w;
6508 surface->ds_current_size.cy = h;
6509 surface->flags &= ~SFLAG_DS_LOCATIONS;
6510 surface->flags |= location;
6513 /* Context activation is done by the caller. */
6514 void surface_load_ds_location(IWineD3DSurfaceImpl *surface, struct wined3d_context *context, DWORD location)
6516 IWineD3DDeviceImpl *device = surface->resource.device;
6517 const struct wined3d_gl_info *gl_info = context->gl_info;
6518 GLsizei w, h;
6520 TRACE("surface %p, new location %#x.\n", surface, location);
6522 /* TODO: Make this work for modes other than FBO */
6523 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
6525 if (!(surface->flags & location))
6527 w = surface->ds_current_size.cx;
6528 h = surface->ds_current_size.cy;
6529 surface->ds_current_size.cx = 0;
6530 surface->ds_current_size.cy = 0;
6532 else
6534 w = surface->resource.width;
6535 h = surface->resource.height;
6538 if (surface->ds_current_size.cx == surface->resource.width
6539 && surface->ds_current_size.cy == surface->resource.height)
6541 TRACE("Location (%#x) is already up to date.\n", location);
6542 return;
6545 if (surface->current_renderbuffer)
6547 FIXME("Not supported with fixed up depth stencil.\n");
6548 return;
6551 if (!(surface->flags & SFLAG_DS_LOCATIONS))
6553 /* This mostly happens when a depth / stencil is used without being
6554 * cleared first. In principle we could upload from sysmem, or
6555 * explicitly clear before first usage. For the moment there don't
6556 * appear to be a lot of applications depending on this, so a FIXME
6557 * should do. */
6558 FIXME("No up to date depth stencil location.\n");
6559 surface->flags |= location;
6560 surface->ds_current_size.cx = surface->resource.width;
6561 surface->ds_current_size.cy = surface->resource.height;
6562 return;
6565 if (location == SFLAG_DS_OFFSCREEN)
6567 GLint old_binding = 0;
6568 GLenum bind_target;
6570 /* The render target is allowed to be smaller than the depth/stencil
6571 * buffer, so the onscreen depth/stencil buffer is potentially smaller
6572 * than the offscreen surface. Don't overwrite the offscreen surface
6573 * with undefined data. */
6574 w = min(w, context->swapchain->presentParms.BackBufferWidth);
6575 h = min(h, context->swapchain->presentParms.BackBufferHeight);
6577 TRACE("Copying onscreen depth buffer to depth texture.\n");
6579 ENTER_GL();
6581 if (!device->depth_blt_texture)
6583 glGenTextures(1, &device->depth_blt_texture);
6586 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
6587 * directly on the FBO texture. That's because we need to flip. */
6588 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
6589 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
6591 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
6592 bind_target = GL_TEXTURE_RECTANGLE_ARB;
6594 else
6596 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
6597 bind_target = GL_TEXTURE_2D;
6599 glBindTexture(bind_target, device->depth_blt_texture);
6600 glCopyTexImage2D(bind_target, surface->texture_level, surface->resource.format->glInternal, 0, 0, w, h, 0);
6601 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
6602 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
6603 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
6604 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
6605 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
6606 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
6607 glBindTexture(bind_target, old_binding);
6609 /* Setup the destination */
6610 if (!device->depth_blt_rb)
6612 gl_info->fbo_ops.glGenRenderbuffers(1, &device->depth_blt_rb);
6613 checkGLcall("glGenRenderbuffersEXT");
6615 if (device->depth_blt_rb_w != w || device->depth_blt_rb_h != h)
6617 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, device->depth_blt_rb);
6618 checkGLcall("glBindRenderbufferEXT");
6619 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h);
6620 checkGLcall("glRenderbufferStorageEXT");
6621 device->depth_blt_rb_w = w;
6622 device->depth_blt_rb_h = h;
6625 context_bind_fbo(context, GL_FRAMEBUFFER, &context->dst_fbo);
6626 gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER,
6627 GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, device->depth_blt_rb);
6628 checkGLcall("glFramebufferRenderbufferEXT");
6629 context_attach_depth_stencil_fbo(context, GL_FRAMEBUFFER, surface, FALSE);
6631 /* Do the actual blit */
6632 surface_depth_blt(surface, gl_info, device->depth_blt_texture, w, h, bind_target);
6633 checkGLcall("depth_blt");
6635 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
6636 else context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
6638 LEAVE_GL();
6640 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
6642 else if (location == SFLAG_DS_ONSCREEN)
6644 TRACE("Copying depth texture to onscreen depth buffer.\n");
6646 ENTER_GL();
6648 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
6649 surface_depth_blt(surface, gl_info, surface->texture_name,
6650 w, h, surface->texture_target);
6651 checkGLcall("depth_blt");
6653 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
6655 LEAVE_GL();
6657 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
6659 else
6661 ERR("Invalid location (%#x) specified.\n", location);
6664 surface->flags |= location;
6665 surface->ds_current_size.cx = surface->resource.width;
6666 surface->ds_current_size.cy = surface->resource.height;
6669 void surface_modify_location(IWineD3DSurfaceImpl *surface, DWORD flag, BOOL persistent)
6671 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
6672 IWineD3DSurfaceImpl *overlay;
6674 TRACE("surface %p, location %s, persistent %#x.\n",
6675 surface, debug_surflocation(flag), persistent);
6677 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6679 if (surface_is_offscreen(surface))
6681 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
6682 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
6684 else
6686 TRACE("Surface %p is an onscreen surface.\n", surface);
6690 if (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6691 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6693 flag |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6696 if (persistent)
6698 if (((surface->flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE))
6699 || ((surface->flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX)))
6701 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
6703 TRACE("Passing to container.\n");
6704 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
6707 surface->flags &= ~SFLAG_LOCATIONS;
6708 surface->flags |= flag;
6710 /* Redraw emulated overlays, if any */
6711 if (flag & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
6713 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, IWineD3DSurfaceImpl, overlay_entry)
6715 overlay->surface_ops->surface_draw_overlay(overlay);
6719 else
6721 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
6723 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
6725 TRACE("Passing to container\n");
6726 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
6729 surface->flags &= ~flag;
6732 if (!(surface->flags & SFLAG_LOCATIONS))
6734 ERR("Surface %p does not have any up to date location.\n", surface);
6738 HRESULT surface_load_location(IWineD3DSurfaceImpl *surface, DWORD flag, const RECT *rect)
6740 IWineD3DDeviceImpl *device = surface->resource.device;
6741 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6742 BOOL drawable_read_ok = surface_is_offscreen(surface);
6743 struct wined3d_format format;
6744 CONVERT_TYPES convert;
6745 int width, pitch, outpitch;
6746 BYTE *mem;
6747 BOOL in_fbo = FALSE;
6749 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(flag), wine_dbgstr_rect(rect));
6751 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6753 if (flag == SFLAG_INTEXTURE)
6755 struct wined3d_context *context = context_acquire(device, NULL);
6756 surface_load_ds_location(surface, context, SFLAG_DS_OFFSCREEN);
6757 context_release(context);
6758 return WINED3D_OK;
6760 else
6762 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(flag));
6763 return WINED3DERR_INVALIDCALL;
6767 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6769 if (surface_is_offscreen(surface))
6771 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
6772 * Prefer SFLAG_INTEXTURE. */
6773 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
6774 drawable_read_ok = FALSE;
6775 in_fbo = TRUE;
6777 else
6779 TRACE("Surface %p is an onscreen surface.\n", surface);
6783 if (flag == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6785 flag = SFLAG_INTEXTURE;
6788 if (surface->flags & flag)
6790 TRACE("Location already up to date\n");
6791 return WINED3D_OK;
6794 if (!(surface->flags & SFLAG_LOCATIONS))
6796 ERR("Surface %p does not have any up to date location.\n", surface);
6797 surface->flags |= SFLAG_LOST;
6798 return WINED3DERR_DEVICELOST;
6801 if (flag == SFLAG_INSYSMEM)
6803 surface_prepare_system_memory(surface);
6805 /* Download the surface to system memory */
6806 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
6808 struct wined3d_context *context = NULL;
6810 if (!device->isInDraw) context = context_acquire(device, NULL);
6812 surface_bind_and_dirtify(surface, gl_info, !(surface->flags & SFLAG_INTEXTURE));
6813 surface_download_data(surface, gl_info);
6815 if (context) context_release(context);
6817 else
6819 /* Note: It might be faster to download into a texture first. */
6820 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
6821 IWineD3DSurface_GetPitch((IWineD3DSurface *)surface));
6824 else if (flag == SFLAG_INDRAWABLE)
6826 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
6827 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
6829 if (surface->flags & SFLAG_INTEXTURE)
6831 RECT r;
6833 surface_get_rect(surface, rect, &r);
6834 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
6836 else
6838 int byte_count;
6839 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
6841 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
6842 * values, otherwise we get incorrect values in the target. For now go the slow way
6843 * via a system memory copy
6845 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6848 d3dfmt_get_conv(surface, FALSE /* We need color keying */,
6849 FALSE /* We won't use textures */, &format, &convert);
6851 /* The width is in 'length' not in bytes */
6852 width = surface->resource.width;
6853 pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *)surface);
6855 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
6856 * but it isn't set (yet) in all cases it is getting called. */
6857 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
6859 struct wined3d_context *context = NULL;
6861 TRACE("Removing the pbo attached to surface %p.\n", surface);
6863 if (!device->isInDraw) context = context_acquire(device, NULL);
6864 surface_remove_pbo(surface, gl_info);
6865 if (context) context_release(context);
6868 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
6870 int height = surface->resource.height;
6871 byte_count = format.conv_byte_count;
6873 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6874 outpitch = width * byte_count;
6875 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6877 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
6878 if(!mem) {
6879 ERR("Out of memory %d, %d!\n", outpitch, height);
6880 return WINED3DERR_OUTOFVIDEOMEMORY;
6882 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, pitch,
6883 width, height, outpitch, convert, surface);
6885 surface->flags |= SFLAG_CONVERTED;
6887 else
6889 surface->flags &= ~SFLAG_CONVERTED;
6890 mem = surface->resource.allocatedMemory;
6891 byte_count = format.byte_count;
6894 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
6896 /* Don't delete PBO memory */
6897 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6898 HeapFree(GetProcessHeap(), 0, mem);
6901 else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */
6903 const DWORD attach_flags = WINED3DFMT_FLAG_FBO_ATTACHABLE | WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB;
6905 if (drawable_read_ok && (surface->flags & SFLAG_INDRAWABLE))
6907 read_from_framebuffer_texture(surface, flag == SFLAG_INSRGBTEX);
6909 else if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6910 && (surface->resource.format->flags & attach_flags) == attach_flags
6911 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6912 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6913 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6915 DWORD src_location = flag == SFLAG_INSRGBTEX ? SFLAG_INTEXTURE : SFLAG_INSRGBTEX;
6916 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
6918 surface_blt_fbo(surface->resource.device, WINED3DTEXF_POINT,
6919 surface, src_location, &rect, surface, flag, &rect);
6921 else
6923 /* Upload from system memory */
6924 BOOL srgb = flag == SFLAG_INSRGBTEX;
6925 struct wined3d_context *context = NULL;
6927 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6928 TRUE /* We will use textures */, &format, &convert);
6930 if (srgb)
6932 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6934 /* Performance warning... */
6935 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6936 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6939 else
6941 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6943 /* Performance warning... */
6944 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6945 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6948 if (!(surface->flags & SFLAG_INSYSMEM))
6950 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6951 /* Lets hope we get it from somewhere... */
6952 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6955 if (!device->isInDraw) context = context_acquire(device, NULL);
6957 surface_prepare_texture(surface, gl_info, srgb);
6958 surface_bind_and_dirtify(surface, gl_info, srgb);
6960 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6962 surface->flags |= SFLAG_GLCKEY;
6963 surface->glCKey = surface->SrcBltCKey;
6965 else surface->flags &= ~SFLAG_GLCKEY;
6967 /* The width is in 'length' not in bytes */
6968 width = surface->resource.width;
6969 pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *)surface);
6971 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
6972 * but it isn't set (yet) in all cases it is getting called. */
6973 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6975 TRACE("Removing the pbo attached to surface %p.\n", surface);
6976 surface_remove_pbo(surface, gl_info);
6979 if (format.convert)
6981 /* This code is entered for texture formats which need a fixup. */
6982 UINT height = surface->resource.height;
6984 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6985 outpitch = width * format.conv_byte_count;
6986 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6988 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
6989 if(!mem) {
6990 ERR("Out of memory %d, %d!\n", outpitch, height);
6991 if (context) context_release(context);
6992 return WINED3DERR_OUTOFVIDEOMEMORY;
6994 format.convert(surface->resource.allocatedMemory, mem, pitch, width, height);
6996 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6998 /* This code is only entered for color keying fixups */
6999 UINT height = surface->resource.height;
7001 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
7002 outpitch = width * format.conv_byte_count;
7003 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
7005 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
7006 if(!mem) {
7007 ERR("Out of memory %d, %d!\n", outpitch, height);
7008 if (context) context_release(context);
7009 return WINED3DERR_OUTOFVIDEOMEMORY;
7011 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, pitch,
7012 width, height, outpitch, convert, surface);
7014 else
7016 mem = surface->resource.allocatedMemory;
7019 /* Make sure the correct pitch is used */
7020 ENTER_GL();
7021 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
7022 LEAVE_GL();
7024 if (mem || (surface->flags & SFLAG_PBO))
7025 surface_upload_data(surface, gl_info, &format, srgb, mem);
7027 /* Restore the default pitch */
7028 ENTER_GL();
7029 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
7030 LEAVE_GL();
7032 if (context) context_release(context);
7034 /* Don't delete PBO memory */
7035 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
7036 HeapFree(GetProcessHeap(), 0, mem);
7040 if (!rect)
7042 surface->flags |= flag;
7044 if (flag != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
7045 surface_evict_sysmem(surface);
7048 if (in_fbo && (surface->flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)))
7050 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
7051 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
7054 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
7055 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
7057 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
7060 return WINED3D_OK;
7063 BOOL surface_is_offscreen(IWineD3DSurfaceImpl *surface)
7065 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
7067 /* Not on a swapchain - must be offscreen */
7068 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
7070 /* The front buffer is always onscreen */
7071 if (surface == swapchain->front_buffer) return FALSE;
7073 /* If the swapchain is rendered to an FBO, the backbuffer is
7074 * offscreen, otherwise onscreen */
7075 return swapchain->render_to_fbo;
7078 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
7080 /* IUnknown */
7081 IWineD3DBaseSurfaceImpl_QueryInterface,
7082 IWineD3DBaseSurfaceImpl_AddRef,
7083 IWineD3DBaseSurfaceImpl_Release,
7084 /* IWineD3DResource */
7085 IWineD3DBaseSurfaceImpl_GetParent,
7086 IWineD3DBaseSurfaceImpl_SetPrivateData,
7087 IWineD3DBaseSurfaceImpl_GetPrivateData,
7088 IWineD3DBaseSurfaceImpl_FreePrivateData,
7089 IWineD3DBaseSurfaceImpl_SetPriority,
7090 IWineD3DBaseSurfaceImpl_GetPriority,
7091 IWineD3DBaseSurfaceImpl_PreLoad,
7092 /* IWineD3DSurface */
7093 IWineD3DBaseSurfaceImpl_GetResource,
7094 IWineD3DBaseSurfaceImpl_Map,
7095 IWineD3DBaseSurfaceImpl_Unmap,
7096 IWineD3DBaseSurfaceImpl_GetDC,
7097 IWineD3DBaseSurfaceImpl_ReleaseDC,
7098 IWineD3DBaseSurfaceImpl_Flip,
7099 IWineD3DSurfaceImpl_Blt,
7100 IWineD3DBaseSurfaceImpl_GetBltStatus,
7101 IWineD3DBaseSurfaceImpl_GetFlipStatus,
7102 IWineD3DBaseSurfaceImpl_IsLost,
7103 IWineD3DBaseSurfaceImpl_Restore,
7104 IWineD3DSurfaceImpl_BltFast,
7105 IWineD3DBaseSurfaceImpl_GetPalette,
7106 IWineD3DBaseSurfaceImpl_SetPalette,
7107 IWineD3DBaseSurfaceImpl_SetColorKey,
7108 IWineD3DBaseSurfaceImpl_GetPitch,
7109 IWineD3DBaseSurfaceImpl_SetMem,
7110 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
7111 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
7112 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
7113 IWineD3DBaseSurfaceImpl_UpdateOverlay,
7114 IWineD3DBaseSurfaceImpl_SetClipper,
7115 IWineD3DBaseSurfaceImpl_GetClipper,
7116 /* Internal use: */
7117 IWineD3DBaseSurfaceImpl_SetFormat,
7120 static HRESULT ffp_blit_alloc(IWineD3DDeviceImpl *device) { return WINED3D_OK; }
7121 /* Context activation is done by the caller. */
7122 static void ffp_blit_free(IWineD3DDeviceImpl *device) { }
7124 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
7125 /* Context activation is done by the caller. */
7126 static void ffp_blit_p8_upload_palette(IWineD3DSurfaceImpl *surface, const struct wined3d_gl_info *gl_info)
7128 BYTE table[256][4];
7129 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
7131 d3dfmt_p8_init_palette(surface, table, colorkey_active);
7133 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
7134 ENTER_GL();
7135 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
7136 LEAVE_GL();
7139 /* Context activation is done by the caller. */
7140 static HRESULT ffp_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, IWineD3DSurfaceImpl *surface)
7142 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
7144 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
7145 * else the surface is converted in software at upload time in LoadLocation.
7147 if(fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
7148 ffp_blit_p8_upload_palette(surface, gl_info);
7150 ENTER_GL();
7151 glEnable(surface->texture_target);
7152 checkGLcall("glEnable(surface->texture_target)");
7153 LEAVE_GL();
7154 return WINED3D_OK;
7157 /* Context activation is done by the caller. */
7158 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
7160 ENTER_GL();
7161 glDisable(GL_TEXTURE_2D);
7162 checkGLcall("glDisable(GL_TEXTURE_2D)");
7163 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
7165 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
7166 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
7168 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
7170 glDisable(GL_TEXTURE_RECTANGLE_ARB);
7171 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
7173 LEAVE_GL();
7176 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
7177 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
7178 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
7180 enum complex_fixup src_fixup;
7182 switch (blit_op)
7184 case WINED3D_BLIT_OP_COLOR_BLIT:
7185 src_fixup = get_complex_fixup(src_format->color_fixup);
7186 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
7188 TRACE("Checking support for fixup:\n");
7189 dump_color_fixup_desc(src_format->color_fixup);
7192 if (!is_identity_fixup(dst_format->color_fixup))
7194 TRACE("Destination fixups are not supported\n");
7195 return FALSE;
7198 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
7200 TRACE("P8 fixup supported\n");
7201 return TRUE;
7204 /* We only support identity conversions. */
7205 if (is_identity_fixup(src_format->color_fixup))
7207 TRACE("[OK]\n");
7208 return TRUE;
7211 TRACE("[FAILED]\n");
7212 return FALSE;
7214 case WINED3D_BLIT_OP_COLOR_FILL:
7215 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
7217 TRACE("Color fill not supported\n");
7218 return FALSE;
7221 return TRUE;
7223 case WINED3D_BLIT_OP_DEPTH_FILL:
7224 return TRUE;
7226 default:
7227 TRACE("Unsupported blit_op=%d\n", blit_op);
7228 return FALSE;
7232 /* Do not call while under the GL lock. */
7233 static HRESULT ffp_blit_color_fill(IWineD3DDeviceImpl *device, IWineD3DSurfaceImpl *dst_surface,
7234 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
7236 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
7238 return device_clear_render_targets(device, 1, &dst_surface, NULL,
7239 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
7242 /* Do not call while under the GL lock. */
7243 static HRESULT ffp_blit_depth_fill(IWineD3DDeviceImpl *device,
7244 IWineD3DSurfaceImpl *surface, const RECT *rect, float depth)
7246 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
7248 return device_clear_render_targets(device, 0, NULL, surface,
7249 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
7252 const struct blit_shader ffp_blit = {
7253 ffp_blit_alloc,
7254 ffp_blit_free,
7255 ffp_blit_set,
7256 ffp_blit_unset,
7257 ffp_blit_supported,
7258 ffp_blit_color_fill,
7259 ffp_blit_depth_fill,
7262 static HRESULT cpu_blit_alloc(IWineD3DDeviceImpl *device)
7264 return WINED3D_OK;
7267 /* Context activation is done by the caller. */
7268 static void cpu_blit_free(IWineD3DDeviceImpl *device)
7272 /* Context activation is done by the caller. */
7273 static HRESULT cpu_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, IWineD3DSurfaceImpl *surface)
7275 return WINED3D_OK;
7278 /* Context activation is done by the caller. */
7279 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
7283 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
7284 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
7285 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
7287 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
7289 return TRUE;
7292 return FALSE;
7295 /* Do not call while under the GL lock. */
7296 static HRESULT cpu_blit_color_fill(IWineD3DDeviceImpl *device, IWineD3DSurfaceImpl *dst_surface,
7297 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
7299 WINEDDBLTFX BltFx;
7301 memset(&BltFx, 0, sizeof(BltFx));
7302 BltFx.dwSize = sizeof(BltFx);
7303 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface->resource.format, color);
7304 return IWineD3DBaseSurfaceImpl_Blt((IWineD3DSurface*)dst_surface, dst_rect,
7305 NULL, NULL, WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7308 /* Do not call while under the GL lock. */
7309 static HRESULT cpu_blit_depth_fill(IWineD3DDeviceImpl *device,
7310 IWineD3DSurfaceImpl *surface, const RECT *rect, float depth)
7312 FIXME("Depth filling not implemented by cpu_blit.\n");
7313 return WINED3DERR_INVALIDCALL;
7316 const struct blit_shader cpu_blit = {
7317 cpu_blit_alloc,
7318 cpu_blit_free,
7319 cpu_blit_set,
7320 cpu_blit_unset,
7321 cpu_blit_supported,
7322 cpu_blit_color_fill,
7323 cpu_blit_depth_fill,
7326 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
7327 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
7328 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
7330 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
7331 return FALSE;
7333 /* Source and/or destination need to be on the GL side */
7334 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
7335 return FALSE;
7337 switch (blit_op)
7339 case WINED3D_BLIT_OP_COLOR_BLIT:
7340 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
7341 return FALSE;
7342 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
7343 return FALSE;
7344 break;
7346 case WINED3D_BLIT_OP_DEPTH_BLIT:
7347 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
7348 return FALSE;
7349 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
7350 return FALSE;
7351 break;
7353 default:
7354 return FALSE;
7357 if (!(src_format->id == dst_format->id
7358 || (is_identity_fixup(src_format->color_fixup)
7359 && is_identity_fixup(dst_format->color_fixup))))
7360 return FALSE;
7362 return TRUE;
7365 static const IWineD3DSurfaceVtbl IWineGDISurface_Vtbl =
7367 /* IUnknown */
7368 IWineD3DBaseSurfaceImpl_QueryInterface,
7369 IWineD3DBaseSurfaceImpl_AddRef,
7370 IWineD3DBaseSurfaceImpl_Release,
7371 /* IWineD3DResource */
7372 IWineD3DBaseSurfaceImpl_GetParent,
7373 IWineD3DBaseSurfaceImpl_SetPrivateData,
7374 IWineD3DBaseSurfaceImpl_GetPrivateData,
7375 IWineD3DBaseSurfaceImpl_FreePrivateData,
7376 IWineD3DBaseSurfaceImpl_SetPriority,
7377 IWineD3DBaseSurfaceImpl_GetPriority,
7378 IWineD3DBaseSurfaceImpl_PreLoad,
7379 /* IWineD3DSurface */
7380 IWineD3DBaseSurfaceImpl_GetResource,
7381 IWineD3DBaseSurfaceImpl_Map,
7382 IWineD3DBaseSurfaceImpl_Unmap,
7383 IWineD3DBaseSurfaceImpl_GetDC,
7384 IWineD3DBaseSurfaceImpl_ReleaseDC,
7385 IWineD3DBaseSurfaceImpl_Flip,
7386 IWineD3DBaseSurfaceImpl_Blt,
7387 IWineD3DBaseSurfaceImpl_GetBltStatus,
7388 IWineD3DBaseSurfaceImpl_GetFlipStatus,
7389 IWineD3DBaseSurfaceImpl_IsLost,
7390 IWineD3DBaseSurfaceImpl_Restore,
7391 IWineD3DBaseSurfaceImpl_BltFast,
7392 IWineD3DBaseSurfaceImpl_GetPalette,
7393 IWineD3DBaseSurfaceImpl_SetPalette,
7394 IWineD3DBaseSurfaceImpl_SetColorKey,
7395 IWineD3DBaseSurfaceImpl_GetPitch,
7396 IWineD3DBaseSurfaceImpl_SetMem,
7397 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
7398 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
7399 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
7400 IWineD3DBaseSurfaceImpl_UpdateOverlay,
7401 IWineD3DBaseSurfaceImpl_SetClipper,
7402 IWineD3DBaseSurfaceImpl_GetClipper,
7403 /* Internal use: */
7404 IWineD3DBaseSurfaceImpl_SetFormat,
7407 HRESULT surface_init(IWineD3DSurfaceImpl *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7408 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
7409 UINT multisample_quality, IWineD3DDeviceImpl *device, DWORD usage, enum wined3d_format_id format_id,
7410 WINED3DPOOL pool, void *parent, const struct wined3d_parent_ops *parent_ops)
7412 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7413 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7414 unsigned int resource_size;
7415 HRESULT hr;
7417 if (multisample_quality > 0)
7419 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7420 multisample_quality = 0;
7423 /* Quick lockable sanity check.
7424 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7425 * this function is too deep to need to care about things like this.
7426 * Levels need to be checked too, since they all affect what can be done. */
7427 switch (pool)
7429 case WINED3DPOOL_SCRATCH:
7430 if (!lockable)
7432 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7433 "which are mutually exclusive, setting lockable to TRUE.\n");
7434 lockable = TRUE;
7436 break;
7438 case WINED3DPOOL_SYSTEMMEM:
7439 if (!lockable)
7440 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7441 break;
7443 case WINED3DPOOL_MANAGED:
7444 if (usage & WINED3DUSAGE_DYNAMIC)
7445 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7446 break;
7448 case WINED3DPOOL_DEFAULT:
7449 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7450 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7451 break;
7453 default:
7454 FIXME("Unknown pool %#x.\n", pool);
7455 break;
7458 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7459 FIXME("Trying to create a render target that isn't in the default pool.\n");
7461 /* FIXME: Check that the format is supported by the device. */
7463 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7464 if (!resource_size)
7465 return WINED3DERR_INVALIDCALL;
7467 surface->surface_type = surface_type;
7469 /* Look at the implementation and set the correct Vtable. */
7470 switch (surface_type)
7472 case SURFACE_OPENGL:
7473 surface->lpVtbl = &IWineD3DSurface_Vtbl;
7474 surface->surface_ops = &surface_ops;
7475 break;
7477 case SURFACE_GDI:
7478 surface->lpVtbl = &IWineGDISurface_Vtbl;
7479 surface->surface_ops = &gdi_surface_ops;
7480 break;
7482 default:
7483 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7484 return WINED3DERR_INVALIDCALL;
7487 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7488 multisample_type, multisample_quality, usage, pool, width, height, 1,
7489 resource_size, parent, parent_ops, &surface_resource_ops);
7490 if (FAILED(hr))
7492 WARN("Failed to initialize resource, returning %#x.\n", hr);
7493 return hr;
7496 /* "Standalone" surface. */
7497 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7499 surface->texture_level = level;
7500 list_init(&surface->overlays);
7502 /* Flags */
7503 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7504 if (discard)
7505 surface->flags |= SFLAG_DISCARD;
7506 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7507 surface->flags |= SFLAG_LOCKABLE;
7509 /* Mark the texture as dirty so that it gets loaded first time around. */
7510 surface_add_dirty_rect(surface, NULL);
7511 list_init(&surface->renderbuffers);
7513 TRACE("surface %p, memory %p, size %u\n",
7514 surface, surface->resource.allocatedMemory, surface->resource.size);
7516 /* Call the private setup routine */
7517 hr = surface->surface_ops->surface_private_setup(surface);
7518 if (FAILED(hr))
7520 ERR("Private setup failed, returning %#x\n", hr);
7521 surface->surface_ops->surface_cleanup(surface);
7522 return hr;
7525 return hr;