wined3d: Set the current location to SFLAG_INTEXTURE instead of SFLAG_INDRAWABLE...
[wine/multimedia.git] / dlls / wined3d / surface.c
blob3cd424ef6ab2e748b60c82c209dccc8a4e9e1380
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d);
36 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
37 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
38 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter);
39 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
40 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *fx,
41 WINED3DTEXTUREFILTERTYPE filter);
43 static void surface_cleanup(struct wined3d_surface *surface)
45 TRACE("surface %p.\n", surface);
47 if (surface->texture_name || (surface->flags & SFLAG_PBO) || !list_empty(&surface->renderbuffers))
49 struct wined3d_renderbuffer_entry *entry, *entry2;
50 const struct wined3d_gl_info *gl_info;
51 struct wined3d_context *context;
53 context = context_acquire(surface->resource.device, NULL);
54 gl_info = context->gl_info;
56 ENTER_GL();
58 if (surface->texture_name)
60 TRACE("Deleting texture %u.\n", surface->texture_name);
61 glDeleteTextures(1, &surface->texture_name);
64 if (surface->flags & SFLAG_PBO)
66 TRACE("Deleting PBO %u.\n", surface->pbo);
67 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
70 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
72 TRACE("Deleting renderbuffer %u.\n", entry->id);
73 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
74 HeapFree(GetProcessHeap(), 0, entry);
77 LEAVE_GL();
79 context_release(context);
82 if (surface->flags & SFLAG_DIBSECTION)
84 /* Release the DC. */
85 SelectObject(surface->hDC, surface->dib.holdbitmap);
86 DeleteDC(surface->hDC);
87 /* Release the DIB section. */
88 DeleteObject(surface->dib.DIBsection);
89 surface->dib.bitmap_data = NULL;
90 surface->resource.allocatedMemory = NULL;
93 if (surface->flags & SFLAG_USERPTR)
94 wined3d_surface_set_mem(surface, NULL);
95 if (surface->overlay_dest)
96 list_remove(&surface->overlay_entry);
98 HeapFree(GetProcessHeap(), 0, surface->palette9);
100 resource_cleanup(&surface->resource);
103 void surface_set_container(struct wined3d_surface *surface, enum wined3d_container_type type, void *container)
105 TRACE("surface %p, container %p.\n", surface, container);
107 if (!container && type != WINED3D_CONTAINER_NONE)
108 ERR("Setting NULL container of type %#x.\n", type);
110 if (type == WINED3D_CONTAINER_SWAPCHAIN)
112 surface->get_drawable_size = get_drawable_size_swapchain;
114 else
116 switch (wined3d_settings.offscreen_rendering_mode)
118 case ORM_FBO:
119 surface->get_drawable_size = get_drawable_size_fbo;
120 break;
122 case ORM_BACKBUFFER:
123 surface->get_drawable_size = get_drawable_size_backbuffer;
124 break;
126 default:
127 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
128 return;
132 surface->container.type = type;
133 surface->container.u.base = container;
136 struct blt_info
138 GLenum binding;
139 GLenum bind_target;
140 enum tex_types tex_type;
141 GLfloat coords[4][3];
144 struct float_rect
146 float l;
147 float t;
148 float r;
149 float b;
152 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
154 f->l = ((r->left * 2.0f) / w) - 1.0f;
155 f->t = ((r->top * 2.0f) / h) - 1.0f;
156 f->r = ((r->right * 2.0f) / w) - 1.0f;
157 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
160 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
162 GLfloat (*coords)[3] = info->coords;
163 struct float_rect f;
165 switch (target)
167 default:
168 FIXME("Unsupported texture target %#x\n", target);
169 /* Fall back to GL_TEXTURE_2D */
170 case GL_TEXTURE_2D:
171 info->binding = GL_TEXTURE_BINDING_2D;
172 info->bind_target = GL_TEXTURE_2D;
173 info->tex_type = tex_2d;
174 coords[0][0] = (float)rect->left / w;
175 coords[0][1] = (float)rect->top / h;
176 coords[0][2] = 0.0f;
178 coords[1][0] = (float)rect->right / w;
179 coords[1][1] = (float)rect->top / h;
180 coords[1][2] = 0.0f;
182 coords[2][0] = (float)rect->left / w;
183 coords[2][1] = (float)rect->bottom / h;
184 coords[2][2] = 0.0f;
186 coords[3][0] = (float)rect->right / w;
187 coords[3][1] = (float)rect->bottom / h;
188 coords[3][2] = 0.0f;
189 break;
191 case GL_TEXTURE_RECTANGLE_ARB:
192 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
193 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
194 info->tex_type = tex_rect;
195 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
196 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
197 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
198 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
199 break;
201 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
202 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
203 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
204 info->tex_type = tex_cube;
205 cube_coords_float(rect, w, h, &f);
207 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
208 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
209 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
210 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
211 break;
213 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
214 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
215 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
216 info->tex_type = tex_cube;
217 cube_coords_float(rect, w, h, &f);
219 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
220 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
221 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
222 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
223 break;
225 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
226 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
227 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
228 info->tex_type = tex_cube;
229 cube_coords_float(rect, w, h, &f);
231 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
232 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
233 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
234 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
235 break;
237 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
238 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
239 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
240 info->tex_type = tex_cube;
241 cube_coords_float(rect, w, h, &f);
243 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
244 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
245 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
246 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
247 break;
249 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
250 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
251 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
252 info->tex_type = tex_cube;
253 cube_coords_float(rect, w, h, &f);
255 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
256 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
257 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
258 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
259 break;
261 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
262 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
263 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
264 info->tex_type = tex_cube;
265 cube_coords_float(rect, w, h, &f);
267 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
268 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
269 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
270 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
271 break;
275 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
277 if (rect_in)
278 *rect_out = *rect_in;
279 else
281 rect_out->left = 0;
282 rect_out->top = 0;
283 rect_out->right = surface->resource.width;
284 rect_out->bottom = surface->resource.height;
288 /* GL locking and context activation is done by the caller */
289 void draw_textured_quad(const struct wined3d_surface *src_surface, const RECT *src_rect,
290 const RECT *dst_rect, WINED3DTEXTUREFILTERTYPE Filter)
292 struct blt_info info;
294 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
296 glEnable(info.bind_target);
297 checkGLcall("glEnable(bind_target)");
299 /* Bind the texture */
300 glBindTexture(info.bind_target, src_surface->texture_name);
301 checkGLcall("glBindTexture");
303 /* Filtering for StretchRect */
304 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
305 wined3d_gl_mag_filter(magLookup, Filter));
306 checkGLcall("glTexParameteri");
307 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
308 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
309 checkGLcall("glTexParameteri");
310 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
311 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
312 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
313 checkGLcall("glTexEnvi");
315 /* Draw a quad */
316 glBegin(GL_TRIANGLE_STRIP);
317 glTexCoord3fv(info.coords[0]);
318 glVertex2i(dst_rect->left, dst_rect->top);
320 glTexCoord3fv(info.coords[1]);
321 glVertex2i(dst_rect->right, dst_rect->top);
323 glTexCoord3fv(info.coords[2]);
324 glVertex2i(dst_rect->left, dst_rect->bottom);
326 glTexCoord3fv(info.coords[3]);
327 glVertex2i(dst_rect->right, dst_rect->bottom);
328 glEnd();
330 /* Unbind the texture */
331 glBindTexture(info.bind_target, 0);
332 checkGLcall("glBindTexture(info->bind_target, 0)");
334 /* We changed the filtering settings on the texture. Inform the
335 * container about this to get the filters reset properly next draw. */
336 if (src_surface->container.type == WINED3D_CONTAINER_TEXTURE)
338 struct wined3d_texture *texture = src_surface->container.u.texture;
339 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
340 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
341 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
345 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
347 const struct wined3d_format *format = surface->resource.format;
348 SYSTEM_INFO sysInfo;
349 BITMAPINFO *b_info;
350 int extraline = 0;
351 DWORD *masks;
352 UINT usage;
353 HDC dc;
355 TRACE("surface %p.\n", surface);
357 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
359 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
360 return WINED3DERR_INVALIDCALL;
363 switch (format->byte_count)
365 case 2:
366 case 4:
367 /* Allocate extra space to store the RGB bit masks. */
368 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
369 break;
371 case 3:
372 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
373 break;
375 default:
376 /* Allocate extra space for a palette. */
377 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
378 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
379 break;
382 if (!b_info)
383 return E_OUTOFMEMORY;
385 /* Some applications access the surface in via DWORDs, and do not take
386 * the necessary care at the end of the surface. So we need at least
387 * 4 extra bytes at the end of the surface. Check against the page size,
388 * if the last page used for the surface has at least 4 spare bytes we're
389 * safe, otherwise add an extra line to the DIB section. */
390 GetSystemInfo(&sysInfo);
391 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
393 extraline = 1;
394 TRACE("Adding an extra line to the DIB section.\n");
397 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
398 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
399 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
400 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
401 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
402 * wined3d_surface_get_pitch(surface);
403 b_info->bmiHeader.biPlanes = 1;
404 b_info->bmiHeader.biBitCount = format->byte_count * 8;
406 b_info->bmiHeader.biXPelsPerMeter = 0;
407 b_info->bmiHeader.biYPelsPerMeter = 0;
408 b_info->bmiHeader.biClrUsed = 0;
409 b_info->bmiHeader.biClrImportant = 0;
411 /* Get the bit masks */
412 masks = (DWORD *)b_info->bmiColors;
413 switch (surface->resource.format->id)
415 case WINED3DFMT_B8G8R8_UNORM:
416 usage = DIB_RGB_COLORS;
417 b_info->bmiHeader.biCompression = BI_RGB;
418 break;
420 case WINED3DFMT_B5G5R5X1_UNORM:
421 case WINED3DFMT_B5G5R5A1_UNORM:
422 case WINED3DFMT_B4G4R4A4_UNORM:
423 case WINED3DFMT_B4G4R4X4_UNORM:
424 case WINED3DFMT_B2G3R3_UNORM:
425 case WINED3DFMT_B2G3R3A8_UNORM:
426 case WINED3DFMT_R10G10B10A2_UNORM:
427 case WINED3DFMT_R8G8B8A8_UNORM:
428 case WINED3DFMT_R8G8B8X8_UNORM:
429 case WINED3DFMT_B10G10R10A2_UNORM:
430 case WINED3DFMT_B5G6R5_UNORM:
431 case WINED3DFMT_R16G16B16A16_UNORM:
432 usage = 0;
433 b_info->bmiHeader.biCompression = BI_BITFIELDS;
434 masks[0] = format->red_mask;
435 masks[1] = format->green_mask;
436 masks[2] = format->blue_mask;
437 break;
439 default:
440 /* Don't know palette */
441 b_info->bmiHeader.biCompression = BI_RGB;
442 usage = 0;
443 break;
446 if (!(dc = GetDC(0)))
448 HeapFree(GetProcessHeap(), 0, b_info);
449 return HRESULT_FROM_WIN32(GetLastError());
452 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
453 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
454 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
455 surface->dib.DIBsection = CreateDIBSection(dc, b_info, usage, &surface->dib.bitmap_data, 0, 0);
456 ReleaseDC(0, dc);
458 if (!surface->dib.DIBsection)
460 ERR("Failed to create DIB section.\n");
461 HeapFree(GetProcessHeap(), 0, b_info);
462 return HRESULT_FROM_WIN32(GetLastError());
465 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
466 /* Copy the existing surface to the dib section. */
467 if (surface->resource.allocatedMemory)
469 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory,
470 surface->resource.height * wined3d_surface_get_pitch(surface));
472 else
474 /* This is to make maps read the GL texture although memory is allocated. */
475 surface->flags &= ~SFLAG_INSYSMEM;
477 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
479 HeapFree(GetProcessHeap(), 0, b_info);
481 /* Now allocate a DC. */
482 surface->hDC = CreateCompatibleDC(0);
483 surface->dib.holdbitmap = SelectObject(surface->hDC, surface->dib.DIBsection);
484 TRACE("Using wined3d palette %p.\n", surface->palette);
485 SelectPalette(surface->hDC, surface->palette ? surface->palette->hpal : 0, FALSE);
487 surface->flags |= SFLAG_DIBSECTION;
489 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
490 surface->resource.heapMemory = NULL;
492 return WINED3D_OK;
495 static void surface_prepare_system_memory(struct wined3d_surface *surface)
497 struct wined3d_device *device = surface->resource.device;
498 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
500 TRACE("surface %p.\n", surface);
502 /* Performance optimization: Count how often a surface is locked, if it is
503 * locked regularly do not throw away the system memory copy. This avoids
504 * the need to download the surface from OpenGL all the time. The surface
505 * is still downloaded if the OpenGL texture is changed. */
506 if (!(surface->flags & SFLAG_DYNLOCK))
508 if (++surface->lockCount > MAXLOCKCOUNT)
510 TRACE("Surface is locked regularly, not freeing the system memory copy any more.\n");
511 surface->flags |= SFLAG_DYNLOCK;
515 /* Create a PBO for dynamically locked surfaces but don't do it for
516 * converted or NPOT surfaces. Also don't create a PBO for systemmem
517 * surfaces. */
518 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (surface->flags & SFLAG_DYNLOCK)
519 && !(surface->flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
520 && (surface->resource.pool != WINED3DPOOL_SYSTEMMEM))
522 struct wined3d_context *context;
523 GLenum error;
525 context = context_acquire(device, NULL);
526 ENTER_GL();
528 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
529 error = glGetError();
530 if (!surface->pbo || error != GL_NO_ERROR)
531 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
533 TRACE("Binding PBO %u.\n", surface->pbo);
535 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
536 checkGLcall("glBindBufferARB");
538 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
539 surface->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
540 checkGLcall("glBufferDataARB");
542 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
543 checkGLcall("glBindBufferARB");
545 /* We don't need the system memory anymore and we can't even use it for PBOs. */
546 if (!(surface->flags & SFLAG_CLIENT))
548 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
549 surface->resource.heapMemory = NULL;
551 surface->resource.allocatedMemory = NULL;
552 surface->flags |= SFLAG_PBO;
553 LEAVE_GL();
554 context_release(context);
556 else if (!(surface->resource.allocatedMemory || surface->flags & SFLAG_PBO))
558 /* Whatever surface we have, make sure that there is memory allocated
559 * for the downloaded copy, or a PBO to map. */
560 if (!surface->resource.heapMemory)
561 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
563 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
564 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
566 if (surface->flags & SFLAG_INSYSMEM)
567 ERR("Surface without memory or PBO has SFLAG_INSYSMEM set.\n");
571 static void surface_evict_sysmem(struct wined3d_surface *surface)
573 if (surface->flags & SFLAG_DONOTFREE)
574 return;
576 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
577 surface->resource.allocatedMemory = NULL;
578 surface->resource.heapMemory = NULL;
579 surface_modify_location(surface, SFLAG_INSYSMEM, FALSE);
582 /* Context activation is done by the caller. */
583 static void surface_bind_and_dirtify(struct wined3d_surface *surface,
584 const struct wined3d_gl_info *gl_info, BOOL srgb)
586 struct wined3d_device *device = surface->resource.device;
587 DWORD active_sampler;
588 GLint active_texture;
590 /* We don't need a specific texture unit, but after binding the texture
591 * the current unit is dirty. Read the unit back instead of switching to
592 * 0, this avoids messing around with the state manager's GL states. The
593 * current texture unit should always be a valid one.
595 * To be more specific, this is tricky because we can implicitly be
596 * called from sampler() in state.c. This means we can't touch anything
597 * other than whatever happens to be the currently active texture, or we
598 * would risk marking already applied sampler states dirty again.
600 * TODO: Track the current active texture per GL context instead of using
601 * glGet(). */
603 ENTER_GL();
604 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
605 LEAVE_GL();
606 active_sampler = device->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
608 if (active_sampler != WINED3D_UNMAPPED_STAGE)
609 device_invalidate_state(device, STATE_SAMPLER(active_sampler));
610 surface_bind(surface, gl_info, srgb);
613 static void surface_force_reload(struct wined3d_surface *surface)
615 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
618 static void surface_release_client_storage(struct wined3d_surface *surface)
620 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
622 ENTER_GL();
623 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
624 if (surface->texture_name)
626 surface_bind_and_dirtify(surface, context->gl_info, FALSE);
627 glTexImage2D(surface->texture_target, surface->texture_level,
628 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
630 if (surface->texture_name_srgb)
632 surface_bind_and_dirtify(surface, context->gl_info, TRUE);
633 glTexImage2D(surface->texture_target, surface->texture_level,
634 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
636 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
637 LEAVE_GL();
639 context_release(context);
641 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
642 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
643 surface_force_reload(surface);
646 static HRESULT surface_private_setup(struct wined3d_surface *surface)
648 /* TODO: Check against the maximum texture sizes supported by the video card. */
649 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
650 unsigned int pow2Width, pow2Height;
652 TRACE("surface %p.\n", surface);
654 surface->texture_name = 0;
655 surface->texture_target = GL_TEXTURE_2D;
657 /* Non-power2 support */
658 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
660 pow2Width = surface->resource.width;
661 pow2Height = surface->resource.height;
663 else
665 /* Find the nearest pow2 match */
666 pow2Width = pow2Height = 1;
667 while (pow2Width < surface->resource.width)
668 pow2Width <<= 1;
669 while (pow2Height < surface->resource.height)
670 pow2Height <<= 1;
672 surface->pow2Width = pow2Width;
673 surface->pow2Height = pow2Height;
675 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
677 /* TODO: Add support for non power two compressed textures. */
678 if (surface->resource.format->flags & WINED3DFMT_FLAG_COMPRESSED)
680 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
681 surface, surface->resource.width, surface->resource.height);
682 return WINED3DERR_NOTAVAILABLE;
686 if (pow2Width != surface->resource.width
687 || pow2Height != surface->resource.height)
689 surface->flags |= SFLAG_NONPOW2;
692 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
693 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
695 /* One of three options:
696 * 1: Do the same as we do with NPOT and scale the texture, (any
697 * texture ops would require the texture to be scaled which is
698 * potentially slow)
699 * 2: Set the texture to the maximum size (bad idea).
700 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
701 * 4: Create the surface, but allow it to be used only for DirectDraw
702 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
703 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
704 * the render target. */
705 if (surface->resource.pool == WINED3DPOOL_DEFAULT || surface->resource.pool == WINED3DPOOL_MANAGED)
707 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
708 return WINED3DERR_NOTAVAILABLE;
711 /* We should never use this surface in combination with OpenGL! */
712 TRACE("Creating an oversized surface: %ux%u.\n",
713 surface->pow2Width, surface->pow2Height);
715 else
717 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8
718 * and EXT_PALETTED_TEXTURE is used in combination with texture
719 * uploads (RTL_READTEX/RTL_TEXTEX). The reason is that
720 * EXT_PALETTED_TEXTURE doesn't work in combination with
721 * ARB_TEXTURE_RECTANGLE. */
722 if (surface->flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
723 && !(surface->resource.format->id == WINED3DFMT_P8_UINT
724 && gl_info->supported[EXT_PALETTED_TEXTURE]
725 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
727 surface->texture_target = GL_TEXTURE_RECTANGLE_ARB;
728 surface->pow2Width = surface->resource.width;
729 surface->pow2Height = surface->resource.height;
730 surface->flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
734 switch (wined3d_settings.offscreen_rendering_mode)
736 case ORM_FBO:
737 surface->get_drawable_size = get_drawable_size_fbo;
738 break;
740 case ORM_BACKBUFFER:
741 surface->get_drawable_size = get_drawable_size_backbuffer;
742 break;
744 default:
745 ERR("Unhandled offscreen rendering mode %#x.\n", wined3d_settings.offscreen_rendering_mode);
746 return WINED3DERR_INVALIDCALL;
749 surface->flags |= SFLAG_INSYSMEM;
751 return WINED3D_OK;
754 static void surface_realize_palette(struct wined3d_surface *surface)
756 struct wined3d_palette *palette = surface->palette;
758 TRACE("surface %p.\n", surface);
760 if (!palette) return;
762 if (surface->resource.format->id == WINED3DFMT_P8_UINT
763 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
765 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
767 /* Make sure the texture is up to date. This call doesn't do
768 * anything if the texture is already up to date. */
769 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
771 /* We want to force a palette refresh, so mark the drawable as not being up to date */
772 if (!surface_is_offscreen(surface))
773 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
775 else
777 if (!(surface->flags & SFLAG_INSYSMEM))
779 TRACE("Palette changed with surface that does not have an up to date system memory copy.\n");
780 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
782 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
786 if (surface->flags & SFLAG_DIBSECTION)
788 RGBQUAD col[256];
789 unsigned int i;
791 TRACE("Updating the DC's palette.\n");
793 for (i = 0; i < 256; ++i)
795 col[i].rgbRed = palette->palents[i].peRed;
796 col[i].rgbGreen = palette->palents[i].peGreen;
797 col[i].rgbBlue = palette->palents[i].peBlue;
798 col[i].rgbReserved = 0;
800 SetDIBColorTable(surface->hDC, 0, 256, col);
803 /* Propagate the changes to the drawable when we have a palette. */
804 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
805 surface_load_location(surface, SFLAG_INDRAWABLE, NULL);
808 static HRESULT surface_draw_overlay(struct wined3d_surface *surface)
810 HRESULT hr;
812 /* If there's no destination surface there is nothing to do. */
813 if (!surface->overlay_dest)
814 return WINED3D_OK;
816 /* Blt calls ModifyLocation on the dest surface, which in turn calls
817 * DrawOverlay to update the overlay. Prevent an endless recursion. */
818 if (surface->overlay_dest->flags & SFLAG_INOVERLAYDRAW)
819 return WINED3D_OK;
821 surface->overlay_dest->flags |= SFLAG_INOVERLAYDRAW;
822 hr = wined3d_surface_blt(surface->overlay_dest, &surface->overlay_destrect, surface,
823 &surface->overlay_srcrect, WINEDDBLT_WAIT, NULL, WINED3DTEXF_LINEAR);
824 surface->overlay_dest->flags &= ~SFLAG_INOVERLAYDRAW;
826 return hr;
829 static void surface_preload(struct wined3d_surface *surface)
831 TRACE("surface %p.\n", surface);
833 surface_internal_preload(surface, SRGB_ANY);
836 static void surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
838 struct wined3d_device *device = surface->resource.device;
839 const RECT *pass_rect = rect;
841 TRACE("surface %p, rect %s, flags %#x.\n",
842 surface, wine_dbgstr_rect(rect), flags);
844 if (flags & WINED3DLOCK_DISCARD)
846 TRACE("WINED3DLOCK_DISCARD flag passed, marking SYSMEM as up to date.\n");
847 surface_prepare_system_memory(surface);
848 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
850 else
852 /* surface_load_location() does not check if the rectangle specifies
853 * the full surface. Most callers don't need that, so do it here. */
854 if (rect && !rect->top && !rect->left
855 && rect->right == surface->resource.width
856 && rect->bottom == surface->resource.height)
857 pass_rect = NULL;
859 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
860 && ((surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
861 || surface == device->fb.render_targets[0])))
862 surface_load_location(surface, SFLAG_INSYSMEM, pass_rect);
865 if (surface->flags & SFLAG_PBO)
867 const struct wined3d_gl_info *gl_info;
868 struct wined3d_context *context;
870 context = context_acquire(device, NULL);
871 gl_info = context->gl_info;
873 ENTER_GL();
874 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
875 checkGLcall("glBindBufferARB");
877 /* This shouldn't happen but could occur if some other function
878 * didn't handle the PBO properly. */
879 if (surface->resource.allocatedMemory)
880 ERR("The surface already has PBO memory allocated.\n");
882 surface->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
883 checkGLcall("glMapBufferARB");
885 /* Make sure the PBO isn't set anymore in order not to break non-PBO
886 * calls. */
887 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
888 checkGLcall("glBindBufferARB");
890 LEAVE_GL();
891 context_release(context);
894 if (!(flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)))
896 if (!rect)
897 surface_add_dirty_rect(surface, NULL);
898 else
900 WINED3DBOX b;
902 b.Left = rect->left;
903 b.Top = rect->top;
904 b.Right = rect->right;
905 b.Bottom = rect->bottom;
906 b.Front = 0;
907 b.Back = 1;
908 surface_add_dirty_rect(surface, &b);
913 static void surface_unmap(struct wined3d_surface *surface)
915 struct wined3d_device *device = surface->resource.device;
916 BOOL fullsurface;
918 TRACE("surface %p.\n", surface);
920 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
922 if (surface->flags & SFLAG_PBO)
924 const struct wined3d_gl_info *gl_info;
925 struct wined3d_context *context;
927 TRACE("Freeing PBO memory.\n");
929 context = context_acquire(device, NULL);
930 gl_info = context->gl_info;
932 ENTER_GL();
933 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
934 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
935 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
936 checkGLcall("glUnmapBufferARB");
937 LEAVE_GL();
938 context_release(context);
940 surface->resource.allocatedMemory = NULL;
943 TRACE("dirtyfied %u.\n", surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
945 if (surface->flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE))
947 TRACE("Not dirtified, nothing to do.\n");
948 goto done;
951 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
952 || (device->fb.render_targets && surface == device->fb.render_targets[0]))
954 if (wined3d_settings.rendertargetlock_mode == RTL_DISABLE)
956 static BOOL warned = FALSE;
957 if (!warned)
959 ERR("The application tries to write to the render target, but render target locking is disabled.\n");
960 warned = TRUE;
962 goto done;
965 if (!surface->dirtyRect.left && !surface->dirtyRect.top
966 && surface->dirtyRect.right == surface->resource.width
967 && surface->dirtyRect.bottom == surface->resource.height)
969 fullsurface = TRUE;
971 else
973 /* TODO: Proper partial rectangle tracking. */
974 fullsurface = FALSE;
975 surface->flags |= SFLAG_INSYSMEM;
978 surface_load_location(surface, SFLAG_INDRAWABLE, fullsurface ? NULL : &surface->dirtyRect);
980 /* Partial rectangle tracking is not commonly implemented, it is only
981 * done for render targets. INSYSMEM was set before to tell
982 * surface_load_location() where to read the rectangle from.
983 * Indrawable is set because all modifications from the partial
984 * sysmem copy are written back to the drawable, thus the surface is
985 * merged again in the drawable. The sysmem copy is not fully up to
986 * date because only a subrectangle was read in Map(). */
987 if (!fullsurface)
989 surface_modify_location(surface, SFLAG_INDRAWABLE, TRUE);
990 surface_evict_sysmem(surface);
993 surface->dirtyRect.left = surface->resource.width;
994 surface->dirtyRect.top = surface->resource.height;
995 surface->dirtyRect.right = 0;
996 surface->dirtyRect.bottom = 0;
998 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
1000 FIXME("Depth / stencil buffer locking is not implemented.\n");
1003 done:
1004 /* Overlays have to be redrawn manually after changes with the GL implementation */
1005 if (surface->overlay_dest)
1006 surface->surface_ops->surface_draw_overlay(surface);
1009 static HRESULT surface_getdc(struct wined3d_surface *surface)
1011 WINED3DLOCKED_RECT lock;
1012 HRESULT hr;
1014 TRACE("surface %p.\n", surface);
1016 /* Create a DIB section if there isn't a dc yet. */
1017 if (!surface->hDC)
1019 if (surface->flags & SFLAG_CLIENT)
1021 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1022 surface_release_client_storage(surface);
1024 hr = surface_create_dib_section(surface);
1025 if (FAILED(hr))
1026 return WINED3DERR_INVALIDCALL;
1028 /* Use the DIB section from now on if we are not using a PBO. */
1029 if (!(surface->flags & SFLAG_PBO))
1030 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1033 /* Map the surface. */
1034 hr = wined3d_surface_map(surface, &lock, NULL, 0);
1035 if (FAILED(hr))
1036 ERR("Map failed, hr %#x.\n", hr);
1038 /* Sync the DIB with the PBO. This can't be done earlier because Map()
1039 * activates the allocatedMemory. */
1040 if (surface->flags & SFLAG_PBO)
1041 memcpy(surface->dib.bitmap_data, surface->resource.allocatedMemory, surface->dib.bitmap_size);
1043 return hr;
1046 static HRESULT surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override)
1048 TRACE("surface %p, override %p.\n", surface, override);
1050 /* Flipping is only supported on render targets and overlays. */
1051 if (!(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)))
1053 WARN("Tried to flip a non-render target, non-overlay surface.\n");
1054 return WINEDDERR_NOTFLIPPABLE;
1057 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1059 flip_surface(surface, override);
1061 /* Update the overlay if it is visible */
1062 if (surface->overlay_dest)
1063 return surface->surface_ops->surface_draw_overlay(surface);
1064 else
1065 return WINED3D_OK;
1068 return WINED3D_OK;
1071 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
1073 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
1074 return FALSE;
1075 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
1076 return FALSE;
1077 return TRUE;
1080 static void wined3d_surface_depth_blt_fbo(struct wined3d_device *device, struct wined3d_surface *src_surface,
1081 const RECT *src_rect, struct wined3d_surface *dst_surface, const RECT *dst_rect)
1083 const struct wined3d_gl_info *gl_info;
1084 struct wined3d_context *context;
1085 DWORD src_mask, dst_mask;
1086 GLbitfield gl_mask;
1088 TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n",
1089 device, src_surface, wine_dbgstr_rect(src_rect),
1090 dst_surface, wine_dbgstr_rect(dst_rect));
1092 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1093 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1095 if (src_mask != dst_mask)
1097 ERR("Incompatible formats %s and %s.\n",
1098 debug_d3dformat(src_surface->resource.format->id),
1099 debug_d3dformat(dst_surface->resource.format->id));
1100 return;
1103 if (!src_mask)
1105 ERR("Not a depth / stencil format: %s.\n",
1106 debug_d3dformat(src_surface->resource.format->id));
1107 return;
1110 gl_mask = 0;
1111 if (src_mask & WINED3DFMT_FLAG_DEPTH)
1112 gl_mask |= GL_DEPTH_BUFFER_BIT;
1113 if (src_mask & WINED3DFMT_FLAG_STENCIL)
1114 gl_mask |= GL_STENCIL_BUFFER_BIT;
1116 /* Make sure the locations are up-to-date. Loading the destination
1117 * surface isn't required if the entire surface is overwritten. */
1118 surface_load_location(src_surface, SFLAG_INTEXTURE, NULL);
1119 if (!surface_is_full_rect(dst_surface, dst_rect))
1120 surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL);
1122 context = context_acquire(device, NULL);
1123 if (!context->valid)
1125 context_release(context);
1126 WARN("Invalid context, skipping blit.\n");
1127 return;
1130 gl_info = context->gl_info;
1132 ENTER_GL();
1134 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE);
1135 glReadBuffer(GL_NONE);
1136 checkGLcall("glReadBuffer()");
1137 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1139 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE);
1140 context_set_draw_buffer(context, GL_NONE);
1141 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1143 if (gl_mask & GL_DEPTH_BUFFER_BIT)
1145 glDepthMask(GL_TRUE);
1146 context_invalidate_state(context, STATE_RENDER(WINED3DRS_ZWRITEENABLE));
1148 if (gl_mask & GL_STENCIL_BUFFER_BIT)
1150 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
1152 glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1153 context_invalidate_state(context, STATE_RENDER(WINED3DRS_TWOSIDEDSTENCILMODE));
1155 glStencilMask(~0U);
1156 context_invalidate_state(context, STATE_RENDER(WINED3DRS_STENCILWRITEMASK));
1159 glDisable(GL_SCISSOR_TEST);
1160 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1162 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
1163 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
1164 checkGLcall("glBlitFramebuffer()");
1166 LEAVE_GL();
1168 if (wined3d_settings.strict_draw_ordering)
1169 wglFlush(); /* Flush to ensure ordering across contexts. */
1171 context_release(context);
1174 /* Blit between surface locations. Onscreen on different swapchains is not supported.
1175 * Depth / stencil is not supported. */
1176 static void surface_blt_fbo(struct wined3d_device *device, const WINED3DTEXTUREFILTERTYPE filter,
1177 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
1178 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
1180 const struct wined3d_gl_info *gl_info;
1181 struct wined3d_context *context;
1182 RECT src_rect, dst_rect;
1183 GLenum gl_filter;
1184 GLenum buffer;
1186 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
1187 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
1188 src_surface, debug_surflocation(src_location), wine_dbgstr_rect(src_rect_in));
1189 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
1190 dst_surface, debug_surflocation(dst_location), wine_dbgstr_rect(dst_rect_in));
1192 src_rect = *src_rect_in;
1193 dst_rect = *dst_rect_in;
1195 switch (filter)
1197 case WINED3DTEXF_LINEAR:
1198 gl_filter = GL_LINEAR;
1199 break;
1201 default:
1202 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
1203 case WINED3DTEXF_NONE:
1204 case WINED3DTEXF_POINT:
1205 gl_filter = GL_NEAREST;
1206 break;
1209 if (src_location == SFLAG_INDRAWABLE && surface_is_offscreen(src_surface))
1210 src_location = SFLAG_INTEXTURE;
1211 if (dst_location == SFLAG_INDRAWABLE && surface_is_offscreen(dst_surface))
1212 dst_location = SFLAG_INTEXTURE;
1214 /* Make sure the locations are up-to-date. Loading the destination
1215 * surface isn't required if the entire surface is overwritten. (And is
1216 * in fact harmful if we're being called by surface_load_location() with
1217 * the purpose of loading the destination surface.) */
1218 surface_load_location(src_surface, src_location, NULL);
1219 if (!surface_is_full_rect(dst_surface, &dst_rect))
1220 surface_load_location(dst_surface, dst_location, NULL);
1222 if (src_location == SFLAG_INDRAWABLE) context = context_acquire(device, src_surface);
1223 else if (dst_location == SFLAG_INDRAWABLE) context = context_acquire(device, dst_surface);
1224 else context = context_acquire(device, NULL);
1226 if (!context->valid)
1228 context_release(context);
1229 WARN("Invalid context, skipping blit.\n");
1230 return;
1233 gl_info = context->gl_info;
1235 if (src_location == SFLAG_INDRAWABLE)
1237 TRACE("Source surface %p is onscreen.\n", src_surface);
1238 buffer = surface_get_gl_buffer(src_surface);
1239 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
1241 else
1243 TRACE("Source surface %p is offscreen.\n", src_surface);
1244 buffer = GL_COLOR_ATTACHMENT0;
1247 ENTER_GL();
1248 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
1249 glReadBuffer(buffer);
1250 checkGLcall("glReadBuffer()");
1251 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1252 LEAVE_GL();
1254 if (dst_location == SFLAG_INDRAWABLE)
1256 TRACE("Destination surface %p is onscreen.\n", dst_surface);
1257 buffer = surface_get_gl_buffer(dst_surface);
1258 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
1260 else
1262 TRACE("Destination surface %p is offscreen.\n", dst_surface);
1263 buffer = GL_COLOR_ATTACHMENT0;
1266 ENTER_GL();
1267 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
1268 context_set_draw_buffer(context, buffer);
1269 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
1270 context_invalidate_state(context, STATE_FRAMEBUFFER);
1272 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1273 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE));
1274 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE1));
1275 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE2));
1276 context_invalidate_state(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE3));
1278 glDisable(GL_SCISSOR_TEST);
1279 context_invalidate_state(context, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
1281 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
1282 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
1283 checkGLcall("glBlitFramebuffer()");
1285 LEAVE_GL();
1287 if (wined3d_settings.strict_draw_ordering
1288 || (dst_location == SFLAG_INDRAWABLE
1289 && dst_surface->container.u.swapchain->front_buffer == dst_surface))
1290 wglFlush();
1292 context_release(context);
1295 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
1296 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
1297 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
1299 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
1300 return FALSE;
1302 /* Source and/or destination need to be on the GL side */
1303 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
1304 return FALSE;
1306 switch (blit_op)
1308 case WINED3D_BLIT_OP_COLOR_BLIT:
1309 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
1310 return FALSE;
1311 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
1312 return FALSE;
1313 break;
1315 case WINED3D_BLIT_OP_DEPTH_BLIT:
1316 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1317 return FALSE;
1318 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1319 return FALSE;
1320 break;
1322 default:
1323 return FALSE;
1326 if (!(src_format->id == dst_format->id
1327 || (is_identity_fixup(src_format->color_fixup)
1328 && is_identity_fixup(dst_format->color_fixup))))
1329 return FALSE;
1331 return TRUE;
1334 /* This function checks if the primary render target uses the 8bit paletted format. */
1335 static BOOL primary_render_target_is_p8(const struct wined3d_device *device)
1337 if (device->fb.render_targets && device->fb.render_targets[0])
1339 const struct wined3d_surface *render_target = device->fb.render_targets[0];
1340 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1341 && (render_target->resource.format->id == WINED3DFMT_P8_UINT))
1342 return TRUE;
1344 return FALSE;
1347 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1348 DWORD color, WINED3DCOLORVALUE *float_color)
1350 const struct wined3d_format *format = surface->resource.format;
1351 const struct wined3d_device *device = surface->resource.device;
1353 switch (format->id)
1355 case WINED3DFMT_P8_UINT:
1356 if (surface->palette)
1358 float_color->r = surface->palette->palents[color].peRed / 255.0f;
1359 float_color->g = surface->palette->palents[color].peGreen / 255.0f;
1360 float_color->b = surface->palette->palents[color].peBlue / 255.0f;
1362 else
1364 float_color->r = 0.0f;
1365 float_color->g = 0.0f;
1366 float_color->b = 0.0f;
1368 float_color->a = primary_render_target_is_p8(device) ? color / 255.0f : 1.0f;
1369 break;
1371 case WINED3DFMT_B5G6R5_UNORM:
1372 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1373 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1374 float_color->b = (color & 0x1f) / 31.0f;
1375 float_color->a = 1.0f;
1376 break;
1378 case WINED3DFMT_B8G8R8_UNORM:
1379 case WINED3DFMT_B8G8R8X8_UNORM:
1380 float_color->r = D3DCOLOR_R(color);
1381 float_color->g = D3DCOLOR_G(color);
1382 float_color->b = D3DCOLOR_B(color);
1383 float_color->a = 1.0f;
1384 break;
1386 case WINED3DFMT_B8G8R8A8_UNORM:
1387 float_color->r = D3DCOLOR_R(color);
1388 float_color->g = D3DCOLOR_G(color);
1389 float_color->b = D3DCOLOR_B(color);
1390 float_color->a = D3DCOLOR_A(color);
1391 break;
1393 default:
1394 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1395 return FALSE;
1398 return TRUE;
1401 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1403 const struct wined3d_format *format = surface->resource.format;
1405 switch (format->id)
1407 case WINED3DFMT_S1_UINT_D15_UNORM:
1408 *float_depth = depth / (float)0x00007fff;
1409 break;
1411 case WINED3DFMT_D16_UNORM:
1412 *float_depth = depth / (float)0x0000ffff;
1413 break;
1415 case WINED3DFMT_D24_UNORM_S8_UINT:
1416 case WINED3DFMT_X8D24_UNORM:
1417 *float_depth = depth / (float)0x00ffffff;
1418 break;
1420 case WINED3DFMT_D32_UNORM:
1421 *float_depth = depth / (float)0xffffffff;
1422 break;
1424 default:
1425 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1426 return FALSE;
1429 return TRUE;
1432 /* Do not call while under the GL lock. */
1433 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1435 const struct wined3d_resource *resource = &surface->resource;
1436 struct wined3d_device *device = resource->device;
1437 const struct blit_shader *blitter;
1439 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1440 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1441 if (!blitter)
1443 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1444 return WINED3DERR_INVALIDCALL;
1447 return blitter->depth_fill(device, surface, rect, depth);
1450 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, const RECT *src_rect,
1451 struct wined3d_surface *dst_surface, const RECT *dst_rect)
1453 struct wined3d_device *device = src_surface->resource.device;
1455 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1456 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1457 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1458 return WINED3DERR_INVALIDCALL;
1460 wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect);
1462 surface_modify_ds_location(dst_surface, SFLAG_DS_OFFSCREEN,
1463 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1464 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
1466 return WINED3D_OK;
1469 /* Do not call while under the GL lock. */
1470 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
1471 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
1472 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
1474 const struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1475 struct wined3d_device *device = dst_surface->resource.device;
1476 DWORD src_ds_flags, dst_ds_flags;
1477 RECT src_rect, dst_rect;
1479 static const DWORD simple_blit = WINEDDBLT_ASYNC
1480 | WINEDDBLT_COLORFILL
1481 | WINEDDBLT_WAIT
1482 | WINEDDBLT_DEPTHFILL
1483 | WINEDDBLT_DONOTWAIT;
1485 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1486 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
1487 flags, fx, debug_d3dtexturefiltertype(filter));
1488 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
1490 if ((dst_surface->flags & SFLAG_LOCKED) || (src_surface && (src_surface->flags & SFLAG_LOCKED)))
1492 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
1493 return WINEDDERR_SURFACEBUSY;
1496 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
1498 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
1499 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
1500 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
1501 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
1502 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
1504 /* The destination rect can be out of bounds on the condition
1505 * that a clipper is set for the surface. */
1506 if (dst_surface->clipper)
1507 FIXME("Blit clipping not implemented.\n");
1508 else
1509 WARN("The application gave us a bad destination rectangle without a clipper set.\n");
1510 return WINEDDERR_INVALIDRECT;
1513 if (src_surface)
1515 surface_get_rect(src_surface, src_rect_in, &src_rect);
1517 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
1518 || src_rect.left > src_surface->resource.width || src_rect.left < 0
1519 || src_rect.top > src_surface->resource.height || src_rect.top < 0
1520 || src_rect.right > src_surface->resource.width || src_rect.right < 0
1521 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
1523 WARN("Application gave us bad source rectangle for Blt.\n");
1524 return WINEDDERR_INVALIDRECT;
1527 else
1529 memset(&src_rect, 0, sizeof(src_rect));
1532 if (!fx || !(fx->dwDDFX))
1533 flags &= ~WINEDDBLT_DDFX;
1535 if (flags & WINEDDBLT_WAIT)
1536 flags &= ~WINEDDBLT_WAIT;
1538 if (flags & WINEDDBLT_ASYNC)
1540 static unsigned int once;
1542 if (!once++)
1543 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
1544 flags &= ~WINEDDBLT_ASYNC;
1547 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
1548 if (flags & WINEDDBLT_DONOTWAIT)
1550 static unsigned int once;
1552 if (!once++)
1553 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
1554 flags &= ~WINEDDBLT_DONOTWAIT;
1557 if (!device->d3d_initialized)
1559 WARN("D3D not initialized, using fallback.\n");
1560 goto cpu;
1563 if (flags & ~simple_blit)
1565 WARN("Using fallback for complex blit (%#x).\n", flags);
1566 goto fallback;
1569 if (src_surface && src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1570 src_swapchain = src_surface->container.u.swapchain;
1571 else
1572 src_swapchain = NULL;
1574 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
1575 dst_swapchain = dst_surface->container.u.swapchain;
1576 else
1577 dst_swapchain = NULL;
1579 /* This isn't strictly needed. FBO blits for example could deal with
1580 * cross-swapchain blits by first downloading the source to a texture
1581 * before switching to the destination context. We just have this here to
1582 * not have to deal with the issue, since cross-swapchain blits should be
1583 * rare. */
1584 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
1586 FIXME("Using fallback for cross-swapchain blit.\n");
1587 goto fallback;
1590 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1591 if (src_surface)
1592 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
1593 else
1594 src_ds_flags = 0;
1596 if (src_ds_flags || dst_ds_flags)
1598 if (flags & WINEDDBLT_DEPTHFILL)
1600 float depth;
1602 TRACE("Depth fill.\n");
1604 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
1605 return WINED3DERR_INVALIDCALL;
1607 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
1608 return WINED3D_OK;
1610 else
1612 /* Accessing depth / stencil surfaces is supposed to fail while in
1613 * a scene, except for fills, which seem to work. */
1614 if (device->inScene)
1616 WARN("Rejecting depth / stencil access while in scene.\n");
1617 return WINED3DERR_INVALIDCALL;
1620 if (src_ds_flags != dst_ds_flags)
1622 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
1623 return WINED3DERR_INVALIDCALL;
1626 if (src_rect.top || src_rect.left
1627 || src_rect.bottom != src_surface->resource.height
1628 || src_rect.right != src_surface->resource.width)
1630 WARN("Rejecting depth / stencil blit with invalid source rect %s.\n",
1631 wine_dbgstr_rect(&src_rect));
1632 return WINED3DERR_INVALIDCALL;
1635 if (dst_rect.top || dst_rect.left
1636 || dst_rect.bottom != dst_surface->resource.height
1637 || dst_rect.right != dst_surface->resource.width)
1639 WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n",
1640 wine_dbgstr_rect(&src_rect));
1641 return WINED3DERR_INVALIDCALL;
1644 if (src_surface->resource.height != dst_surface->resource.height
1645 || src_surface->resource.width != dst_surface->resource.width)
1647 WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n");
1648 return WINED3DERR_INVALIDCALL;
1651 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, &src_rect, dst_surface, &dst_rect)))
1652 return WINED3D_OK;
1655 else
1657 if (flags & WINEDDBLT_COLORFILL)
1659 WINED3DCOLORVALUE color;
1661 TRACE("Color fill.\n");
1663 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
1664 goto fallback;
1666 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
1667 return WINED3D_OK;
1669 else
1671 TRACE("Color blit.\n");
1673 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1674 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1675 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1677 TRACE("Using FBO blit.\n");
1679 surface_blt_fbo(device, filter,
1680 src_surface, SFLAG_INDRAWABLE, &src_rect,
1681 dst_surface, SFLAG_INDRAWABLE, &dst_rect);
1682 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
1683 return WINED3D_OK;
1686 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
1687 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1688 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1690 TRACE("Using arbfp blit.\n");
1692 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
1693 return WINED3D_OK;
1698 fallback:
1700 /* Special cases for render targets. */
1701 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1702 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1704 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1705 src_surface, &src_rect, flags, fx, filter)))
1706 return WINED3D_OK;
1709 cpu:
1711 /* For the rest call the X11 surface implementation. For render targets
1712 * this should be implemented OpenGL accelerated in BltOverride, other
1713 * blits are rather rare. */
1714 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1717 /* Do not call while under the GL lock. */
1718 HRESULT CDECL wined3d_surface_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
1719 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD trans)
1721 RECT src_rect, dst_rect;
1722 DWORD flags = 0;
1724 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect_in %s, trans %#x.\n",
1725 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect_in), trans);
1727 surface_get_rect(src_surface, src_rect_in, &src_rect);
1729 dst_rect.left = dst_x;
1730 dst_rect.top = dst_y;
1731 dst_rect.right = dst_x + src_rect.right - src_rect.left;
1732 dst_rect.bottom = dst_y + src_rect.bottom - src_rect.top;
1734 if (trans & WINEDDBLTFAST_SRCCOLORKEY)
1735 flags |= WINEDDBLT_KEYSRC;
1736 if (trans & WINEDDBLTFAST_DESTCOLORKEY)
1737 flags |= WINEDDBLT_KEYDEST;
1738 if (trans & WINEDDBLTFAST_WAIT)
1739 flags |= WINEDDBLT_WAIT;
1740 if (trans & WINEDDBLTFAST_DONOTWAIT)
1741 flags |= WINEDDBLT_DONOTWAIT;
1743 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, NULL, WINED3DTEXF_POINT);
1746 static HRESULT surface_set_mem(struct wined3d_surface *surface, void *mem)
1748 TRACE("surface %p, mem %p.\n", surface, mem);
1750 if (mem && mem != surface->resource.allocatedMemory)
1752 void *release = NULL;
1754 /* Do I have to copy the old surface content? */
1755 if (surface->flags & SFLAG_DIBSECTION)
1757 SelectObject(surface->hDC, surface->dib.holdbitmap);
1758 DeleteDC(surface->hDC);
1759 /* Release the DIB section. */
1760 DeleteObject(surface->dib.DIBsection);
1761 surface->dib.bitmap_data = NULL;
1762 surface->resource.allocatedMemory = NULL;
1763 surface->hDC = NULL;
1764 surface->flags &= ~SFLAG_DIBSECTION;
1766 else if (!(surface->flags & SFLAG_USERPTR))
1768 release = surface->resource.heapMemory;
1769 surface->resource.heapMemory = NULL;
1771 surface->resource.allocatedMemory = mem;
1772 surface->flags |= SFLAG_USERPTR;
1774 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
1775 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1777 /* For client textures OpenGL has to be notified. */
1778 if (surface->flags & SFLAG_CLIENT)
1779 surface_release_client_storage(surface);
1781 /* Now free the old memory if any. */
1782 HeapFree(GetProcessHeap(), 0, release);
1784 else if (surface->flags & SFLAG_USERPTR)
1786 /* HeapMemory should be NULL already. */
1787 if (surface->resource.heapMemory)
1788 ERR("User pointer surface has heap memory allocated.\n");
1790 if (!mem)
1792 surface->resource.allocatedMemory = NULL;
1793 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
1795 if (surface->flags & SFLAG_CLIENT)
1796 surface_release_client_storage(surface);
1798 surface_prepare_system_memory(surface);
1801 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1804 return WINED3D_OK;
1807 /* Context activation is done by the caller. */
1808 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1810 if (!surface->resource.heapMemory)
1812 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1813 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1814 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1817 ENTER_GL();
1818 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1819 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1820 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1821 surface->resource.size, surface->resource.allocatedMemory));
1822 checkGLcall("glGetBufferSubDataARB");
1823 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1824 checkGLcall("glDeleteBuffersARB");
1825 LEAVE_GL();
1827 surface->pbo = 0;
1828 surface->flags &= ~SFLAG_PBO;
1831 /* Do not call while under the GL lock. */
1832 static void surface_unload(struct wined3d_resource *resource)
1834 struct wined3d_surface *surface = surface_from_resource(resource);
1835 struct wined3d_renderbuffer_entry *entry, *entry2;
1836 struct wined3d_device *device = resource->device;
1837 const struct wined3d_gl_info *gl_info;
1838 struct wined3d_context *context;
1840 TRACE("surface %p.\n", surface);
1842 if (resource->pool == WINED3DPOOL_DEFAULT)
1844 /* Default pool resources are supposed to be destroyed before Reset is called.
1845 * Implicit resources stay however. So this means we have an implicit render target
1846 * or depth stencil. The content may be destroyed, but we still have to tear down
1847 * opengl resources, so we cannot leave early.
1849 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1850 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1851 * or the depth stencil into an FBO the texture or render buffer will be removed
1852 * and all flags get lost
1854 surface_init_sysmem(surface);
1856 else
1858 /* Load the surface into system memory */
1859 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1860 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
1862 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1863 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1864 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1866 context = context_acquire(device, NULL);
1867 gl_info = context->gl_info;
1869 /* Destroy PBOs, but load them into real sysmem before */
1870 if (surface->flags & SFLAG_PBO)
1871 surface_remove_pbo(surface, gl_info);
1873 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1874 * all application-created targets the application has to release the surface
1875 * before calling _Reset
1877 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1879 ENTER_GL();
1880 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1881 LEAVE_GL();
1882 list_remove(&entry->entry);
1883 HeapFree(GetProcessHeap(), 0, entry);
1885 list_init(&surface->renderbuffers);
1886 surface->current_renderbuffer = NULL;
1888 /* If we're in a texture, the texture name belongs to the texture.
1889 * Otherwise, destroy it. */
1890 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1892 ENTER_GL();
1893 glDeleteTextures(1, &surface->texture_name);
1894 surface->texture_name = 0;
1895 glDeleteTextures(1, &surface->texture_name_srgb);
1896 surface->texture_name_srgb = 0;
1897 LEAVE_GL();
1900 context_release(context);
1902 resource_unload(resource);
1905 static const struct wined3d_resource_ops surface_resource_ops =
1907 surface_unload,
1910 static const struct wined3d_surface_ops surface_ops =
1912 surface_private_setup,
1913 surface_cleanup,
1914 surface_realize_palette,
1915 surface_draw_overlay,
1916 surface_preload,
1917 surface_map,
1918 surface_unmap,
1919 surface_getdc,
1920 surface_flip,
1921 surface_set_mem,
1924 /*****************************************************************************
1925 * Initializes the GDI surface, aka creates the DIB section we render to
1926 * The DIB section creation is done by calling GetDC, which will create the
1927 * section and releasing the dc to allow the app to use it. The dib section
1928 * will stay until the surface is released
1930 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1931 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1932 * avoid confusion in the shared surface code.
1934 * Returns:
1935 * WINED3D_OK on success
1936 * The return values of called methods on failure
1938 *****************************************************************************/
1939 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1941 HRESULT hr;
1943 TRACE("surface %p.\n", surface);
1945 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1947 ERR("Overlays not yet supported by GDI surfaces.\n");
1948 return WINED3DERR_INVALIDCALL;
1951 /* Sysmem textures have memory already allocated - release it,
1952 * this avoids an unnecessary memcpy. */
1953 hr = surface_create_dib_section(surface);
1954 if (SUCCEEDED(hr))
1956 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1957 surface->resource.heapMemory = NULL;
1958 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1961 /* We don't mind the nonpow2 stuff in GDI. */
1962 surface->pow2Width = surface->resource.width;
1963 surface->pow2Height = surface->resource.height;
1965 return WINED3D_OK;
1968 static void surface_gdi_cleanup(struct wined3d_surface *surface)
1970 TRACE("surface %p.\n", surface);
1972 if (surface->flags & SFLAG_DIBSECTION)
1974 /* Release the DC. */
1975 SelectObject(surface->hDC, surface->dib.holdbitmap);
1976 DeleteDC(surface->hDC);
1977 /* Release the DIB section. */
1978 DeleteObject(surface->dib.DIBsection);
1979 surface->dib.bitmap_data = NULL;
1980 surface->resource.allocatedMemory = NULL;
1983 if (surface->flags & SFLAG_USERPTR)
1984 wined3d_surface_set_mem(surface, NULL);
1985 if (surface->overlay_dest)
1986 list_remove(&surface->overlay_entry);
1988 HeapFree(GetProcessHeap(), 0, surface->palette9);
1990 resource_cleanup(&surface->resource);
1993 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1995 struct wined3d_palette *palette = surface->palette;
1997 TRACE("surface %p.\n", surface);
1999 if (!palette) return;
2001 if (surface->flags & SFLAG_DIBSECTION)
2003 RGBQUAD col[256];
2004 unsigned int i;
2006 TRACE("Updating the DC's palette.\n");
2008 for (i = 0; i < 256; ++i)
2010 col[i].rgbRed = palette->palents[i].peRed;
2011 col[i].rgbGreen = palette->palents[i].peGreen;
2012 col[i].rgbBlue = palette->palents[i].peBlue;
2013 col[i].rgbReserved = 0;
2015 SetDIBColorTable(surface->hDC, 0, 256, col);
2018 /* Update the image because of the palette change. Some games like e.g.
2019 * Red Alert call SetEntries a lot to implement fading. */
2020 /* Tell the swapchain to update the screen. */
2021 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2023 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2024 if (surface == swapchain->front_buffer)
2026 x11_copy_to_screen(swapchain, NULL);
2031 static HRESULT gdi_surface_draw_overlay(struct wined3d_surface *surface)
2033 FIXME("GDI surfaces can't draw overlays yet.\n");
2034 return E_FAIL;
2037 static void gdi_surface_preload(struct wined3d_surface *surface)
2039 TRACE("surface %p.\n", surface);
2041 ERR("Preloading GDI surfaces is not supported.\n");
2044 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
2046 TRACE("surface %p, rect %s, flags %#x.\n",
2047 surface, wine_dbgstr_rect(rect), flags);
2049 if (!surface->resource.allocatedMemory)
2051 /* This happens on gdi surfaces if the application set a user pointer
2052 * and resets it. Recreate the DIB section. */
2053 surface_create_dib_section(surface);
2054 surface->resource.allocatedMemory = surface->dib.bitmap_data;
2058 static void gdi_surface_unmap(struct wined3d_surface *surface)
2060 TRACE("surface %p.\n", surface);
2062 /* Tell the swapchain to update the screen. */
2063 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2065 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2066 if (surface == swapchain->front_buffer)
2068 x11_copy_to_screen(swapchain, &surface->lockedRect);
2072 memset(&surface->lockedRect, 0, sizeof(RECT));
2075 static HRESULT gdi_surface_getdc(struct wined3d_surface *surface)
2077 WINED3DLOCKED_RECT lock;
2078 HRESULT hr;
2080 TRACE("surface %p.\n", surface);
2082 /* Should have a DIB section already. */
2083 if (!(surface->flags & SFLAG_DIBSECTION))
2085 WARN("DC not supported on this surface\n");
2086 return WINED3DERR_INVALIDCALL;
2089 /* Map the surface. */
2090 hr = wined3d_surface_map(surface, &lock, NULL, 0);
2091 if (FAILED(hr))
2092 ERR("Map failed, hr %#x.\n", hr);
2094 return hr;
2097 static HRESULT gdi_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override)
2099 TRACE("surface %p, override %p.\n", surface, override);
2101 return WINED3D_OK;
2104 static HRESULT gdi_surface_set_mem(struct wined3d_surface *surface, void *mem)
2106 TRACE("surface %p, mem %p.\n", surface, mem);
2108 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
2109 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2111 ERR("Not supported on render targets.\n");
2112 return WINED3DERR_INVALIDCALL;
2115 if (mem && mem != surface->resource.allocatedMemory)
2117 void *release = NULL;
2119 /* Do I have to copy the old surface content? */
2120 if (surface->flags & SFLAG_DIBSECTION)
2122 SelectObject(surface->hDC, surface->dib.holdbitmap);
2123 DeleteDC(surface->hDC);
2124 /* Release the DIB section. */
2125 DeleteObject(surface->dib.DIBsection);
2126 surface->dib.bitmap_data = NULL;
2127 surface->resource.allocatedMemory = NULL;
2128 surface->hDC = NULL;
2129 surface->flags &= ~SFLAG_DIBSECTION;
2131 else if (!(surface->flags & SFLAG_USERPTR))
2133 release = surface->resource.allocatedMemory;
2135 surface->resource.allocatedMemory = mem;
2136 surface->flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2138 /* Now free the old memory, if any. */
2139 HeapFree(GetProcessHeap(), 0, release);
2141 else if (surface->flags & SFLAG_USERPTR)
2143 /* Map() and GetDC() will re-create the dib section and allocated memory. */
2144 surface->resource.allocatedMemory = NULL;
2145 surface->flags &= ~SFLAG_USERPTR;
2148 return WINED3D_OK;
2151 static const struct wined3d_surface_ops gdi_surface_ops =
2153 gdi_surface_private_setup,
2154 surface_gdi_cleanup,
2155 gdi_surface_realize_palette,
2156 gdi_surface_draw_overlay,
2157 gdi_surface_preload,
2158 gdi_surface_map,
2159 gdi_surface_unmap,
2160 gdi_surface_getdc,
2161 gdi_surface_flip,
2162 gdi_surface_set_mem,
2165 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2167 GLuint *name;
2168 DWORD flag;
2170 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2172 if(srgb)
2174 name = &surface->texture_name_srgb;
2175 flag = SFLAG_INSRGBTEX;
2177 else
2179 name = &surface->texture_name;
2180 flag = SFLAG_INTEXTURE;
2183 if (!*name && new_name)
2185 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2186 * surface has no texture name yet. See if we can get rid of this. */
2187 if (surface->flags & flag)
2188 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2189 surface_modify_location(surface, flag, FALSE);
2192 *name = new_name;
2193 surface_force_reload(surface);
2196 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2198 TRACE("surface %p, target %#x.\n", surface, target);
2200 if (surface->texture_target != target)
2202 if (target == GL_TEXTURE_RECTANGLE_ARB)
2204 surface->flags &= ~SFLAG_NORMCOORD;
2206 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2208 surface->flags |= SFLAG_NORMCOORD;
2211 surface->texture_target = target;
2212 surface_force_reload(surface);
2215 /* Context activation is done by the caller. */
2216 void surface_bind(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
2218 TRACE("surface %p, gl_info %p, srgb %#x.\n", surface, gl_info, srgb);
2220 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2222 struct wined3d_texture *texture = surface->container.u.texture;
2224 TRACE("Passing to container (%p).\n", texture);
2225 texture->texture_ops->texture_bind(texture, gl_info, srgb);
2227 else
2229 if (surface->texture_level)
2231 ERR("Standalone surface %p is non-zero texture level %u.\n",
2232 surface, surface->texture_level);
2235 if (srgb)
2236 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2238 ENTER_GL();
2240 if (!surface->texture_name)
2242 glGenTextures(1, &surface->texture_name);
2243 checkGLcall("glGenTextures");
2245 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2247 glBindTexture(surface->texture_target, surface->texture_name);
2248 checkGLcall("glBindTexture");
2249 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2250 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2251 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2252 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2253 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2254 checkGLcall("glTexParameteri");
2256 else
2258 glBindTexture(surface->texture_target, surface->texture_name);
2259 checkGLcall("glBindTexture");
2262 LEAVE_GL();
2266 /* This call just downloads data, the caller is responsible for binding the
2267 * correct texture. */
2268 /* Context activation is done by the caller. */
2269 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2271 const struct wined3d_format *format = surface->resource.format;
2273 /* Only support read back of converted P8 surfaces. */
2274 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2276 FIXME("Readback conversion not supported for format %s.\n", debug_d3dformat(format->id));
2277 return;
2280 ENTER_GL();
2282 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2284 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2285 surface, surface->texture_level, format->glFormat, format->glType,
2286 surface->resource.allocatedMemory);
2288 if (surface->flags & SFLAG_PBO)
2290 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2291 checkGLcall("glBindBufferARB");
2292 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2293 checkGLcall("glGetCompressedTexImageARB");
2294 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2295 checkGLcall("glBindBufferARB");
2297 else
2299 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2300 surface->texture_level, surface->resource.allocatedMemory));
2301 checkGLcall("glGetCompressedTexImageARB");
2304 LEAVE_GL();
2306 else
2308 void *mem;
2309 GLenum gl_format = format->glFormat;
2310 GLenum gl_type = format->glType;
2311 int src_pitch = 0;
2312 int dst_pitch = 0;
2314 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2315 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2317 gl_format = GL_ALPHA;
2318 gl_type = GL_UNSIGNED_BYTE;
2321 if (surface->flags & SFLAG_NONPOW2)
2323 unsigned char alignment = surface->resource.device->surface_alignment;
2324 src_pitch = format->byte_count * surface->pow2Width;
2325 dst_pitch = wined3d_surface_get_pitch(surface);
2326 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2327 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2329 else
2331 mem = surface->resource.allocatedMemory;
2334 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2335 surface, surface->texture_level, gl_format, gl_type, mem);
2337 if (surface->flags & SFLAG_PBO)
2339 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2340 checkGLcall("glBindBufferARB");
2342 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2343 checkGLcall("glGetTexImage");
2345 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2346 checkGLcall("glBindBufferARB");
2348 else
2350 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2351 checkGLcall("glGetTexImage");
2353 LEAVE_GL();
2355 if (surface->flags & SFLAG_NONPOW2)
2357 const BYTE *src_data;
2358 BYTE *dst_data;
2359 UINT y;
2361 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2362 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2363 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2365 * We're doing this...
2367 * instead of boxing the texture :
2368 * |<-texture width ->| -->pow2width| /\
2369 * |111111111111111111| | |
2370 * |222 Texture 222222| boxed empty | texture height
2371 * |3333 Data 33333333| | |
2372 * |444444444444444444| | \/
2373 * ----------------------------------- |
2374 * | boxed empty | boxed empty | pow2height
2375 * | | | \/
2376 * -----------------------------------
2379 * we're repacking the data to the expected texture width
2381 * |<-texture width ->| -->pow2width| /\
2382 * |111111111111111111222222222222222| |
2383 * |222333333333333333333444444444444| texture height
2384 * |444444 | |
2385 * | | \/
2386 * | | |
2387 * | empty | pow2height
2388 * | | \/
2389 * -----------------------------------
2391 * == is the same as
2393 * |<-texture width ->| /\
2394 * |111111111111111111|
2395 * |222222222222222222|texture height
2396 * |333333333333333333|
2397 * |444444444444444444| \/
2398 * --------------------
2400 * this also means that any references to allocatedMemory should work with the data as if were a
2401 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2403 * internally the texture is still stored in a boxed format so any references to textureName will
2404 * get a boxed texture with width pow2width and not a texture of width resource.width.
2406 * Performance should not be an issue, because applications normally do not lock the surfaces when
2407 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2408 * and doesn't have to be re-read. */
2409 src_data = mem;
2410 dst_data = surface->resource.allocatedMemory;
2411 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2412 for (y = 1; y < surface->resource.height; ++y)
2414 /* skip the first row */
2415 src_data += src_pitch;
2416 dst_data += dst_pitch;
2417 memcpy(dst_data, src_data, dst_pitch);
2420 HeapFree(GetProcessHeap(), 0, mem);
2424 /* Surface has now been downloaded */
2425 surface->flags |= SFLAG_INSYSMEM;
2428 /* This call just uploads data, the caller is responsible for binding the
2429 * correct texture. */
2430 /* Context activation is done by the caller. */
2431 void surface_upload_data(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2432 const struct wined3d_format *format, const RECT *src_rect, UINT src_w, const POINT *dst_point,
2433 BOOL srgb, const struct wined3d_bo_address *data)
2435 UINT update_w = src_rect->right - src_rect->left;
2436 UINT update_h = src_rect->bottom - src_rect->top;
2438 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_w %u, dst_point %p, srgb %#x, data {%#x:%p}.\n",
2439 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_w,
2440 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2442 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2443 update_h *= format->heightscale;
2445 ENTER_GL();
2447 if (data->buffer_object)
2449 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2450 checkGLcall("glBindBufferARB");
2453 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2455 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2456 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2457 UINT src_pitch = wined3d_format_calculate_size(format, 1, src_w, 1);
2458 const BYTE *addr = data->addr;
2459 GLenum internal;
2461 addr += (src_rect->top / format->block_height) * src_pitch;
2462 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2464 if (srgb)
2465 internal = format->glGammaInternal;
2466 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2467 internal = format->rtInternal;
2468 else
2469 internal = format->glInternal;
2471 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2472 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2473 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2475 if (row_length == src_pitch)
2477 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2478 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2480 else
2482 UINT row, y;
2484 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2485 * can't use the unpack row length like below. */
2486 for (row = 0, y = dst_point->y; row < row_count; ++row)
2488 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2489 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2490 y += format->block_height;
2491 addr += src_pitch;
2494 checkGLcall("glCompressedTexSubImage2DARB");
2496 else
2498 const BYTE *addr = data->addr;
2500 addr += src_rect->top * src_w * format->byte_count;
2501 addr += src_rect->left * format->byte_count;
2503 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2504 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2505 update_w, update_h, format->glFormat, format->glType, addr);
2507 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_w);
2508 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2509 update_w, update_h, format->glFormat, format->glType, addr);
2510 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2511 checkGLcall("glTexSubImage2D");
2514 if (data->buffer_object)
2516 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2517 checkGLcall("glBindBufferARB");
2520 LEAVE_GL();
2522 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2524 struct wined3d_device *device = surface->resource.device;
2525 unsigned int i;
2527 for (i = 0; i < device->context_count; ++i)
2529 context_surface_update(device->contexts[i], surface);
2534 /* This call just allocates the texture, the caller is responsible for binding
2535 * the correct texture. */
2536 /* Context activation is done by the caller. */
2537 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2538 const struct wined3d_format *format, BOOL srgb)
2540 BOOL enable_client_storage = FALSE;
2541 GLsizei width = surface->pow2Width;
2542 GLsizei height = surface->pow2Height;
2543 const BYTE *mem = NULL;
2544 GLenum internal;
2546 if (srgb)
2548 internal = format->glGammaInternal;
2550 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2552 internal = format->rtInternal;
2554 else
2556 internal = format->glInternal;
2559 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2561 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",
2562 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2563 internal, width, height, format->glFormat, format->glType);
2565 ENTER_GL();
2567 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2569 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2570 || !surface->resource.allocatedMemory)
2572 /* In some cases we want to disable client storage.
2573 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2574 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2575 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2576 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2578 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2579 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2580 surface->flags &= ~SFLAG_CLIENT;
2581 enable_client_storage = TRUE;
2583 else
2585 surface->flags |= SFLAG_CLIENT;
2587 /* Point OpenGL to our allocated texture memory. Do not use
2588 * resource.allocatedMemory here because it might point into a
2589 * PBO. Instead use heapMemory, but get the alignment right. */
2590 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2591 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2595 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2597 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2598 internal, width, height, 0, surface->resource.size, mem));
2599 checkGLcall("glCompressedTexImage2DARB");
2601 else
2603 glTexImage2D(surface->texture_target, surface->texture_level,
2604 internal, width, height, 0, format->glFormat, format->glType, mem);
2605 checkGLcall("glTexImage2D");
2608 if(enable_client_storage) {
2609 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2610 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2612 LEAVE_GL();
2615 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2616 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2617 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2618 /* GL locking is done by the caller */
2619 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2621 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2622 struct wined3d_renderbuffer_entry *entry;
2623 GLuint renderbuffer = 0;
2624 unsigned int src_width, src_height;
2625 unsigned int width, height;
2627 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2629 width = rt->pow2Width;
2630 height = rt->pow2Height;
2632 else
2634 width = surface->pow2Width;
2635 height = surface->pow2Height;
2638 src_width = surface->pow2Width;
2639 src_height = surface->pow2Height;
2641 /* A depth stencil smaller than the render target is not valid */
2642 if (width > src_width || height > src_height) return;
2644 /* Remove any renderbuffer set if the sizes match */
2645 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2646 || (width == src_width && height == src_height))
2648 surface->current_renderbuffer = NULL;
2649 return;
2652 /* Look if we've already got a renderbuffer of the correct dimensions */
2653 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2655 if (entry->width == width && entry->height == height)
2657 renderbuffer = entry->id;
2658 surface->current_renderbuffer = entry;
2659 break;
2663 if (!renderbuffer)
2665 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2666 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2667 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2668 surface->resource.format->glInternal, width, height);
2670 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2671 entry->width = width;
2672 entry->height = height;
2673 entry->id = renderbuffer;
2674 list_add_head(&surface->renderbuffers, &entry->entry);
2676 surface->current_renderbuffer = entry;
2679 checkGLcall("set_compatible_renderbuffer");
2682 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2684 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2686 TRACE("surface %p.\n", surface);
2688 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2690 ERR("Surface %p is not on a swapchain.\n", surface);
2691 return GL_NONE;
2694 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2696 if (swapchain->render_to_fbo)
2698 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2699 return GL_COLOR_ATTACHMENT0;
2701 TRACE("Returning GL_BACK\n");
2702 return GL_BACK;
2704 else if (surface == swapchain->front_buffer)
2706 TRACE("Returning GL_FRONT\n");
2707 return GL_FRONT;
2710 FIXME("Higher back buffer, returning GL_BACK\n");
2711 return GL_BACK;
2714 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2715 void surface_add_dirty_rect(struct wined3d_surface *surface, const WINED3DBOX *dirty_rect)
2717 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2719 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2720 /* No partial locking for textures yet. */
2721 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2723 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2724 if (dirty_rect)
2726 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->Left);
2727 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->Top);
2728 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->Right);
2729 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->Bottom);
2731 else
2733 surface->dirtyRect.left = 0;
2734 surface->dirtyRect.top = 0;
2735 surface->dirtyRect.right = surface->resource.width;
2736 surface->dirtyRect.bottom = surface->resource.height;
2739 /* if the container is a texture then mark it dirty. */
2740 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2742 TRACE("Passing to container.\n");
2743 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2747 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2749 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2750 BOOL ck_changed;
2752 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2754 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2756 ERR("Not supported on scratch surfaces.\n");
2757 return WINED3DERR_INVALIDCALL;
2760 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2762 /* Reload if either the texture and sysmem have different ideas about the
2763 * color key, or the actual key values changed. */
2764 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2765 && (surface->glCKey.dwColorSpaceLowValue != surface->SrcBltCKey.dwColorSpaceLowValue
2766 || surface->glCKey.dwColorSpaceHighValue != surface->SrcBltCKey.dwColorSpaceHighValue)))
2768 TRACE("Reloading because of color keying\n");
2769 /* To perform the color key conversion we need a sysmem copy of
2770 * the surface. Make sure we have it. */
2772 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2773 /* Make sure the texture is reloaded because of the color key change,
2774 * this kills performance though :( */
2775 /* TODO: This is not necessarily needed with hw palettized texture support. */
2776 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2777 /* Switching color keying on / off may change the internal format. */
2778 if (ck_changed)
2779 surface_force_reload(surface);
2781 else if (!(surface->flags & flag))
2783 TRACE("Reloading because surface is dirty.\n");
2785 else
2787 TRACE("surface is already in texture\n");
2788 return WINED3D_OK;
2791 /* No partial locking for textures yet. */
2792 surface_load_location(surface, flag, NULL);
2793 surface_evict_sysmem(surface);
2795 return WINED3D_OK;
2798 /* See also float_16_to_32() in wined3d_private.h */
2799 static inline unsigned short float_32_to_16(const float *in)
2801 int exp = 0;
2802 float tmp = fabsf(*in);
2803 unsigned int mantissa;
2804 unsigned short ret;
2806 /* Deal with special numbers */
2807 if (*in == 0.0f)
2808 return 0x0000;
2809 if (isnan(*in))
2810 return 0x7c01;
2811 if (isinf(*in))
2812 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2814 if (tmp < powf(2, 10))
2818 tmp = tmp * 2.0f;
2819 exp--;
2820 } while (tmp < powf(2, 10));
2822 else if (tmp >= powf(2, 11))
2826 tmp /= 2.0f;
2827 exp++;
2828 } while (tmp >= powf(2, 11));
2831 mantissa = (unsigned int)tmp;
2832 if (tmp - mantissa >= 0.5f)
2833 ++mantissa; /* Round to nearest, away from zero. */
2835 exp += 10; /* Normalize the mantissa. */
2836 exp += 15; /* Exponent is encoded with excess 15. */
2838 if (exp > 30) /* too big */
2840 ret = 0x7c00; /* INF */
2842 else if (exp <= 0)
2844 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2845 while (exp <= 0)
2847 mantissa = mantissa >> 1;
2848 ++exp;
2850 ret = mantissa & 0x3ff;
2852 else
2854 ret = (exp << 10) | (mantissa & 0x3ff);
2857 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2858 return ret;
2861 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2863 ULONG refcount;
2865 TRACE("Surface %p, container %p of type %#x.\n",
2866 surface, surface->container.u.base, surface->container.type);
2868 switch (surface->container.type)
2870 case WINED3D_CONTAINER_TEXTURE:
2871 return wined3d_texture_incref(surface->container.u.texture);
2873 case WINED3D_CONTAINER_SWAPCHAIN:
2874 return wined3d_swapchain_incref(surface->container.u.swapchain);
2876 default:
2877 ERR("Unhandled container type %#x.\n", surface->container.type);
2878 case WINED3D_CONTAINER_NONE:
2879 break;
2882 refcount = InterlockedIncrement(&surface->resource.ref);
2883 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2885 return refcount;
2888 /* Do not call while under the GL lock. */
2889 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2891 ULONG refcount;
2893 TRACE("Surface %p, container %p of type %#x.\n",
2894 surface, surface->container.u.base, surface->container.type);
2896 switch (surface->container.type)
2898 case WINED3D_CONTAINER_TEXTURE:
2899 return wined3d_texture_decref(surface->container.u.texture);
2901 case WINED3D_CONTAINER_SWAPCHAIN:
2902 return wined3d_swapchain_decref(surface->container.u.swapchain);
2904 default:
2905 ERR("Unhandled container type %#x.\n", surface->container.type);
2906 case WINED3D_CONTAINER_NONE:
2907 break;
2910 refcount = InterlockedDecrement(&surface->resource.ref);
2911 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2913 if (!refcount)
2915 surface->surface_ops->surface_cleanup(surface);
2916 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2918 TRACE("Destroyed surface %p.\n", surface);
2919 HeapFree(GetProcessHeap(), 0, surface);
2922 return refcount;
2925 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2927 return resource_set_priority(&surface->resource, priority);
2930 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2932 return resource_get_priority(&surface->resource);
2935 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2937 TRACE("surface %p.\n", surface);
2939 surface->surface_ops->surface_preload(surface);
2942 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2944 TRACE("surface %p.\n", surface);
2946 return surface->resource.parent;
2949 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2951 TRACE("surface %p.\n", surface);
2953 return &surface->resource;
2956 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2958 TRACE("surface %p, flags %#x.\n", surface, flags);
2960 switch (flags)
2962 case WINEDDGBS_CANBLT:
2963 case WINEDDGBS_ISBLTDONE:
2964 return WINED3D_OK;
2966 default:
2967 return WINED3DERR_INVALIDCALL;
2971 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2973 TRACE("surface %p, flags %#x.\n", surface, flags);
2975 /* XXX: DDERR_INVALIDSURFACETYPE */
2977 switch (flags)
2979 case WINEDDGFS_CANFLIP:
2980 case WINEDDGFS_ISFLIPDONE:
2981 return WINED3D_OK;
2983 default:
2984 return WINED3DERR_INVALIDCALL;
2988 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2990 TRACE("surface %p.\n", surface);
2992 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2993 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2996 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2998 TRACE("surface %p.\n", surface);
3000 /* So far we don't lose anything :) */
3001 surface->flags &= ~SFLAG_LOST;
3002 return WINED3D_OK;
3005 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
3007 TRACE("surface %p, palette %p.\n", surface, palette);
3009 if (surface->palette == palette)
3011 TRACE("Nop palette change.\n");
3012 return WINED3D_OK;
3015 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
3016 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3018 surface->palette = palette;
3020 if (palette)
3022 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3023 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
3025 surface->surface_ops->surface_realize_palette(surface);
3028 return WINED3D_OK;
3031 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
3032 DWORD flags, const WINEDDCOLORKEY *color_key)
3034 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3036 if (flags & WINEDDCKEY_COLORSPACE)
3038 FIXME(" colorkey value not supported (%08x) !\n", flags);
3039 return WINED3DERR_INVALIDCALL;
3042 /* Dirtify the surface, but only if a key was changed. */
3043 if (color_key)
3045 switch (flags & ~WINEDDCKEY_COLORSPACE)
3047 case WINEDDCKEY_DESTBLT:
3048 surface->DestBltCKey = *color_key;
3049 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3050 break;
3052 case WINEDDCKEY_DESTOVERLAY:
3053 surface->DestOverlayCKey = *color_key;
3054 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3055 break;
3057 case WINEDDCKEY_SRCOVERLAY:
3058 surface->SrcOverlayCKey = *color_key;
3059 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3060 break;
3062 case WINEDDCKEY_SRCBLT:
3063 surface->SrcBltCKey = *color_key;
3064 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3065 break;
3068 else
3070 switch (flags & ~WINEDDCKEY_COLORSPACE)
3072 case WINEDDCKEY_DESTBLT:
3073 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3074 break;
3076 case WINEDDCKEY_DESTOVERLAY:
3077 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3078 break;
3080 case WINEDDCKEY_SRCOVERLAY:
3081 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3082 break;
3084 case WINEDDCKEY_SRCBLT:
3085 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3086 break;
3090 return WINED3D_OK;
3093 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3095 TRACE("surface %p.\n", surface);
3097 return surface->palette;
3100 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3102 const struct wined3d_format *format = surface->resource.format;
3103 DWORD pitch;
3105 TRACE("surface %p.\n", surface);
3107 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3109 /* Since compressed formats are block based, pitch means the amount of
3110 * bytes to the next row of block rather than the next row of pixels. */
3111 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3112 pitch = row_block_count * format->block_byte_count;
3114 else
3116 unsigned char alignment = surface->resource.device->surface_alignment;
3117 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3118 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3121 TRACE("Returning %u.\n", pitch);
3123 return pitch;
3126 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3128 TRACE("surface %p, mem %p.\n", surface, mem);
3130 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3132 WARN("Surface is locked or the DC is in use.\n");
3133 return WINED3DERR_INVALIDCALL;
3136 return surface->surface_ops->surface_set_mem(surface, mem);
3139 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3141 LONG w, h;
3143 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3145 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3147 WARN("Not an overlay surface.\n");
3148 return WINEDDERR_NOTAOVERLAYSURFACE;
3151 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3152 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3153 surface->overlay_destrect.left = x;
3154 surface->overlay_destrect.top = y;
3155 surface->overlay_destrect.right = x + w;
3156 surface->overlay_destrect.bottom = y + h;
3158 surface->surface_ops->surface_draw_overlay(surface);
3160 return WINED3D_OK;
3163 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3165 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3167 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3169 TRACE("Not an overlay surface.\n");
3170 return WINEDDERR_NOTAOVERLAYSURFACE;
3173 if (!surface->overlay_dest)
3175 TRACE("Overlay not visible.\n");
3176 *x = 0;
3177 *y = 0;
3178 return WINEDDERR_OVERLAYNOTVISIBLE;
3181 *x = surface->overlay_destrect.left;
3182 *y = surface->overlay_destrect.top;
3184 TRACE("Returning position %d, %d.\n", *x, *y);
3186 return WINED3D_OK;
3189 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3190 DWORD flags, struct wined3d_surface *ref)
3192 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3194 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3196 TRACE("Not an overlay surface.\n");
3197 return WINEDDERR_NOTAOVERLAYSURFACE;
3200 return WINED3D_OK;
3203 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3204 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3206 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3207 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3209 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3211 WARN("Not an overlay surface.\n");
3212 return WINEDDERR_NOTAOVERLAYSURFACE;
3214 else if (!dst_surface)
3216 WARN("Dest surface is NULL.\n");
3217 return WINED3DERR_INVALIDCALL;
3220 if (src_rect)
3222 surface->overlay_srcrect = *src_rect;
3224 else
3226 surface->overlay_srcrect.left = 0;
3227 surface->overlay_srcrect.top = 0;
3228 surface->overlay_srcrect.right = surface->resource.width;
3229 surface->overlay_srcrect.bottom = surface->resource.height;
3232 if (dst_rect)
3234 surface->overlay_destrect = *dst_rect;
3236 else
3238 surface->overlay_destrect.left = 0;
3239 surface->overlay_destrect.top = 0;
3240 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3241 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3244 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3246 list_remove(&surface->overlay_entry);
3249 if (flags & WINEDDOVER_SHOW)
3251 if (surface->overlay_dest != dst_surface)
3253 surface->overlay_dest = dst_surface;
3254 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3257 else if (flags & WINEDDOVER_HIDE)
3259 /* tests show that the rectangles are erased on hide */
3260 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3261 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3262 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3263 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3264 surface->overlay_dest = NULL;
3267 surface->surface_ops->surface_draw_overlay(surface);
3269 return WINED3D_OK;
3272 HRESULT CDECL wined3d_surface_set_clipper(struct wined3d_surface *surface, struct wined3d_clipper *clipper)
3274 TRACE("surface %p, clipper %p.\n", surface, clipper);
3276 surface->clipper = clipper;
3278 return WINED3D_OK;
3281 struct wined3d_clipper * CDECL wined3d_surface_get_clipper(const struct wined3d_surface *surface)
3283 TRACE("surface %p.\n", surface);
3285 return surface->clipper;
3288 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3290 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3292 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3294 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3296 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3297 return WINED3DERR_INVALIDCALL;
3300 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3301 surface->pow2Width, surface->pow2Height);
3302 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3303 surface->resource.format = format;
3305 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3306 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3307 format->glFormat, format->glInternal, format->glType);
3309 return WINED3D_OK;
3312 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3313 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3315 unsigned short *dst_s;
3316 const float *src_f;
3317 unsigned int x, y;
3319 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3321 for (y = 0; y < h; ++y)
3323 src_f = (const float *)(src + y * pitch_in);
3324 dst_s = (unsigned short *) (dst + y * pitch_out);
3325 for (x = 0; x < w; ++x)
3327 dst_s[x] = float_32_to_16(src_f + x);
3332 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3333 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3335 static const unsigned char convert_5to8[] =
3337 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3338 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3339 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3340 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3342 static const unsigned char convert_6to8[] =
3344 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3345 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3346 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3347 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3348 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3349 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3350 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3351 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3353 unsigned int x, y;
3355 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3357 for (y = 0; y < h; ++y)
3359 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3360 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3361 for (x = 0; x < w; ++x)
3363 WORD pixel = src_line[x];
3364 dst_line[x] = 0xff000000
3365 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3366 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3367 | convert_5to8[(pixel & 0x001f)];
3372 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3373 * in both cases we're just setting the X / Alpha channel to 0xff. */
3374 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3375 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3377 unsigned int x, y;
3379 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3381 for (y = 0; y < h; ++y)
3383 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3384 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3386 for (x = 0; x < w; ++x)
3388 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3393 static inline BYTE cliptobyte(int x)
3395 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3398 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3399 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3401 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3402 unsigned int x, y;
3404 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3406 for (y = 0; y < h; ++y)
3408 const BYTE *src_line = src + y * pitch_in;
3409 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3410 for (x = 0; x < w; ++x)
3412 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3413 * C = Y - 16; D = U - 128; E = V - 128;
3414 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3415 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3416 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3417 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3418 * U and V are shared between the pixels. */
3419 if (!(x & 1)) /* For every even pixel, read new U and V. */
3421 d = (int) src_line[1] - 128;
3422 e = (int) src_line[3] - 128;
3423 r2 = 409 * e + 128;
3424 g2 = - 100 * d - 208 * e + 128;
3425 b2 = 516 * d + 128;
3427 c2 = 298 * ((int) src_line[0] - 16);
3428 dst_line[x] = 0xff000000
3429 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3430 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3431 | cliptobyte((c2 + b2) >> 8); /* blue */
3432 /* Scale RGB values to 0..255 range,
3433 * then clip them if still not in range (may be negative),
3434 * then shift them within DWORD if necessary. */
3435 src_line += 2;
3440 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3441 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3443 unsigned int x, y;
3444 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3446 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3448 for (y = 0; y < h; ++y)
3450 const BYTE *src_line = src + y * pitch_in;
3451 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3452 for (x = 0; x < w; ++x)
3454 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3455 * C = Y - 16; D = U - 128; E = V - 128;
3456 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3457 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3458 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3459 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3460 * U and V are shared between the pixels. */
3461 if (!(x & 1)) /* For every even pixel, read new U and V. */
3463 d = (int) src_line[1] - 128;
3464 e = (int) src_line[3] - 128;
3465 r2 = 409 * e + 128;
3466 g2 = - 100 * d - 208 * e + 128;
3467 b2 = 516 * d + 128;
3469 c2 = 298 * ((int) src_line[0] - 16);
3470 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3471 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3472 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3473 /* Scale RGB values to 0..255 range,
3474 * then clip them if still not in range (may be negative),
3475 * then shift them within DWORD if necessary. */
3476 src_line += 2;
3481 struct d3dfmt_convertor_desc
3483 enum wined3d_format_id from, to;
3484 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3487 static const struct d3dfmt_convertor_desc convertors[] =
3489 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3490 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3491 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3492 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3493 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3494 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3497 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3498 enum wined3d_format_id to)
3500 unsigned int i;
3502 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3504 if (convertors[i].from == from && convertors[i].to == to)
3505 return &convertors[i];
3508 return NULL;
3511 /*****************************************************************************
3512 * surface_convert_format
3514 * Creates a duplicate of a surface in a different format. Is used by Blt to
3515 * blit between surfaces with different formats.
3517 * Parameters
3518 * source: Source surface
3519 * fmt: Requested destination format
3521 *****************************************************************************/
3522 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3524 const struct d3dfmt_convertor_desc *conv;
3525 WINED3DLOCKED_RECT lock_src, lock_dst;
3526 struct wined3d_surface *ret = NULL;
3527 HRESULT hr;
3529 conv = find_convertor(source->resource.format->id, to_fmt);
3530 if (!conv)
3532 FIXME("Cannot find a conversion function from format %s to %s.\n",
3533 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3534 return NULL;
3537 wined3d_surface_create(source->resource.device, source->resource.width,
3538 source->resource.height, to_fmt, TRUE /* lockable */, TRUE /* discard */, 0 /* level */,
3539 0 /* usage */, WINED3DPOOL_SCRATCH, WINED3DMULTISAMPLE_NONE /* TODO: Multisampled conversion */,
3540 0 /* MultiSampleQuality */, source->surface_type, NULL /* parent */, &wined3d_null_parent_ops, &ret);
3541 if (!ret)
3543 ERR("Failed to create a destination surface for conversion.\n");
3544 return NULL;
3547 memset(&lock_src, 0, sizeof(lock_src));
3548 memset(&lock_dst, 0, sizeof(lock_dst));
3550 hr = wined3d_surface_map(source, &lock_src, NULL, WINED3DLOCK_READONLY);
3551 if (FAILED(hr))
3553 ERR("Failed to lock the source surface.\n");
3554 wined3d_surface_decref(ret);
3555 return NULL;
3557 hr = wined3d_surface_map(ret, &lock_dst, NULL, WINED3DLOCK_READONLY);
3558 if (FAILED(hr))
3560 ERR("Failed to lock the destination surface.\n");
3561 wined3d_surface_unmap(source);
3562 wined3d_surface_decref(ret);
3563 return NULL;
3566 conv->convert(lock_src.pBits, lock_dst.pBits, lock_src.Pitch, lock_dst.Pitch,
3567 source->resource.width, source->resource.height);
3569 wined3d_surface_unmap(ret);
3570 wined3d_surface_unmap(source);
3572 return ret;
3575 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3576 unsigned int bpp, UINT pitch, DWORD color)
3578 BYTE *first;
3579 int x, y;
3581 /* Do first row */
3583 #define COLORFILL_ROW(type) \
3584 do { \
3585 type *d = (type *)buf; \
3586 for (x = 0; x < width; ++x) \
3587 d[x] = (type)color; \
3588 } while(0)
3590 switch (bpp)
3592 case 1:
3593 COLORFILL_ROW(BYTE);
3594 break;
3596 case 2:
3597 COLORFILL_ROW(WORD);
3598 break;
3600 case 3:
3602 BYTE *d = buf;
3603 for (x = 0; x < width; ++x, d += 3)
3605 d[0] = (color ) & 0xFF;
3606 d[1] = (color >> 8) & 0xFF;
3607 d[2] = (color >> 16) & 0xFF;
3609 break;
3611 case 4:
3612 COLORFILL_ROW(DWORD);
3613 break;
3615 default:
3616 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3617 return WINED3DERR_NOTAVAILABLE;
3620 #undef COLORFILL_ROW
3622 /* Now copy first row. */
3623 first = buf;
3624 for (y = 1; y < height; ++y)
3626 buf += pitch;
3627 memcpy(buf, first, width * bpp);
3630 return WINED3D_OK;
3633 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3635 TRACE("surface %p.\n", surface);
3637 if (!(surface->flags & SFLAG_LOCKED))
3639 WARN("Trying to unmap unmapped surface.\n");
3640 return WINEDDERR_NOTLOCKED;
3642 surface->flags &= ~SFLAG_LOCKED;
3644 surface->surface_ops->surface_unmap(surface);
3646 return WINED3D_OK;
3649 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3650 WINED3DLOCKED_RECT *locked_rect, const RECT *rect, DWORD flags)
3652 TRACE("surface %p, locked_rect %p, rect %s, flags %#x.\n",
3653 surface, locked_rect, wine_dbgstr_rect(rect), flags);
3655 if (surface->flags & SFLAG_LOCKED)
3657 WARN("Surface is already mapped.\n");
3658 return WINED3DERR_INVALIDCALL;
3660 surface->flags |= SFLAG_LOCKED;
3662 if (!(surface->flags & SFLAG_LOCKABLE))
3663 WARN("Trying to lock unlockable surface.\n");
3665 surface->surface_ops->surface_map(surface, rect, flags);
3667 locked_rect->Pitch = wined3d_surface_get_pitch(surface);
3669 if (!rect)
3671 locked_rect->pBits = surface->resource.allocatedMemory;
3672 surface->lockedRect.left = 0;
3673 surface->lockedRect.top = 0;
3674 surface->lockedRect.right = surface->resource.width;
3675 surface->lockedRect.bottom = surface->resource.height;
3677 else
3679 const struct wined3d_format *format = surface->resource.format;
3681 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3683 /* Compressed textures are block based, so calculate the offset of
3684 * the block that contains the top-left pixel of the locked rectangle. */
3685 locked_rect->pBits = surface->resource.allocatedMemory
3686 + ((rect->top / format->block_height) * locked_rect->Pitch)
3687 + ((rect->left / format->block_width) * format->block_byte_count);
3689 else
3691 locked_rect->pBits = surface->resource.allocatedMemory
3692 + (locked_rect->Pitch * rect->top)
3693 + (rect->left * format->byte_count);
3695 surface->lockedRect.left = rect->left;
3696 surface->lockedRect.top = rect->top;
3697 surface->lockedRect.right = rect->right;
3698 surface->lockedRect.bottom = rect->bottom;
3701 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3702 TRACE("Returning memory %p, pitch %u.\n", locked_rect->pBits, locked_rect->Pitch);
3704 return WINED3D_OK;
3707 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3709 HRESULT hr;
3711 TRACE("surface %p, dc %p.\n", surface, dc);
3713 if (surface->flags & SFLAG_USERPTR)
3715 ERR("Not supported on surfaces with application-provided memory.\n");
3716 return WINEDDERR_NODC;
3719 /* Give more detailed info for ddraw. */
3720 if (surface->flags & SFLAG_DCINUSE)
3721 return WINEDDERR_DCALREADYCREATED;
3723 /* Can't GetDC if the surface is locked. */
3724 if (surface->flags & SFLAG_LOCKED)
3725 return WINED3DERR_INVALIDCALL;
3727 hr = surface->surface_ops->surface_getdc(surface);
3728 if (FAILED(hr))
3729 return hr;
3731 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3732 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3734 /* GetDC on palettized formats is unsupported in D3D9, and the method
3735 * is missing in D3D8, so this should only be used for DX <=7
3736 * surfaces (with non-device palettes). */
3737 const PALETTEENTRY *pal = NULL;
3739 if (surface->palette)
3741 pal = surface->palette->palents;
3743 else
3745 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3746 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3748 if (dds_primary && dds_primary->palette)
3749 pal = dds_primary->palette->palents;
3752 if (pal)
3754 RGBQUAD col[256];
3755 unsigned int i;
3757 for (i = 0; i < 256; ++i)
3759 col[i].rgbRed = pal[i].peRed;
3760 col[i].rgbGreen = pal[i].peGreen;
3761 col[i].rgbBlue = pal[i].peBlue;
3762 col[i].rgbReserved = 0;
3764 SetDIBColorTable(surface->hDC, 0, 256, col);
3768 surface->flags |= SFLAG_DCINUSE;
3770 *dc = surface->hDC;
3771 TRACE("Returning dc %p.\n", *dc);
3773 return WINED3D_OK;
3776 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3778 TRACE("surface %p, dc %p.\n", surface, dc);
3780 if (!(surface->flags & SFLAG_DCINUSE))
3781 return WINEDDERR_NODC;
3783 if (surface->hDC != dc)
3785 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3786 dc, surface->hDC);
3787 return WINEDDERR_NODC;
3790 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3792 /* Copy the contents of the DIB over to the PBO. */
3793 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->dib.bitmap_size);
3796 /* We locked first, so unlock now. */
3797 wined3d_surface_unmap(surface);
3799 surface->flags &= ~SFLAG_DCINUSE;
3801 return WINED3D_OK;
3804 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3806 struct wined3d_swapchain *swapchain;
3807 HRESULT hr;
3809 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3811 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
3813 ERR("Flipped surface is not on a swapchain.\n");
3814 return WINEDDERR_NOTFLIPPABLE;
3816 swapchain = surface->container.u.swapchain;
3818 hr = surface->surface_ops->surface_flip(surface, override);
3819 if (FAILED(hr))
3820 return hr;
3822 /* Just overwrite the swapchain presentation interval. This is ok because
3823 * only ddraw apps can call Flip, and only d3d8 and d3d9 applications
3824 * specify the presentation interval. */
3825 if (!(flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)))
3826 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
3827 else if (flags & WINEDDFLIP_NOVSYNC)
3828 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3829 else if (flags & WINEDDFLIP_INTERVAL2)
3830 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
3831 else if (flags & WINEDDFLIP_INTERVAL3)
3832 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
3833 else
3834 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3836 return wined3d_swapchain_present(swapchain, NULL, NULL, swapchain->win_handle, NULL, 0);
3839 /* Do not call while under the GL lock. */
3840 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3842 struct wined3d_device *device = surface->resource.device;
3844 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3846 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3848 struct wined3d_texture *texture = surface->container.u.texture;
3850 TRACE("Passing to container (%p).\n", texture);
3851 texture->texture_ops->texture_preload(texture, srgb);
3853 else
3855 struct wined3d_context *context = NULL;
3857 TRACE("(%p) : About to load surface\n", surface);
3859 if (!device->isInDraw) context = context_acquire(device, NULL);
3861 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3862 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3864 if (palette9_changed(surface))
3866 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
3867 /* TODO: This is not necessarily needed with hw palettized texture support */
3868 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3869 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
3870 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
3874 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3876 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3878 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3879 GLclampf tmp;
3880 tmp = 0.9f;
3881 ENTER_GL();
3882 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3883 LEAVE_GL();
3886 if (context) context_release(context);
3890 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3892 if (!surface->resource.allocatedMemory)
3894 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3895 surface->resource.size + RESOURCE_ALIGNMENT);
3896 if (!surface->resource.heapMemory)
3898 ERR("Out of memory\n");
3899 return FALSE;
3901 surface->resource.allocatedMemory =
3902 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
3904 else
3906 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
3909 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3911 return TRUE;
3914 /* Read the framebuffer back into the surface */
3915 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
3917 struct wined3d_device *device = surface->resource.device;
3918 const struct wined3d_gl_info *gl_info;
3919 struct wined3d_context *context;
3920 BYTE *mem;
3921 GLint fmt;
3922 GLint type;
3923 BYTE *row, *top, *bottom;
3924 int i;
3925 BOOL bpp;
3926 RECT local_rect;
3927 BOOL srcIsUpsideDown;
3928 GLint rowLen = 0;
3929 GLint skipPix = 0;
3930 GLint skipRow = 0;
3932 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
3933 static BOOL warned = FALSE;
3934 if(!warned) {
3935 ERR("The application tries to lock the render target, but render target locking is disabled\n");
3936 warned = TRUE;
3938 return;
3941 context = context_acquire(device, surface);
3942 context_apply_blit_state(context, device);
3943 gl_info = context->gl_info;
3945 ENTER_GL();
3947 /* Select the correct read buffer, and give some debug output.
3948 * There is no need to keep track of the current read buffer or reset it, every part of the code
3949 * that reads sets the read buffer as desired.
3951 if (surface_is_offscreen(surface))
3953 /* Mapping the primary render target which is not on a swapchain.
3954 * Read from the back buffer. */
3955 TRACE("Mapping offscreen render target.\n");
3956 glReadBuffer(device->offscreenBuffer);
3957 srcIsUpsideDown = TRUE;
3959 else
3961 /* Onscreen surfaces are always part of a swapchain */
3962 GLenum buffer = surface_get_gl_buffer(surface);
3963 TRACE("Mapping %#x buffer.\n", buffer);
3964 glReadBuffer(buffer);
3965 checkGLcall("glReadBuffer");
3966 srcIsUpsideDown = FALSE;
3969 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
3970 if (!rect)
3972 local_rect.left = 0;
3973 local_rect.top = 0;
3974 local_rect.right = surface->resource.width;
3975 local_rect.bottom = surface->resource.height;
3977 else
3979 local_rect = *rect;
3981 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
3983 switch (surface->resource.format->id)
3985 case WINED3DFMT_P8_UINT:
3987 if (primary_render_target_is_p8(device))
3989 /* In case of P8 render targets the index is stored in the alpha component */
3990 fmt = GL_ALPHA;
3991 type = GL_UNSIGNED_BYTE;
3992 mem = dest;
3993 bpp = surface->resource.format->byte_count;
3995 else
3997 /* GL can't return palettized data, so read ARGB pixels into a
3998 * separate block of memory and convert them into palettized format
3999 * in software. Slow, but if the app means to use palettized render
4000 * targets and locks it...
4002 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
4003 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
4004 * for the color channels when palettizing the colors.
4006 fmt = GL_RGB;
4007 type = GL_UNSIGNED_BYTE;
4008 pitch *= 3;
4009 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
4010 if (!mem)
4012 ERR("Out of memory\n");
4013 LEAVE_GL();
4014 return;
4016 bpp = surface->resource.format->byte_count * 3;
4019 break;
4021 default:
4022 mem = dest;
4023 fmt = surface->resource.format->glFormat;
4024 type = surface->resource.format->glType;
4025 bpp = surface->resource.format->byte_count;
4028 if (surface->flags & SFLAG_PBO)
4030 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4031 checkGLcall("glBindBufferARB");
4032 if (mem)
4034 ERR("mem not null for pbo -- unexpected\n");
4035 mem = NULL;
4039 /* Save old pixel store pack state */
4040 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4041 checkGLcall("glGetIntegerv");
4042 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4043 checkGLcall("glGetIntegerv");
4044 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4045 checkGLcall("glGetIntegerv");
4047 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4048 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4049 checkGLcall("glPixelStorei");
4050 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4051 checkGLcall("glPixelStorei");
4052 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4053 checkGLcall("glPixelStorei");
4055 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4056 local_rect.right - local_rect.left,
4057 local_rect.bottom - local_rect.top,
4058 fmt, type, mem);
4059 checkGLcall("glReadPixels");
4061 /* Reset previous pixel store pack state */
4062 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4063 checkGLcall("glPixelStorei");
4064 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4065 checkGLcall("glPixelStorei");
4066 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4067 checkGLcall("glPixelStorei");
4069 if (surface->flags & SFLAG_PBO)
4071 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4072 checkGLcall("glBindBufferARB");
4074 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4075 * to get a pointer to it and perform the flipping in software. This is a lot
4076 * faster than calling glReadPixels for each line. In case we want more speed
4077 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4078 if (!srcIsUpsideDown)
4080 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4081 checkGLcall("glBindBufferARB");
4083 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4084 checkGLcall("glMapBufferARB");
4088 /* TODO: Merge this with the palettization loop below for P8 targets */
4089 if(!srcIsUpsideDown) {
4090 UINT len, off;
4091 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4092 Flip the lines in software */
4093 len = (local_rect.right - local_rect.left) * bpp;
4094 off = local_rect.left * bpp;
4096 row = HeapAlloc(GetProcessHeap(), 0, len);
4097 if(!row) {
4098 ERR("Out of memory\n");
4099 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4100 HeapFree(GetProcessHeap(), 0, mem);
4101 LEAVE_GL();
4102 return;
4105 top = mem + pitch * local_rect.top;
4106 bottom = mem + pitch * (local_rect.bottom - 1);
4107 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4108 memcpy(row, top + off, len);
4109 memcpy(top + off, bottom + off, len);
4110 memcpy(bottom + off, row, len);
4111 top += pitch;
4112 bottom -= pitch;
4114 HeapFree(GetProcessHeap(), 0, row);
4116 /* Unmap the temp PBO buffer */
4117 if (surface->flags & SFLAG_PBO)
4119 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4120 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4124 LEAVE_GL();
4125 context_release(context);
4127 /* For P8 textures we need to perform an inverse palette lookup. This is
4128 * done by searching for a palette index which matches the RGB value.
4129 * Note this isn't guaranteed to work when there are multiple entries for
4130 * the same color but we have no choice. In case of P8 render targets,
4131 * the index is stored in the alpha component so no conversion is needed. */
4132 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4134 const PALETTEENTRY *pal = NULL;
4135 DWORD width = pitch / 3;
4136 int x, y, c;
4138 if (surface->palette)
4140 pal = surface->palette->palents;
4142 else
4144 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4145 HeapFree(GetProcessHeap(), 0, mem);
4146 return;
4149 for(y = local_rect.top; y < local_rect.bottom; y++) {
4150 for(x = local_rect.left; x < local_rect.right; x++) {
4151 /* start lines pixels */
4152 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4153 const BYTE *green = blue + 1;
4154 const BYTE *red = green + 1;
4156 for(c = 0; c < 256; c++) {
4157 if(*red == pal[c].peRed &&
4158 *green == pal[c].peGreen &&
4159 *blue == pal[c].peBlue)
4161 *((BYTE *) dest + y * width + x) = c;
4162 break;
4167 HeapFree(GetProcessHeap(), 0, mem);
4171 /* Read the framebuffer contents into a texture */
4172 static void read_from_framebuffer_texture(struct wined3d_surface *surface, BOOL srgb)
4174 struct wined3d_device *device = surface->resource.device;
4175 const struct wined3d_gl_info *gl_info;
4176 struct wined3d_context *context;
4178 if (!surface_is_offscreen(surface))
4180 /* We would need to flip onscreen surfaces, but there's no efficient
4181 * way to do that here. It makes more sense for the caller to
4182 * explicitly go through sysmem. */
4183 ERR("Not supported for onscreen targets.\n");
4184 return;
4187 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
4188 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
4189 * states in the stateblock, and no driver was found yet that had bugs in that regard.
4191 context = context_acquire(device, surface);
4192 gl_info = context->gl_info;
4193 device_invalidate_state(device, STATE_FRAMEBUFFER);
4195 surface_prepare_texture(surface, gl_info, srgb);
4196 surface_bind_and_dirtify(surface, gl_info, srgb);
4198 TRACE("Reading back offscreen render target %p.\n", surface);
4200 ENTER_GL();
4202 glReadBuffer(device->offscreenBuffer);
4203 checkGLcall("glReadBuffer");
4205 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4206 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4207 checkGLcall("glCopyTexSubImage2D");
4209 LEAVE_GL();
4211 context_release(context);
4214 /* Context activation is done by the caller. */
4215 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4216 const struct wined3d_gl_info *gl_info, BOOL srgb)
4218 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4219 CONVERT_TYPES convert;
4220 struct wined3d_format format;
4222 if (surface->flags & alloc_flag) return;
4224 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4225 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4226 else surface->flags &= ~SFLAG_CONVERTED;
4228 surface_bind_and_dirtify(surface, gl_info, srgb);
4229 surface_allocate_surface(surface, gl_info, &format, srgb);
4230 surface->flags |= alloc_flag;
4233 /* Context activation is done by the caller. */
4234 void surface_prepare_texture(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
4236 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4238 struct wined3d_texture *texture = surface->container.u.texture;
4239 UINT sub_count = texture->level_count * texture->layer_count;
4240 UINT i;
4242 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4244 for (i = 0; i < sub_count; ++i)
4246 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4247 surface_prepare_texture_internal(s, gl_info, srgb);
4250 return;
4253 surface_prepare_texture_internal(surface, gl_info, srgb);
4256 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4257 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4259 struct wined3d_device *device = surface->resource.device;
4260 UINT pitch = wined3d_surface_get_pitch(surface);
4261 const struct wined3d_gl_info *gl_info;
4262 struct wined3d_context *context;
4263 RECT local_rect;
4264 UINT w, h;
4266 surface_get_rect(surface, rect, &local_rect);
4268 mem += local_rect.top * pitch + local_rect.left * bpp;
4269 w = local_rect.right - local_rect.left;
4270 h = local_rect.bottom - local_rect.top;
4272 /* Activate the correct context for the render target */
4273 context = context_acquire(device, surface);
4274 context_apply_blit_state(context, device);
4275 gl_info = context->gl_info;
4277 ENTER_GL();
4279 if (!surface_is_offscreen(surface))
4281 GLenum buffer = surface_get_gl_buffer(surface);
4282 TRACE("Unlocking %#x buffer.\n", buffer);
4283 context_set_draw_buffer(context, buffer);
4285 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4286 glPixelZoom(1.0f, -1.0f);
4288 else
4290 /* Primary offscreen render target */
4291 TRACE("Offscreen render target.\n");
4292 context_set_draw_buffer(context, device->offscreenBuffer);
4294 glPixelZoom(1.0f, 1.0f);
4297 glRasterPos3i(local_rect.left, local_rect.top, 1);
4298 checkGLcall("glRasterPos3i");
4300 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4301 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4303 if (surface->flags & SFLAG_PBO)
4305 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4306 checkGLcall("glBindBufferARB");
4309 glDrawPixels(w, h, fmt, type, mem);
4310 checkGLcall("glDrawPixels");
4312 if (surface->flags & SFLAG_PBO)
4314 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4315 checkGLcall("glBindBufferARB");
4318 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4319 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4321 LEAVE_GL();
4323 if (wined3d_settings.strict_draw_ordering
4324 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4325 && surface->container.u.swapchain->front_buffer == surface))
4326 wglFlush();
4328 context_release(context);
4331 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4332 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4334 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4335 const struct wined3d_device *device = surface->resource.device;
4336 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4337 BOOL blit_supported = FALSE;
4339 /* Copy the default values from the surface. Below we might perform fixups */
4340 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4341 *format = *surface->resource.format;
4342 *convert = NO_CONVERSION;
4344 /* Ok, now look if we have to do any conversion */
4345 switch (surface->resource.format->id)
4347 case WINED3DFMT_P8_UINT:
4348 /* Below the call to blit_supported is disabled for Wine 1.2
4349 * because the function isn't operating correctly yet. At the
4350 * moment 8-bit blits are handled in software and if certain GL
4351 * extensions are around, surface conversion is performed at
4352 * upload time. The blit_supported call recognizes it as a
4353 * destination fixup. This type of upload 'fixup' and 8-bit to
4354 * 8-bit blits need to be handled by the blit_shader.
4355 * TODO: get rid of this #if 0. */
4356 #if 0
4357 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4358 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4359 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4360 #endif
4361 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4363 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4364 * texturing. Further also use conversion in case of color keying.
4365 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4366 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4367 * conflicts with this.
4369 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4370 || colorkey_active || !use_texturing)
4372 format->glFormat = GL_RGBA;
4373 format->glInternal = GL_RGBA;
4374 format->glType = GL_UNSIGNED_BYTE;
4375 format->conv_byte_count = 4;
4376 if (colorkey_active)
4377 *convert = CONVERT_PALETTED_CK;
4378 else
4379 *convert = CONVERT_PALETTED;
4381 break;
4383 case WINED3DFMT_B2G3R3_UNORM:
4384 /* **********************
4385 GL_UNSIGNED_BYTE_3_3_2
4386 ********************** */
4387 if (colorkey_active) {
4388 /* This texture format will never be used.. So do not care about color keying
4389 up until the point in time it will be needed :-) */
4390 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4392 break;
4394 case WINED3DFMT_B5G6R5_UNORM:
4395 if (colorkey_active)
4397 *convert = CONVERT_CK_565;
4398 format->glFormat = GL_RGBA;
4399 format->glInternal = GL_RGB5_A1;
4400 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4401 format->conv_byte_count = 2;
4403 break;
4405 case WINED3DFMT_B5G5R5X1_UNORM:
4406 if (colorkey_active)
4408 *convert = CONVERT_CK_5551;
4409 format->glFormat = GL_BGRA;
4410 format->glInternal = GL_RGB5_A1;
4411 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4412 format->conv_byte_count = 2;
4414 break;
4416 case WINED3DFMT_B8G8R8_UNORM:
4417 if (colorkey_active)
4419 *convert = CONVERT_CK_RGB24;
4420 format->glFormat = GL_RGBA;
4421 format->glInternal = GL_RGBA8;
4422 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4423 format->conv_byte_count = 4;
4425 break;
4427 case WINED3DFMT_B8G8R8X8_UNORM:
4428 if (colorkey_active)
4430 *convert = CONVERT_RGB32_888;
4431 format->glFormat = GL_RGBA;
4432 format->glInternal = GL_RGBA8;
4433 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4434 format->conv_byte_count = 4;
4436 break;
4438 default:
4439 break;
4442 return WINED3D_OK;
4445 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4447 const struct wined3d_device *device = surface->resource.device;
4448 const struct wined3d_palette *pal = surface->palette;
4449 BOOL index_in_alpha = FALSE;
4450 unsigned int i;
4452 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4453 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4454 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4455 * duplicate entries. Store the color key in the unused alpha component to speed the
4456 * download up and to make conversion unneeded. */
4457 index_in_alpha = primary_render_target_is_p8(device);
4459 if (!pal)
4461 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
4462 if (device->wined3d->flags & WINED3D_PALETTE_PER_SURFACE)
4464 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4465 if (index_in_alpha)
4467 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4468 * there's no palette at this time. */
4469 for (i = 0; i < 256; i++) table[i][3] = i;
4472 else
4474 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
4475 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
4476 * capability flag is present (wine does advertise this capability) */
4477 for (i = 0; i < 256; ++i)
4479 table[i][0] = device->palettes[device->currentPalette][i].peRed;
4480 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
4481 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
4482 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
4486 else
4488 TRACE("Using surface palette %p\n", pal);
4489 /* Get the surface's palette */
4490 for (i = 0; i < 256; ++i)
4492 table[i][0] = pal->palents[i].peRed;
4493 table[i][1] = pal->palents[i].peGreen;
4494 table[i][2] = pal->palents[i].peBlue;
4496 /* When index_in_alpha is set the palette index is stored in the
4497 * alpha component. In case of a readback we can then read
4498 * GL_ALPHA. Color keying is handled in BltOverride using a
4499 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4500 * color key itself is passed to glAlphaFunc in other cases the
4501 * alpha component of pixels that should be masked away is set to 0. */
4502 if (index_in_alpha)
4504 table[i][3] = i;
4506 else if (colorkey && (i >= surface->SrcBltCKey.dwColorSpaceLowValue)
4507 && (i <= surface->SrcBltCKey.dwColorSpaceHighValue))
4509 table[i][3] = 0x00;
4511 else if (pal->flags & WINEDDPCAPS_ALPHA)
4513 table[i][3] = pal->palents[i].peFlags;
4515 else
4517 table[i][3] = 0xFF;
4523 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4524 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4526 const BYTE *source;
4527 BYTE *dest;
4528 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4530 switch (convert) {
4531 case NO_CONVERSION:
4533 memcpy(dst, src, pitch * height);
4534 break;
4536 case CONVERT_PALETTED:
4537 case CONVERT_PALETTED_CK:
4539 BYTE table[256][4];
4540 unsigned int x, y;
4542 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4544 for (y = 0; y < height; y++)
4546 source = src + pitch * y;
4547 dest = dst + outpitch * y;
4548 /* This is an 1 bpp format, using the width here is fine */
4549 for (x = 0; x < width; x++) {
4550 BYTE color = *source++;
4551 *dest++ = table[color][0];
4552 *dest++ = table[color][1];
4553 *dest++ = table[color][2];
4554 *dest++ = table[color][3];
4558 break;
4560 case CONVERT_CK_565:
4562 /* Converting the 565 format in 5551 packed to emulate color-keying.
4564 Note : in all these conversion, it would be best to average the averaging
4565 pixels to get the color of the pixel that will be color-keyed to
4566 prevent 'color bleeding'. This will be done later on if ever it is
4567 too visible.
4569 Note2: Nvidia documents say that their driver does not support alpha + color keying
4570 on the same surface and disables color keying in such a case
4572 unsigned int x, y;
4573 const WORD *Source;
4574 WORD *Dest;
4576 TRACE("Color keyed 565\n");
4578 for (y = 0; y < height; y++) {
4579 Source = (const WORD *)(src + y * pitch);
4580 Dest = (WORD *) (dst + y * outpitch);
4581 for (x = 0; x < width; x++ ) {
4582 WORD color = *Source++;
4583 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4584 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4585 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4586 *Dest |= 0x0001;
4587 Dest++;
4591 break;
4593 case CONVERT_CK_5551:
4595 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4596 unsigned int x, y;
4597 const WORD *Source;
4598 WORD *Dest;
4599 TRACE("Color keyed 5551\n");
4600 for (y = 0; y < height; y++) {
4601 Source = (const WORD *)(src + y * pitch);
4602 Dest = (WORD *) (dst + y * outpitch);
4603 for (x = 0; x < width; x++ ) {
4604 WORD color = *Source++;
4605 *Dest = color;
4606 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4607 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4608 *Dest |= (1 << 15);
4609 else
4610 *Dest &= ~(1 << 15);
4611 Dest++;
4615 break;
4617 case CONVERT_CK_RGB24:
4619 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4620 unsigned int x, y;
4621 for (y = 0; y < height; y++)
4623 source = src + pitch * y;
4624 dest = dst + outpitch * y;
4625 for (x = 0; x < width; x++) {
4626 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4627 DWORD dstcolor = color << 8;
4628 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4629 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4630 dstcolor |= 0xff;
4631 *(DWORD*)dest = dstcolor;
4632 source += 3;
4633 dest += 4;
4637 break;
4639 case CONVERT_RGB32_888:
4641 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4642 unsigned int x, y;
4643 for (y = 0; y < height; y++)
4645 source = src + pitch * y;
4646 dest = dst + outpitch * y;
4647 for (x = 0; x < width; x++) {
4648 DWORD color = 0xffffff & *(const DWORD*)source;
4649 DWORD dstcolor = color << 8;
4650 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4651 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4652 dstcolor |= 0xff;
4653 *(DWORD*)dest = dstcolor;
4654 source += 4;
4655 dest += 4;
4659 break;
4661 default:
4662 ERR("Unsupported conversion type %#x.\n", convert);
4664 return WINED3D_OK;
4667 BOOL palette9_changed(struct wined3d_surface *surface)
4669 struct wined3d_device *device = surface->resource.device;
4671 if (surface->palette || (surface->resource.format->id != WINED3DFMT_P8_UINT
4672 && surface->resource.format->id != WINED3DFMT_P8_UINT_A8_UNORM))
4674 /* If a ddraw-style palette is attached assume no d3d9 palette change.
4675 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
4677 return FALSE;
4680 if (surface->palette9)
4682 if (!memcmp(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
4684 return FALSE;
4687 else
4689 surface->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
4691 memcpy(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
4693 return TRUE;
4696 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4698 /* Flip the surface contents */
4699 /* Flip the DC */
4701 HDC tmp;
4702 tmp = front->hDC;
4703 front->hDC = back->hDC;
4704 back->hDC = tmp;
4707 /* Flip the DIBsection */
4709 HBITMAP tmp;
4710 BOOL hasDib = front->flags & SFLAG_DIBSECTION;
4711 tmp = front->dib.DIBsection;
4712 front->dib.DIBsection = back->dib.DIBsection;
4713 back->dib.DIBsection = tmp;
4715 if (back->flags & SFLAG_DIBSECTION) front->flags |= SFLAG_DIBSECTION;
4716 else front->flags &= ~SFLAG_DIBSECTION;
4717 if (hasDib) back->flags |= SFLAG_DIBSECTION;
4718 else back->flags &= ~SFLAG_DIBSECTION;
4721 /* Flip the surface data */
4723 void* tmp;
4725 tmp = front->dib.bitmap_data;
4726 front->dib.bitmap_data = back->dib.bitmap_data;
4727 back->dib.bitmap_data = tmp;
4729 tmp = front->resource.allocatedMemory;
4730 front->resource.allocatedMemory = back->resource.allocatedMemory;
4731 back->resource.allocatedMemory = tmp;
4733 tmp = front->resource.heapMemory;
4734 front->resource.heapMemory = back->resource.heapMemory;
4735 back->resource.heapMemory = tmp;
4738 /* Flip the PBO */
4740 GLuint tmp_pbo = front->pbo;
4741 front->pbo = back->pbo;
4742 back->pbo = tmp_pbo;
4745 /* client_memory should not be different, but just in case */
4747 BOOL tmp;
4748 tmp = front->dib.client_memory;
4749 front->dib.client_memory = back->dib.client_memory;
4750 back->dib.client_memory = tmp;
4753 /* Flip the opengl texture */
4755 GLuint tmp;
4757 tmp = back->texture_name;
4758 back->texture_name = front->texture_name;
4759 front->texture_name = tmp;
4761 tmp = back->texture_name_srgb;
4762 back->texture_name_srgb = front->texture_name_srgb;
4763 front->texture_name_srgb = tmp;
4765 resource_unload(&back->resource);
4766 resource_unload(&front->resource);
4770 DWORD tmp_flags = back->flags;
4771 back->flags = front->flags;
4772 front->flags = tmp_flags;
4776 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4777 * pixel copy calls. */
4778 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4779 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4781 struct wined3d_device *device = dst_surface->resource.device;
4782 float xrel, yrel;
4783 UINT row;
4784 struct wined3d_context *context;
4785 BOOL upsidedown = FALSE;
4786 RECT dst_rect = *dst_rect_in;
4788 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4789 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4791 if(dst_rect.top > dst_rect.bottom) {
4792 UINT tmp = dst_rect.bottom;
4793 dst_rect.bottom = dst_rect.top;
4794 dst_rect.top = tmp;
4795 upsidedown = TRUE;
4798 context = context_acquire(device, src_surface);
4799 context_apply_blit_state(context, device);
4800 surface_internal_preload(dst_surface, SRGB_RGB);
4801 ENTER_GL();
4803 /* Bind the target texture */
4804 glBindTexture(dst_surface->texture_target, dst_surface->texture_name);
4805 checkGLcall("glBindTexture");
4806 if (surface_is_offscreen(src_surface))
4808 TRACE("Reading from an offscreen target\n");
4809 upsidedown = !upsidedown;
4810 glReadBuffer(device->offscreenBuffer);
4812 else
4814 glReadBuffer(surface_get_gl_buffer(src_surface));
4816 checkGLcall("glReadBuffer");
4818 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4819 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4821 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4823 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4825 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4826 ERR("Texture filtering not supported in direct blit\n");
4829 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4830 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4832 ERR("Texture filtering not supported in direct blit\n");
4835 if (upsidedown
4836 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4837 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4839 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4841 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4842 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4843 src_rect->left, src_surface->resource.height - src_rect->bottom,
4844 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4846 else
4848 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4849 /* I have to process this row by row to swap the image,
4850 * otherwise it would be upside down, so stretching in y direction
4851 * doesn't cost extra time
4853 * However, stretching in x direction can be avoided if not necessary
4855 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4856 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4858 /* Well, that stuff works, but it's very slow.
4859 * find a better way instead
4861 UINT col;
4863 for (col = dst_rect.left; col < dst_rect.right; ++col)
4865 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4866 dst_rect.left + col /* x offset */, row /* y offset */,
4867 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4870 else
4872 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4873 dst_rect.left /* x offset */, row /* y offset */,
4874 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4878 checkGLcall("glCopyTexSubImage2D");
4880 LEAVE_GL();
4881 context_release(context);
4883 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4884 * path is never entered
4886 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4889 /* Uses the hardware to stretch and flip the image */
4890 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4891 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4893 struct wined3d_device *device = dst_surface->resource.device;
4894 struct wined3d_swapchain *src_swapchain = NULL;
4895 GLuint src, backup = 0;
4896 float left, right, top, bottom; /* Texture coordinates */
4897 UINT fbwidth = src_surface->resource.width;
4898 UINT fbheight = src_surface->resource.height;
4899 struct wined3d_context *context;
4900 GLenum drawBuffer = GL_BACK;
4901 GLenum texture_target;
4902 BOOL noBackBufferBackup;
4903 BOOL src_offscreen;
4904 BOOL upsidedown = FALSE;
4905 RECT dst_rect = *dst_rect_in;
4907 TRACE("Using hwstretch blit\n");
4908 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4909 context = context_acquire(device, src_surface);
4910 context_apply_blit_state(context, device);
4911 surface_internal_preload(dst_surface, SRGB_RGB);
4913 src_offscreen = surface_is_offscreen(src_surface);
4914 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4915 if (!noBackBufferBackup && !src_surface->texture_name)
4917 /* Get it a description */
4918 surface_internal_preload(src_surface, SRGB_RGB);
4920 ENTER_GL();
4922 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4923 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4925 if (context->aux_buffers >= 2)
4927 /* Got more than one aux buffer? Use the 2nd aux buffer */
4928 drawBuffer = GL_AUX1;
4930 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4932 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4933 drawBuffer = GL_AUX0;
4936 if(noBackBufferBackup) {
4937 glGenTextures(1, &backup);
4938 checkGLcall("glGenTextures");
4939 glBindTexture(GL_TEXTURE_2D, backup);
4940 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
4941 texture_target = GL_TEXTURE_2D;
4942 } else {
4943 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4944 * we are reading from the back buffer, the backup can be used as source texture
4946 texture_target = src_surface->texture_target;
4947 glBindTexture(texture_target, src_surface->texture_name);
4948 checkGLcall("glBindTexture(texture_target, src_surface->texture_name)");
4949 glEnable(texture_target);
4950 checkGLcall("glEnable(texture_target)");
4952 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4953 src_surface->flags &= ~SFLAG_INTEXTURE;
4956 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4957 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4959 if(dst_rect.top > dst_rect.bottom) {
4960 UINT tmp = dst_rect.bottom;
4961 dst_rect.bottom = dst_rect.top;
4962 dst_rect.top = tmp;
4963 upsidedown = TRUE;
4966 if (src_offscreen)
4968 TRACE("Reading from an offscreen target\n");
4969 upsidedown = !upsidedown;
4970 glReadBuffer(device->offscreenBuffer);
4972 else
4974 glReadBuffer(surface_get_gl_buffer(src_surface));
4977 /* TODO: Only back up the part that will be overwritten */
4978 glCopyTexSubImage2D(texture_target, 0,
4979 0, 0 /* read offsets */,
4980 0, 0,
4981 fbwidth,
4982 fbheight);
4984 checkGLcall("glCopyTexSubImage2D");
4986 /* No issue with overriding these - the sampler is dirty due to blit usage */
4987 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
4988 wined3d_gl_mag_filter(magLookup, Filter));
4989 checkGLcall("glTexParameteri");
4990 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
4991 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
4992 checkGLcall("glTexParameteri");
4994 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
4995 src_swapchain = src_surface->container.u.swapchain;
4996 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
4998 src = backup ? backup : src_surface->texture_name;
5000 else
5002 glReadBuffer(GL_FRONT);
5003 checkGLcall("glReadBuffer(GL_FRONT)");
5005 glGenTextures(1, &src);
5006 checkGLcall("glGenTextures(1, &src)");
5007 glBindTexture(GL_TEXTURE_2D, src);
5008 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
5010 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
5011 * out for power of 2 sizes
5013 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5014 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5015 checkGLcall("glTexImage2D");
5016 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5017 0, 0 /* read offsets */,
5018 0, 0,
5019 fbwidth,
5020 fbheight);
5022 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5023 checkGLcall("glTexParameteri");
5024 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5025 checkGLcall("glTexParameteri");
5027 glReadBuffer(GL_BACK);
5028 checkGLcall("glReadBuffer(GL_BACK)");
5030 if(texture_target != GL_TEXTURE_2D) {
5031 glDisable(texture_target);
5032 glEnable(GL_TEXTURE_2D);
5033 texture_target = GL_TEXTURE_2D;
5036 checkGLcall("glEnd and previous");
5038 left = src_rect->left;
5039 right = src_rect->right;
5041 if (!upsidedown)
5043 top = src_surface->resource.height - src_rect->top;
5044 bottom = src_surface->resource.height - src_rect->bottom;
5046 else
5048 top = src_surface->resource.height - src_rect->bottom;
5049 bottom = src_surface->resource.height - src_rect->top;
5052 if (src_surface->flags & SFLAG_NORMCOORD)
5054 left /= src_surface->pow2Width;
5055 right /= src_surface->pow2Width;
5056 top /= src_surface->pow2Height;
5057 bottom /= src_surface->pow2Height;
5060 /* draw the source texture stretched and upside down. The correct surface is bound already */
5061 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5062 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5064 context_set_draw_buffer(context, drawBuffer);
5065 glReadBuffer(drawBuffer);
5067 glBegin(GL_QUADS);
5068 /* bottom left */
5069 glTexCoord2f(left, bottom);
5070 glVertex2i(0, 0);
5072 /* top left */
5073 glTexCoord2f(left, top);
5074 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5076 /* top right */
5077 glTexCoord2f(right, top);
5078 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5080 /* bottom right */
5081 glTexCoord2f(right, bottom);
5082 glVertex2i(dst_rect.right - dst_rect.left, 0);
5083 glEnd();
5084 checkGLcall("glEnd and previous");
5086 if (texture_target != dst_surface->texture_target)
5088 glDisable(texture_target);
5089 glEnable(dst_surface->texture_target);
5090 texture_target = dst_surface->texture_target;
5093 /* Now read the stretched and upside down image into the destination texture */
5094 glBindTexture(texture_target, dst_surface->texture_name);
5095 checkGLcall("glBindTexture");
5096 glCopyTexSubImage2D(texture_target,
5098 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5099 0, 0, /* We blitted the image to the origin */
5100 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5101 checkGLcall("glCopyTexSubImage2D");
5103 if(drawBuffer == GL_BACK) {
5104 /* Write the back buffer backup back */
5105 if(backup) {
5106 if(texture_target != GL_TEXTURE_2D) {
5107 glDisable(texture_target);
5108 glEnable(GL_TEXTURE_2D);
5109 texture_target = GL_TEXTURE_2D;
5111 glBindTexture(GL_TEXTURE_2D, backup);
5112 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
5114 else
5116 if (texture_target != src_surface->texture_target)
5118 glDisable(texture_target);
5119 glEnable(src_surface->texture_target);
5120 texture_target = src_surface->texture_target;
5122 glBindTexture(src_surface->texture_target, src_surface->texture_name);
5123 checkGLcall("glBindTexture(src_surface->texture_target, src_surface->texture_name)");
5126 glBegin(GL_QUADS);
5127 /* top left */
5128 glTexCoord2f(0.0f, 0.0f);
5129 glVertex2i(0, fbheight);
5131 /* bottom left */
5132 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5133 glVertex2i(0, 0);
5135 /* bottom right */
5136 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5137 (float)fbheight / (float)src_surface->pow2Height);
5138 glVertex2i(fbwidth, 0);
5140 /* top right */
5141 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5142 glVertex2i(fbwidth, fbheight);
5143 glEnd();
5145 glDisable(texture_target);
5146 checkGLcall("glDisable(texture_target)");
5148 /* Cleanup */
5149 if (src != src_surface->texture_name && src != backup)
5151 glDeleteTextures(1, &src);
5152 checkGLcall("glDeleteTextures(1, &src)");
5154 if(backup) {
5155 glDeleteTextures(1, &backup);
5156 checkGLcall("glDeleteTextures(1, &backup)");
5159 LEAVE_GL();
5161 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5163 context_release(context);
5165 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5166 * path is never entered
5168 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5171 /* Front buffer coordinates are always full screen coordinates, but our GL
5172 * drawable is limited to the window's client area. The sysmem and texture
5173 * copies do have the full screen size. Note that GL has a bottom-left
5174 * origin, while D3D has a top-left origin. */
5175 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5177 UINT drawable_height;
5179 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5180 && surface == surface->container.u.swapchain->front_buffer)
5182 POINT offset = {0, 0};
5183 RECT windowsize;
5185 ScreenToClient(window, &offset);
5186 OffsetRect(rect, offset.x, offset.y);
5188 GetClientRect(window, &windowsize);
5189 drawable_height = windowsize.bottom - windowsize.top;
5191 else
5193 drawable_height = surface->resource.height;
5196 rect->top = drawable_height - rect->top;
5197 rect->bottom = drawable_height - rect->bottom;
5200 static void surface_blt_to_drawable(struct wined3d_device *device,
5201 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5202 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5203 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5205 struct wined3d_context *context;
5206 RECT src_rect, dst_rect;
5208 src_rect = *src_rect_in;
5209 dst_rect = *dst_rect_in;
5211 /* Make sure the surface is up-to-date. This should probably use
5212 * surface_load_location() and worry about the destination surface too,
5213 * unless we're overwriting it completely. */
5214 surface_internal_preload(src_surface, SRGB_RGB);
5216 /* Activate the destination context, set it up for blitting */
5217 context = context_acquire(device, dst_surface);
5218 context_apply_blit_state(context, device);
5220 if (!surface_is_offscreen(dst_surface))
5221 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5223 device->blitter->set_shader(device->blit_priv, context->gl_info, src_surface);
5225 ENTER_GL();
5227 if (color_key)
5229 glEnable(GL_ALPHA_TEST);
5230 checkGLcall("glEnable(GL_ALPHA_TEST)");
5232 /* When the primary render target uses P8, the alpha component
5233 * contains the palette index. Which means that the colorkey is one of
5234 * the palette entries. In other cases pixels that should be masked
5235 * away have alpha set to 0. */
5236 if (primary_render_target_is_p8(device))
5237 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
5238 else
5239 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5240 checkGLcall("glAlphaFunc");
5242 else
5244 glDisable(GL_ALPHA_TEST);
5245 checkGLcall("glDisable(GL_ALPHA_TEST)");
5248 draw_textured_quad(src_surface, &src_rect, &dst_rect, filter);
5250 if (color_key)
5252 glDisable(GL_ALPHA_TEST);
5253 checkGLcall("glDisable(GL_ALPHA_TEST)");
5256 LEAVE_GL();
5258 /* Leave the opengl state valid for blitting */
5259 device->blitter->unset_shader(context->gl_info);
5261 if (wined3d_settings.strict_draw_ordering
5262 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5263 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5264 wglFlush(); /* Flush to ensure ordering across contexts. */
5266 context_release(context);
5269 /* Do not call while under the GL lock. */
5270 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const WINED3DCOLORVALUE *color)
5272 struct wined3d_device *device = s->resource.device;
5273 const struct blit_shader *blitter;
5275 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5276 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5277 if (!blitter)
5279 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5280 return WINED3DERR_INVALIDCALL;
5283 return blitter->color_fill(device, s, rect, color);
5286 /* Do not call while under the GL lock. */
5287 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5288 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5289 WINED3DTEXTUREFILTERTYPE Filter)
5291 struct wined3d_device *device = dst_surface->resource.device;
5292 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5293 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5295 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5296 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5297 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5299 /* Get the swapchain. One of the surfaces has to be a primary surface */
5300 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5302 WARN("Destination is in sysmem, rejecting gl blt\n");
5303 return WINED3DERR_INVALIDCALL;
5306 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5307 dstSwapchain = dst_surface->container.u.swapchain;
5309 if (src_surface)
5311 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5313 WARN("Src is in sysmem, rejecting gl blt\n");
5314 return WINED3DERR_INVALIDCALL;
5317 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5318 srcSwapchain = src_surface->container.u.swapchain;
5321 /* Early sort out of cases where no render target is used */
5322 if (!dstSwapchain && !srcSwapchain
5323 && src_surface != device->fb.render_targets[0]
5324 && dst_surface != device->fb.render_targets[0])
5326 TRACE("No surface is render target, not using hardware blit.\n");
5327 return WINED3DERR_INVALIDCALL;
5330 /* No destination color keying supported */
5331 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5333 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5334 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5335 return WINED3DERR_INVALIDCALL;
5338 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
5339 if (dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->back_buffers
5340 && dst_surface == dstSwapchain->front_buffer
5341 && src_surface == dstSwapchain->back_buffers[0])
5343 /* Half-Life does a Blt from the back buffer to the front buffer,
5344 * Full surface size, no flags... Use present instead
5346 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
5349 /* Check rects - wined3d_swapchain_present() doesn't handle them. */
5350 for (;;)
5352 TRACE("Looking if a Present can be done...\n");
5353 /* Source Rectangle must be full surface */
5354 if (src_rect->left || src_rect->top
5355 || src_rect->right != src_surface->resource.width
5356 || src_rect->bottom != src_surface->resource.height)
5358 TRACE("No, Source rectangle doesn't match\n");
5359 break;
5362 /* No stretching may occur */
5363 if (src_rect->right != dst_rect->right - dst_rect->left
5364 || src_rect->bottom != dst_rect->bottom - dst_rect->top)
5366 TRACE("No, stretching is done\n");
5367 break;
5370 /* Destination must be full surface or match the clipping rectangle */
5371 if (dst_surface->clipper && dst_surface->clipper->hWnd)
5373 RECT cliprect;
5374 POINT pos[2];
5375 GetClientRect(dst_surface->clipper->hWnd, &cliprect);
5376 pos[0].x = dst_rect->left;
5377 pos[0].y = dst_rect->top;
5378 pos[1].x = dst_rect->right;
5379 pos[1].y = dst_rect->bottom;
5380 MapWindowPoints(GetDesktopWindow(), dst_surface->clipper->hWnd, pos, 2);
5382 if (pos[0].x != cliprect.left || pos[0].y != cliprect.top
5383 || pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
5385 TRACE("No, dest rectangle doesn't match(clipper)\n");
5386 TRACE("Clip rect at %s\n", wine_dbgstr_rect(&cliprect));
5387 TRACE("Blt dest: %s\n", wine_dbgstr_rect(dst_rect));
5388 break;
5391 else if (dst_rect->left || dst_rect->top
5392 || dst_rect->right != dst_surface->resource.width
5393 || dst_rect->bottom != dst_surface->resource.height)
5395 TRACE("No, dest rectangle doesn't match(surface size)\n");
5396 break;
5399 TRACE("Yes\n");
5401 /* These flags are unimportant for the flag check, remove them */
5402 if (!(flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)))
5404 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
5406 /* The idea behind this is that a glReadPixels and a glDrawPixels call
5407 * take very long, while a flip is fast.
5408 * This applies to Half-Life, which does such Blts every time it finished
5409 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
5410 * menu. This is also used by all apps when they do windowed rendering
5412 * The problem is that flipping is not really the same as copying. After a
5413 * Blt the front buffer is a copy of the back buffer, and the back buffer is
5414 * untouched. Therefore it's necessary to override the swap effect
5415 * and to set it back after the flip.
5417 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
5418 * testcases.
5421 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
5422 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
5424 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead.\n");
5425 wined3d_swapchain_present(dstSwapchain, NULL, NULL, dstSwapchain->win_handle, NULL, 0);
5427 dstSwapchain->presentParms.SwapEffect = orig_swap;
5429 return WINED3D_OK;
5431 break;
5434 TRACE("Unsupported blit between buffers on the same swapchain\n");
5435 return WINED3DERR_INVALIDCALL;
5436 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
5437 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5438 return WINED3DERR_INVALIDCALL;
5439 } else if(dstSwapchain && srcSwapchain) {
5440 FIXME("Implement hardware blit between two different swapchains\n");
5441 return WINED3DERR_INVALIDCALL;
5443 else if (dstSwapchain)
5445 /* Handled with regular texture -> swapchain blit */
5446 if (src_surface == device->fb.render_targets[0])
5447 TRACE("Blit from active render target to a swapchain\n");
5449 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5451 FIXME("Implement blit from a swapchain to the active render target\n");
5452 return WINED3DERR_INVALIDCALL;
5455 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5457 /* Blit from render target to texture */
5458 BOOL stretchx;
5460 /* P8 read back is not implemented */
5461 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5462 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5464 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5465 return WINED3DERR_INVALIDCALL;
5468 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5470 TRACE("Color keying not supported by frame buffer to texture blit\n");
5471 return WINED3DERR_INVALIDCALL;
5472 /* Destination color key is checked above */
5475 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5476 stretchx = TRUE;
5477 else
5478 stretchx = FALSE;
5480 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5481 * flip the image nor scale it.
5483 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5484 * -> If the app wants a image width an unscaled width, copy it line per line
5485 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5486 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5487 * back buffer. This is slower than reading line per line, thus not used for flipping
5488 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5489 * pixel by pixel. */
5490 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5491 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5493 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5494 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, Filter);
5495 } else {
5496 TRACE("Using hardware stretching to flip / stretch the texture\n");
5497 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5500 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5502 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5503 dst_surface->resource.allocatedMemory = NULL;
5504 dst_surface->resource.heapMemory = NULL;
5506 else
5508 dst_surface->flags &= ~SFLAG_INSYSMEM;
5511 return WINED3D_OK;
5513 else if (src_surface)
5515 /* Blit from offscreen surface to render target */
5516 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5517 WINEDDCOLORKEY oldBltCKey = src_surface->SrcBltCKey;
5519 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5521 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5522 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5523 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5525 FIXME("Unsupported blit operation falling back to software\n");
5526 return WINED3DERR_INVALIDCALL;
5529 /* Color keying: Check if we have to do a color keyed blt,
5530 * and if not check if a color key is activated.
5532 * Just modify the color keying parameters in the surface and restore them afterwards
5533 * The surface keeps track of the color key last used to load the opengl surface.
5534 * PreLoad will catch the change to the flags and color key and reload if necessary.
5536 if (flags & WINEDDBLT_KEYSRC)
5538 /* Use color key from surface */
5540 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5542 /* Use color key from DDBltFx */
5543 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5544 src_surface->SrcBltCKey = DDBltFx->ddckSrcColorkey;
5546 else
5548 /* Do not use color key */
5549 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5552 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5553 src_surface, src_rect, dst_surface, dst_rect);
5555 /* Restore the color key parameters */
5556 src_surface->CKeyFlags = oldCKeyFlags;
5557 src_surface->SrcBltCKey = oldBltCKey;
5559 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
5561 return WINED3D_OK;
5564 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5565 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5566 return WINED3DERR_INVALIDCALL;
5569 /* GL locking is done by the caller */
5570 static void surface_depth_blt(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
5571 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5573 struct wined3d_device *device = surface->resource.device;
5574 GLint compare_mode = GL_NONE;
5575 struct blt_info info;
5576 GLint old_binding = 0;
5577 RECT rect;
5579 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5581 glDisable(GL_CULL_FACE);
5582 glDisable(GL_BLEND);
5583 glDisable(GL_ALPHA_TEST);
5584 glDisable(GL_SCISSOR_TEST);
5585 glDisable(GL_STENCIL_TEST);
5586 glEnable(GL_DEPTH_TEST);
5587 glDepthFunc(GL_ALWAYS);
5588 glDepthMask(GL_TRUE);
5589 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5590 glViewport(x, y, w, h);
5592 SetRect(&rect, 0, h, w, 0);
5593 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5594 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
5595 glGetIntegerv(info.binding, &old_binding);
5596 glBindTexture(info.bind_target, texture);
5597 if (gl_info->supported[ARB_SHADOW])
5599 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5600 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5603 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5604 gl_info, info.tex_type, &surface->ds_current_size);
5606 glBegin(GL_TRIANGLE_STRIP);
5607 glTexCoord3fv(info.coords[0]);
5608 glVertex2f(-1.0f, -1.0f);
5609 glTexCoord3fv(info.coords[1]);
5610 glVertex2f(1.0f, -1.0f);
5611 glTexCoord3fv(info.coords[2]);
5612 glVertex2f(-1.0f, 1.0f);
5613 glTexCoord3fv(info.coords[3]);
5614 glVertex2f(1.0f, 1.0f);
5615 glEnd();
5617 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5618 glBindTexture(info.bind_target, old_binding);
5620 glPopAttrib();
5622 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5625 void surface_modify_ds_location(struct wined3d_surface *surface,
5626 DWORD location, UINT w, UINT h)
5628 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5630 if (location & ~SFLAG_DS_LOCATIONS)
5631 FIXME("Invalid location (%#x) specified.\n", location);
5633 surface->ds_current_size.cx = w;
5634 surface->ds_current_size.cy = h;
5635 surface->flags &= ~SFLAG_DS_LOCATIONS;
5636 surface->flags |= location;
5639 /* Context activation is done by the caller. */
5640 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5642 struct wined3d_device *device = surface->resource.device;
5643 const struct wined3d_gl_info *gl_info = context->gl_info;
5644 GLsizei w, h;
5646 TRACE("surface %p, new location %#x.\n", surface, location);
5648 /* TODO: Make this work for modes other than FBO */
5649 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5651 if (!(surface->flags & location))
5653 w = surface->ds_current_size.cx;
5654 h = surface->ds_current_size.cy;
5655 surface->ds_current_size.cx = 0;
5656 surface->ds_current_size.cy = 0;
5658 else
5660 w = surface->resource.width;
5661 h = surface->resource.height;
5664 if (surface->ds_current_size.cx == surface->resource.width
5665 && surface->ds_current_size.cy == surface->resource.height)
5667 TRACE("Location (%#x) is already up to date.\n", location);
5668 return;
5671 if (surface->current_renderbuffer)
5673 FIXME("Not supported with fixed up depth stencil.\n");
5674 return;
5677 if (!(surface->flags & SFLAG_DS_LOCATIONS))
5679 /* This mostly happens when a depth / stencil is used without being
5680 * cleared first. In principle we could upload from sysmem, or
5681 * explicitly clear before first usage. For the moment there don't
5682 * appear to be a lot of applications depending on this, so a FIXME
5683 * should do. */
5684 FIXME("No up to date depth stencil location.\n");
5685 surface->flags |= location;
5686 surface->ds_current_size.cx = surface->resource.width;
5687 surface->ds_current_size.cy = surface->resource.height;
5688 return;
5691 if (location == SFLAG_DS_OFFSCREEN)
5693 GLint old_binding = 0;
5694 GLenum bind_target;
5696 /* The render target is allowed to be smaller than the depth/stencil
5697 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5698 * than the offscreen surface. Don't overwrite the offscreen surface
5699 * with undefined data. */
5700 w = min(w, context->swapchain->presentParms.BackBufferWidth);
5701 h = min(h, context->swapchain->presentParms.BackBufferHeight);
5703 TRACE("Copying onscreen depth buffer to depth texture.\n");
5705 ENTER_GL();
5707 if (!device->depth_blt_texture)
5709 glGenTextures(1, &device->depth_blt_texture);
5712 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5713 * directly on the FBO texture. That's because we need to flip. */
5714 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5715 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5716 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5718 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5719 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5721 else
5723 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5724 bind_target = GL_TEXTURE_2D;
5726 glBindTexture(bind_target, device->depth_blt_texture);
5727 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5728 * internal format, because the internal format might include stencil
5729 * data. In principle we should copy stencil data as well, but unless
5730 * the driver supports stencil export it's hard to do, and doesn't
5731 * seem to be needed in practice. If the hardware doesn't support
5732 * writing stencil data, the glCopyTexImage2D() call might trigger
5733 * software fallbacks. */
5734 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5735 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5736 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5737 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5738 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5739 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5740 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5741 glBindTexture(bind_target, old_binding);
5743 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5744 NULL, surface, SFLAG_INTEXTURE);
5745 context_set_draw_buffer(context, GL_NONE);
5746 glReadBuffer(GL_NONE);
5748 /* Do the actual blit */
5749 surface_depth_blt(surface, gl_info, device->depth_blt_texture, 0, 0, w, h, bind_target);
5750 checkGLcall("depth_blt");
5752 context_invalidate_state(context, STATE_FRAMEBUFFER);
5754 LEAVE_GL();
5756 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5758 else if (location == SFLAG_DS_ONSCREEN)
5760 TRACE("Copying depth texture to onscreen depth buffer.\n");
5762 ENTER_GL();
5764 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5765 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5766 surface_depth_blt(surface, gl_info, surface->texture_name,
5767 0, surface->pow2Height - h, w, h, surface->texture_target);
5768 checkGLcall("depth_blt");
5770 context_invalidate_state(context, STATE_FRAMEBUFFER);
5772 LEAVE_GL();
5774 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5776 else
5778 ERR("Invalid location (%#x) specified.\n", location);
5781 surface->flags |= location;
5782 surface->ds_current_size.cx = surface->resource.width;
5783 surface->ds_current_size.cy = surface->resource.height;
5786 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5788 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5789 struct wined3d_surface *overlay;
5791 TRACE("surface %p, location %s, persistent %#x.\n",
5792 surface, debug_surflocation(location), persistent);
5794 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5796 if (surface_is_offscreen(surface))
5798 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same
5799 * for offscreen targets. */
5800 if (location & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))
5801 location |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
5803 else
5805 TRACE("Surface %p is an onscreen surface.\n", surface);
5809 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5810 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5811 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5813 if (persistent)
5815 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5816 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5818 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5820 TRACE("Passing to container.\n");
5821 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5824 surface->flags &= ~SFLAG_LOCATIONS;
5825 surface->flags |= location;
5827 /* Redraw emulated overlays, if any */
5828 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5830 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5832 overlay->surface_ops->surface_draw_overlay(overlay);
5836 else
5838 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5840 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5842 TRACE("Passing to container\n");
5843 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5846 surface->flags &= ~location;
5849 if (!(surface->flags & SFLAG_LOCATIONS))
5851 ERR("Surface %p does not have any up to date location.\n", surface);
5855 static DWORD resource_access_from_location(DWORD location)
5857 switch (location)
5859 case SFLAG_INSYSMEM:
5860 return WINED3D_RESOURCE_ACCESS_CPU;
5862 case SFLAG_INDRAWABLE:
5863 case SFLAG_INSRGBTEX:
5864 case SFLAG_INTEXTURE:
5865 return WINED3D_RESOURCE_ACCESS_GPU;
5867 default:
5868 FIXME("Unhandled location %#x.\n", location);
5869 return 0;
5873 static void surface_load_sysmem(struct wined3d_surface *surface,
5874 const struct wined3d_gl_info *gl_info, const RECT *rect)
5876 surface_prepare_system_memory(surface);
5878 /* Download the surface to system memory. */
5879 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5881 struct wined3d_device *device = surface->resource.device;
5882 struct wined3d_context *context = NULL;
5884 if (!device->isInDraw)
5885 context = context_acquire(device, NULL);
5887 surface_bind_and_dirtify(surface, gl_info, !(surface->flags & SFLAG_INTEXTURE));
5888 surface_download_data(surface, gl_info);
5890 if (context)
5891 context_release(context);
5893 return;
5896 /* Note: It might be faster to download into a texture first. */
5897 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5898 wined3d_surface_get_pitch(surface));
5901 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5902 const struct wined3d_gl_info *gl_info, const RECT *rect)
5904 struct wined3d_device *device = surface->resource.device;
5905 struct wined3d_format format;
5906 CONVERT_TYPES convert;
5907 UINT byte_count;
5908 BYTE *mem;
5910 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5911 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5913 if (surface->flags & SFLAG_INTEXTURE)
5915 RECT r;
5917 surface_get_rect(surface, rect, &r);
5918 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5920 return WINED3D_OK;
5923 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5925 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5926 * path through sysmem. */
5927 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5930 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5932 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5933 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5934 * called. */
5935 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5937 struct wined3d_context *context = NULL;
5939 TRACE("Removing the pbo attached to surface %p.\n", surface);
5941 if (!device->isInDraw)
5942 context = context_acquire(device, NULL);
5944 surface_remove_pbo(surface, gl_info);
5946 if (context)
5947 context_release(context);
5950 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5952 UINT height = surface->resource.height;
5953 UINT width = surface->resource.width;
5954 UINT src_pitch, dst_pitch;
5956 byte_count = format.conv_byte_count;
5957 src_pitch = wined3d_surface_get_pitch(surface);
5959 /* Stick to the alignment for the converted surface too, makes it
5960 * easier to load the surface. */
5961 dst_pitch = width * byte_count;
5962 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5964 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5966 ERR("Out of memory (%u).\n", dst_pitch * height);
5967 return E_OUTOFMEMORY;
5970 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5971 src_pitch, width, height, dst_pitch, convert, surface);
5973 surface->flags |= SFLAG_CONVERTED;
5975 else
5977 surface->flags &= ~SFLAG_CONVERTED;
5978 mem = surface->resource.allocatedMemory;
5979 byte_count = format.byte_count;
5982 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5984 /* Don't delete PBO memory. */
5985 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5986 HeapFree(GetProcessHeap(), 0, mem);
5988 return WINED3D_OK;
5991 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5992 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5994 const DWORD attach_flags = WINED3DFMT_FLAG_FBO_ATTACHABLE | WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB;
5995 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5996 struct wined3d_device *device = surface->resource.device;
5997 struct wined3d_context *context = NULL;
5998 UINT width, src_pitch, dst_pitch;
5999 struct wined3d_bo_address data;
6000 struct wined3d_format format;
6001 POINT dst_point = {0, 0};
6002 CONVERT_TYPES convert;
6003 BYTE *mem;
6005 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
6006 && surface_is_offscreen(surface)
6007 && (surface->flags & SFLAG_INDRAWABLE))
6009 read_from_framebuffer_texture(surface, srgb);
6011 return WINED3D_OK;
6014 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6015 && (surface->resource.format->flags & attach_flags) == attach_flags
6016 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6017 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6018 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6020 if (srgb)
6021 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
6022 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6023 else
6024 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
6025 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6027 return WINED3D_OK;
6030 /* Upload from system memory */
6032 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6033 TRUE /* We will use textures */, &format, &convert);
6035 if (srgb)
6037 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6039 /* Performance warning... */
6040 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6041 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6044 else
6046 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6048 /* Performance warning... */
6049 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6050 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6054 if (!(surface->flags & SFLAG_INSYSMEM))
6056 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6057 /* Lets hope we get it from somewhere... */
6058 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6061 if (!device->isInDraw)
6062 context = context_acquire(device, NULL);
6064 surface_prepare_texture(surface, gl_info, srgb);
6065 surface_bind_and_dirtify(surface, gl_info, srgb);
6067 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6069 surface->flags |= SFLAG_GLCKEY;
6070 surface->glCKey = surface->SrcBltCKey;
6072 else surface->flags &= ~SFLAG_GLCKEY;
6074 width = surface->resource.width;
6075 src_pitch = wined3d_surface_get_pitch(surface);
6077 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6078 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6079 * called. */
6080 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6082 TRACE("Removing the pbo attached to surface %p.\n", surface);
6083 surface_remove_pbo(surface, gl_info);
6086 if (format.convert)
6088 /* This code is entered for texture formats which need a fixup. */
6089 UINT height = surface->resource.height;
6091 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6092 dst_pitch = width * format.conv_byte_count;
6093 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6095 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6097 ERR("Out of memory (%u).\n", dst_pitch * height);
6098 if (context)
6099 context_release(context);
6100 return E_OUTOFMEMORY;
6102 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6104 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6106 /* This code is only entered for color keying fixups */
6107 UINT height = surface->resource.height;
6109 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6110 dst_pitch = width * format.conv_byte_count;
6111 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6113 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6115 ERR("Out of memory (%u).\n", dst_pitch * height);
6116 if (context)
6117 context_release(context);
6118 return E_OUTOFMEMORY;
6120 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6121 width, height, dst_pitch, convert, surface);
6123 else
6125 mem = surface->resource.allocatedMemory;
6128 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6129 data.addr = mem;
6130 surface_upload_data(surface, gl_info, &format, &src_rect, width, &dst_point, srgb, &data);
6132 if (context)
6133 context_release(context);
6135 /* Don't delete PBO memory. */
6136 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6137 HeapFree(GetProcessHeap(), 0, mem);
6139 return WINED3D_OK;
6142 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6144 struct wined3d_device *device = surface->resource.device;
6145 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6146 BOOL in_fbo = FALSE;
6147 HRESULT hr;
6149 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6151 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6153 if (location == SFLAG_INTEXTURE)
6155 struct wined3d_context *context = context_acquire(device, NULL);
6156 surface_load_ds_location(surface, context, SFLAG_DS_OFFSCREEN);
6157 context_release(context);
6158 return WINED3D_OK;
6160 else
6162 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6163 return WINED3DERR_INVALIDCALL;
6167 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6169 if (surface_is_offscreen(surface))
6171 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same
6172 * for offscreen targets. Prefer SFLAG_INTEXTURE. */
6173 if (location == SFLAG_INDRAWABLE)
6174 location = SFLAG_INTEXTURE;
6175 in_fbo = TRUE;
6177 else
6179 TRACE("Surface %p is an onscreen surface.\n", surface);
6183 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6184 location = SFLAG_INTEXTURE;
6186 if (surface->flags & location)
6188 TRACE("Location already up to date.\n");
6189 return WINED3D_OK;
6192 if (WARN_ON(d3d_surface))
6194 DWORD required_access = resource_access_from_location(location);
6195 if ((surface->resource.access_flags & required_access) != required_access)
6196 WARN("Operation requires %#x access, but surface only has %#x.\n",
6197 required_access, surface->resource.access_flags);
6200 if (!(surface->flags & SFLAG_LOCATIONS))
6202 ERR("Surface %p does not have any up to date location.\n", surface);
6203 surface->flags |= SFLAG_LOST;
6204 return WINED3DERR_DEVICELOST;
6207 switch (location)
6209 case SFLAG_INSYSMEM:
6210 surface_load_sysmem(surface, gl_info, rect);
6211 break;
6213 case SFLAG_INDRAWABLE:
6214 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6215 return hr;
6216 break;
6218 case SFLAG_INTEXTURE:
6219 case SFLAG_INSRGBTEX:
6220 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6221 return hr;
6222 break;
6224 default:
6225 ERR("Don't know how to handle location %#x.\n", location);
6226 break;
6229 if (!rect)
6231 surface->flags |= location;
6233 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6234 surface_evict_sysmem(surface);
6237 if (in_fbo && (surface->flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)))
6239 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
6240 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
6243 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6244 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6246 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6249 return WINED3D_OK;
6252 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6254 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6256 /* Not on a swapchain - must be offscreen */
6257 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6259 /* The front buffer is always onscreen */
6260 if (surface == swapchain->front_buffer) return FALSE;
6262 /* If the swapchain is rendered to an FBO, the backbuffer is
6263 * offscreen, otherwise onscreen */
6264 return swapchain->render_to_fbo;
6267 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6268 /* Context activation is done by the caller. */
6269 static void ffp_blit_free(struct wined3d_device *device) { }
6271 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6272 /* Context activation is done by the caller. */
6273 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6275 BYTE table[256][4];
6276 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6278 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6280 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6281 ENTER_GL();
6282 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6283 LEAVE_GL();
6286 /* Context activation is done by the caller. */
6287 static HRESULT ffp_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, struct wined3d_surface *surface)
6289 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6291 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6292 * else the surface is converted in software at upload time in LoadLocation.
6294 if(fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6295 ffp_blit_p8_upload_palette(surface, gl_info);
6297 ENTER_GL();
6298 glEnable(surface->texture_target);
6299 checkGLcall("glEnable(surface->texture_target)");
6300 LEAVE_GL();
6301 return WINED3D_OK;
6304 /* Context activation is done by the caller. */
6305 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6307 ENTER_GL();
6308 glDisable(GL_TEXTURE_2D);
6309 checkGLcall("glDisable(GL_TEXTURE_2D)");
6310 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6312 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6313 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6315 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6317 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6318 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6320 LEAVE_GL();
6323 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6324 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6325 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6327 enum complex_fixup src_fixup;
6329 switch (blit_op)
6331 case WINED3D_BLIT_OP_COLOR_BLIT:
6332 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
6333 return FALSE;
6335 src_fixup = get_complex_fixup(src_format->color_fixup);
6336 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6338 TRACE("Checking support for fixup:\n");
6339 dump_color_fixup_desc(src_format->color_fixup);
6342 if (!is_identity_fixup(dst_format->color_fixup))
6344 TRACE("Destination fixups are not supported\n");
6345 return FALSE;
6348 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6350 TRACE("P8 fixup supported\n");
6351 return TRUE;
6354 /* We only support identity conversions. */
6355 if (is_identity_fixup(src_format->color_fixup))
6357 TRACE("[OK]\n");
6358 return TRUE;
6361 TRACE("[FAILED]\n");
6362 return FALSE;
6364 case WINED3D_BLIT_OP_COLOR_FILL:
6365 if (dst_pool == WINED3DPOOL_SYSTEMMEM)
6366 return FALSE;
6368 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6370 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6371 return FALSE;
6373 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6375 TRACE("Color fill not supported\n");
6376 return FALSE;
6379 /* FIXME: We should reject color fills on formats with fixups,
6380 * but this would break P8 color fills for example. */
6382 return TRUE;
6384 case WINED3D_BLIT_OP_DEPTH_FILL:
6385 return TRUE;
6387 default:
6388 TRACE("Unsupported blit_op=%d\n", blit_op);
6389 return FALSE;
6393 /* Do not call while under the GL lock. */
6394 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6395 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
6397 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6398 struct wined3d_fb_state fb = {&dst_surface, NULL};
6400 return device_clear_render_targets(device, 1, &fb,
6401 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6404 /* Do not call while under the GL lock. */
6405 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6406 struct wined3d_surface *surface, const RECT *rect, float depth)
6408 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6409 struct wined3d_fb_state fb = {NULL, surface};
6411 return device_clear_render_targets(device, 0, &fb,
6412 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6415 const struct blit_shader ffp_blit = {
6416 ffp_blit_alloc,
6417 ffp_blit_free,
6418 ffp_blit_set,
6419 ffp_blit_unset,
6420 ffp_blit_supported,
6421 ffp_blit_color_fill,
6422 ffp_blit_depth_fill,
6425 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6427 return WINED3D_OK;
6430 /* Context activation is done by the caller. */
6431 static void cpu_blit_free(struct wined3d_device *device)
6435 /* Context activation is done by the caller. */
6436 static HRESULT cpu_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, struct wined3d_surface *surface)
6438 return WINED3D_OK;
6441 /* Context activation is done by the caller. */
6442 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6446 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6447 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6448 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6450 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6452 return TRUE;
6455 return FALSE;
6458 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6459 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6460 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6462 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6463 const struct wined3d_format *src_format, *dst_format;
6464 struct wined3d_surface *orig_src = src_surface;
6465 WINED3DLOCKED_RECT dlock, slock;
6466 HRESULT hr = WINED3D_OK;
6467 const BYTE *sbuf;
6468 RECT xdst,xsrc;
6469 BYTE *dbuf;
6470 int x, y;
6472 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6473 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6474 flags, fx, debug_d3dtexturefiltertype(filter));
6476 xsrc = *src_rect;
6478 if (!src_surface)
6480 RECT full_rect;
6482 full_rect.left = 0;
6483 full_rect.top = 0;
6484 full_rect.right = dst_surface->resource.width;
6485 full_rect.bottom = dst_surface->resource.height;
6486 IntersectRect(&xdst, &full_rect, dst_rect);
6488 else
6490 BOOL clip_horiz, clip_vert;
6492 xdst = *dst_rect;
6493 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6494 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6496 if (clip_vert || clip_horiz)
6498 /* Now check if this is a special case or not... */
6499 if ((flags & WINEDDBLT_DDFX)
6500 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6501 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6503 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6504 return WINED3D_OK;
6507 if (clip_horiz)
6509 if (xdst.left < 0)
6511 xsrc.left -= xdst.left;
6512 xdst.left = 0;
6514 if (xdst.right > dst_surface->resource.width)
6516 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6517 xdst.right = (int)dst_surface->resource.width;
6521 if (clip_vert)
6523 if (xdst.top < 0)
6525 xsrc.top -= xdst.top;
6526 xdst.top = 0;
6528 if (xdst.bottom > dst_surface->resource.height)
6530 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6531 xdst.bottom = (int)dst_surface->resource.height;
6535 /* And check if after clipping something is still to be done... */
6536 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6537 || (xdst.left >= (int)dst_surface->resource.width)
6538 || (xdst.top >= (int)dst_surface->resource.height)
6539 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6540 || (xsrc.left >= (int)src_surface->resource.width)
6541 || (xsrc.top >= (int)src_surface->resource.height))
6543 TRACE("Nothing to be done after clipping.\n");
6544 return WINED3D_OK;
6549 if (src_surface == dst_surface)
6551 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6552 slock = dlock;
6553 src_format = dst_surface->resource.format;
6554 dst_format = src_format;
6556 else
6558 dst_format = dst_surface->resource.format;
6559 if (src_surface)
6561 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6563 src_surface = surface_convert_format(src_surface, dst_format->id);
6564 if (!src_surface)
6566 /* The conv function writes a FIXME */
6567 WARN("Cannot convert source surface format to dest format.\n");
6568 goto release;
6571 wined3d_surface_map(src_surface, &slock, NULL, WINED3DLOCK_READONLY);
6572 src_format = src_surface->resource.format;
6574 else
6576 src_format = dst_format;
6578 if (dst_rect)
6579 wined3d_surface_map(dst_surface, &dlock, &xdst, 0);
6580 else
6581 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6584 bpp = dst_surface->resource.format->byte_count;
6585 srcheight = xsrc.bottom - xsrc.top;
6586 srcwidth = xsrc.right - xsrc.left;
6587 dstheight = xdst.bottom - xdst.top;
6588 dstwidth = xdst.right - xdst.left;
6589 width = (xdst.right - xdst.left) * bpp;
6591 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_COMPRESSED)
6593 UINT row_block_count;
6595 if (flags || src_surface == dst_surface)
6597 FIXME("Only plain blits supported on compressed surfaces.\n");
6598 hr = E_NOTIMPL;
6599 goto release;
6602 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6604 if (srcheight != dstheight || srcwidth != dstwidth)
6606 WARN("Stretching not supported on compressed surfaces.\n");
6607 hr = WINED3DERR_INVALIDCALL;
6608 goto release;
6611 dbuf = dlock.pBits;
6612 sbuf = slock.pBits;
6614 row_block_count = (dstwidth + dst_format->block_width - 1) / dst_format->block_width;
6615 for (y = 0; y < dstheight; y += dst_format->block_height)
6617 memcpy(dbuf, sbuf, row_block_count * dst_format->block_byte_count);
6618 dbuf += dlock.Pitch;
6619 sbuf += slock.Pitch;
6622 goto release;
6625 if (dst_rect && src_surface != dst_surface)
6626 dbuf = dlock.pBits;
6627 else
6628 dbuf = (BYTE*)dlock.pBits+(xdst.top*dlock.Pitch)+(xdst.left*bpp);
6630 /* First, all the 'source-less' blits */
6631 if (flags & WINEDDBLT_COLORFILL)
6633 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dlock.Pitch, fx->u5.dwFillColor);
6634 flags &= ~WINEDDBLT_COLORFILL;
6637 if (flags & WINEDDBLT_DEPTHFILL)
6639 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6641 if (flags & WINEDDBLT_ROP)
6643 /* Catch some degenerate cases here. */
6644 switch (fx->dwROP)
6646 case BLACKNESS:
6647 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,0);
6648 break;
6649 case 0xAA0029: /* No-op */
6650 break;
6651 case WHITENESS:
6652 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,~0);
6653 break;
6654 case SRCCOPY: /* Well, we do that below? */
6655 break;
6656 default:
6657 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6658 goto error;
6660 flags &= ~WINEDDBLT_ROP;
6662 if (flags & WINEDDBLT_DDROPS)
6664 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6666 /* Now the 'with source' blits. */
6667 if (src_surface)
6669 const BYTE *sbase;
6670 int sx, xinc, sy, yinc;
6672 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6673 goto release;
6675 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6676 && (srcwidth != dstwidth || srcheight != dstheight))
6678 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6679 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6682 sbase = (BYTE*)slock.pBits+(xsrc.top*slock.Pitch)+xsrc.left*bpp;
6683 xinc = (srcwidth << 16) / dstwidth;
6684 yinc = (srcheight << 16) / dstheight;
6686 if (!flags)
6688 /* No effects, we can cheat here. */
6689 if (dstwidth == srcwidth)
6691 if (dstheight == srcheight)
6693 /* No stretching in either direction. This needs to be as
6694 * fast as possible. */
6695 sbuf = sbase;
6697 /* Check for overlapping surfaces. */
6698 if (src_surface != dst_surface || xdst.top < xsrc.top
6699 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6701 /* No overlap, or dst above src, so copy from top downwards. */
6702 for (y = 0; y < dstheight; ++y)
6704 memcpy(dbuf, sbuf, width);
6705 sbuf += slock.Pitch;
6706 dbuf += dlock.Pitch;
6709 else if (xdst.top > xsrc.top)
6711 /* Copy from bottom upwards. */
6712 sbuf += (slock.Pitch*dstheight);
6713 dbuf += (dlock.Pitch*dstheight);
6714 for (y = 0; y < dstheight; ++y)
6716 sbuf -= slock.Pitch;
6717 dbuf -= dlock.Pitch;
6718 memcpy(dbuf, sbuf, width);
6721 else
6723 /* Src and dst overlapping on the same line, use memmove. */
6724 for (y = 0; y < dstheight; ++y)
6726 memmove(dbuf, sbuf, width);
6727 sbuf += slock.Pitch;
6728 dbuf += dlock.Pitch;
6732 else
6734 /* Stretching in y direction only. */
6735 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6737 sbuf = sbase + (sy >> 16) * slock.Pitch;
6738 memcpy(dbuf, sbuf, width);
6739 dbuf += dlock.Pitch;
6743 else
6745 /* Stretching in X direction. */
6746 int last_sy = -1;
6747 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6749 sbuf = sbase + (sy >> 16) * slock.Pitch;
6751 if ((sy >> 16) == (last_sy >> 16))
6753 /* This source row is the same as last source row -
6754 * Copy the already stretched row. */
6755 memcpy(dbuf, dbuf - dlock.Pitch, width);
6757 else
6759 #define STRETCH_ROW(type) \
6760 do { \
6761 const type *s = (const type *)sbuf; \
6762 type *d = (type *)dbuf; \
6763 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6764 d[x] = s[sx >> 16]; \
6765 } while(0)
6767 switch(bpp)
6769 case 1:
6770 STRETCH_ROW(BYTE);
6771 break;
6772 case 2:
6773 STRETCH_ROW(WORD);
6774 break;
6775 case 4:
6776 STRETCH_ROW(DWORD);
6777 break;
6778 case 3:
6780 const BYTE *s;
6781 BYTE *d = dbuf;
6782 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6784 DWORD pixel;
6786 s = sbuf + 3 * (sx >> 16);
6787 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6788 d[0] = (pixel ) & 0xff;
6789 d[1] = (pixel >> 8) & 0xff;
6790 d[2] = (pixel >> 16) & 0xff;
6791 d += 3;
6793 break;
6795 default:
6796 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6797 hr = WINED3DERR_NOTAVAILABLE;
6798 goto error;
6800 #undef STRETCH_ROW
6802 dbuf += dlock.Pitch;
6803 last_sy = sy;
6807 else
6809 LONG dstyinc = dlock.Pitch, dstxinc = bpp;
6810 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6811 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6812 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6814 /* The color keying flags are checked for correctness in ddraw */
6815 if (flags & WINEDDBLT_KEYSRC)
6817 keylow = src_surface->SrcBltCKey.dwColorSpaceLowValue;
6818 keyhigh = src_surface->SrcBltCKey.dwColorSpaceHighValue;
6820 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6822 keylow = fx->ddckSrcColorkey.dwColorSpaceLowValue;
6823 keyhigh = fx->ddckSrcColorkey.dwColorSpaceHighValue;
6826 if (flags & WINEDDBLT_KEYDEST)
6828 /* Destination color keys are taken from the source surface! */
6829 destkeylow = src_surface->DestBltCKey.dwColorSpaceLowValue;
6830 destkeyhigh = src_surface->DestBltCKey.dwColorSpaceHighValue;
6832 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6834 destkeylow = fx->ddckDestColorkey.dwColorSpaceLowValue;
6835 destkeyhigh = fx->ddckDestColorkey.dwColorSpaceHighValue;
6838 if (bpp == 1)
6840 keymask = 0xff;
6842 else
6844 keymask = src_format->red_mask
6845 | src_format->green_mask
6846 | src_format->blue_mask;
6848 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6851 if (flags & WINEDDBLT_DDFX)
6853 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6854 LONG tmpxy;
6855 dTopLeft = dbuf;
6856 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6857 dBottomLeft = dTopLeft + ((dstheight - 1) * dlock.Pitch);
6858 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6860 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6862 /* I don't think we need to do anything about this flag */
6863 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6865 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6867 tmp = dTopRight;
6868 dTopRight = dTopLeft;
6869 dTopLeft = tmp;
6870 tmp = dBottomRight;
6871 dBottomRight = dBottomLeft;
6872 dBottomLeft = tmp;
6873 dstxinc = dstxinc * -1;
6875 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6877 tmp = dTopLeft;
6878 dTopLeft = dBottomLeft;
6879 dBottomLeft = tmp;
6880 tmp = dTopRight;
6881 dTopRight = dBottomRight;
6882 dBottomRight = tmp;
6883 dstyinc = dstyinc * -1;
6885 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6887 /* I don't think we need to do anything about this flag */
6888 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6890 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6892 tmp = dBottomRight;
6893 dBottomRight = dTopLeft;
6894 dTopLeft = tmp;
6895 tmp = dBottomLeft;
6896 dBottomLeft = dTopRight;
6897 dTopRight = tmp;
6898 dstxinc = dstxinc * -1;
6899 dstyinc = dstyinc * -1;
6901 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6903 tmp = dTopLeft;
6904 dTopLeft = dBottomLeft;
6905 dBottomLeft = dBottomRight;
6906 dBottomRight = dTopRight;
6907 dTopRight = tmp;
6908 tmpxy = dstxinc;
6909 dstxinc = dstyinc;
6910 dstyinc = tmpxy;
6911 dstxinc = dstxinc * -1;
6913 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6915 tmp = dTopLeft;
6916 dTopLeft = dTopRight;
6917 dTopRight = dBottomRight;
6918 dBottomRight = dBottomLeft;
6919 dBottomLeft = tmp;
6920 tmpxy = dstxinc;
6921 dstxinc = dstyinc;
6922 dstyinc = tmpxy;
6923 dstyinc = dstyinc * -1;
6925 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6927 /* I don't think we need to do anything about this flag */
6928 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6930 dbuf = dTopLeft;
6931 flags &= ~(WINEDDBLT_DDFX);
6934 #define COPY_COLORKEY_FX(type) \
6935 do { \
6936 const type *s; \
6937 type *d = (type *)dbuf, *dx, tmp; \
6938 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6940 s = (const type *)(sbase + (sy >> 16) * slock.Pitch); \
6941 dx = d; \
6942 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6944 tmp = s[sx >> 16]; \
6945 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6946 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6948 dx[0] = tmp; \
6950 dx = (type *)(((BYTE *)dx) + dstxinc); \
6952 d = (type *)(((BYTE *)d) + dstyinc); \
6954 } while(0)
6956 switch (bpp)
6958 case 1:
6959 COPY_COLORKEY_FX(BYTE);
6960 break;
6961 case 2:
6962 COPY_COLORKEY_FX(WORD);
6963 break;
6964 case 4:
6965 COPY_COLORKEY_FX(DWORD);
6966 break;
6967 case 3:
6969 const BYTE *s;
6970 BYTE *d = dbuf, *dx;
6971 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6973 sbuf = sbase + (sy >> 16) * slock.Pitch;
6974 dx = d;
6975 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
6977 DWORD pixel, dpixel = 0;
6978 s = sbuf + 3 * (sx>>16);
6979 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6980 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
6981 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
6982 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
6984 dx[0] = (pixel ) & 0xff;
6985 dx[1] = (pixel >> 8) & 0xff;
6986 dx[2] = (pixel >> 16) & 0xff;
6988 dx += dstxinc;
6990 d += dstyinc;
6992 break;
6994 default:
6995 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
6996 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
6997 hr = WINED3DERR_NOTAVAILABLE;
6998 goto error;
6999 #undef COPY_COLORKEY_FX
7004 error:
7005 if (flags && FIXME_ON(d3d_surface))
7007 FIXME("\tUnsupported flags: %#x.\n", flags);
7010 release:
7011 wined3d_surface_unmap(dst_surface);
7012 if (src_surface && src_surface != dst_surface)
7013 wined3d_surface_unmap(src_surface);
7014 /* Release the converted surface, if any. */
7015 if (src_surface && src_surface != orig_src)
7016 wined3d_surface_decref(src_surface);
7018 return hr;
7021 /* Do not call while under the GL lock. */
7022 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7023 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
7025 static const RECT src_rect;
7026 WINEDDBLTFX BltFx;
7028 memset(&BltFx, 0, sizeof(BltFx));
7029 BltFx.dwSize = sizeof(BltFx);
7030 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7031 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7032 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7035 /* Do not call while under the GL lock. */
7036 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7037 struct wined3d_surface *surface, const RECT *rect, float depth)
7039 FIXME("Depth filling not implemented by cpu_blit.\n");
7040 return WINED3DERR_INVALIDCALL;
7043 const struct blit_shader cpu_blit = {
7044 cpu_blit_alloc,
7045 cpu_blit_free,
7046 cpu_blit_set,
7047 cpu_blit_unset,
7048 cpu_blit_supported,
7049 cpu_blit_color_fill,
7050 cpu_blit_depth_fill,
7053 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7054 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
7055 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7056 WINED3DPOOL pool, void *parent, const struct wined3d_parent_ops *parent_ops)
7058 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7059 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7060 unsigned int resource_size;
7061 HRESULT hr;
7063 if (multisample_quality > 0)
7065 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7066 multisample_quality = 0;
7069 /* Quick lockable sanity check.
7070 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7071 * this function is too deep to need to care about things like this.
7072 * Levels need to be checked too, since they all affect what can be done. */
7073 switch (pool)
7075 case WINED3DPOOL_SCRATCH:
7076 if (!lockable)
7078 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7079 "which are mutually exclusive, setting lockable to TRUE.\n");
7080 lockable = TRUE;
7082 break;
7084 case WINED3DPOOL_SYSTEMMEM:
7085 if (!lockable)
7086 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7087 break;
7089 case WINED3DPOOL_MANAGED:
7090 if (usage & WINED3DUSAGE_DYNAMIC)
7091 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7092 break;
7094 case WINED3DPOOL_DEFAULT:
7095 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7096 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7097 break;
7099 default:
7100 FIXME("Unknown pool %#x.\n", pool);
7101 break;
7104 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7105 FIXME("Trying to create a render target that isn't in the default pool.\n");
7107 /* FIXME: Check that the format is supported by the device. */
7109 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7110 if (!resource_size)
7111 return WINED3DERR_INVALIDCALL;
7113 surface->surface_type = surface_type;
7115 switch (surface_type)
7117 case SURFACE_OPENGL:
7118 surface->surface_ops = &surface_ops;
7119 break;
7121 case SURFACE_GDI:
7122 surface->surface_ops = &gdi_surface_ops;
7123 break;
7125 default:
7126 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7127 return WINED3DERR_INVALIDCALL;
7130 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7131 multisample_type, multisample_quality, usage, pool, width, height, 1,
7132 resource_size, parent, parent_ops, &surface_resource_ops);
7133 if (FAILED(hr))
7135 WARN("Failed to initialize resource, returning %#x.\n", hr);
7136 return hr;
7139 /* "Standalone" surface. */
7140 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7142 surface->texture_level = level;
7143 list_init(&surface->overlays);
7145 /* Flags */
7146 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7147 if (discard)
7148 surface->flags |= SFLAG_DISCARD;
7149 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7150 surface->flags |= SFLAG_LOCKABLE;
7151 /* I'm not sure if this qualifies as a hack or as an optimization. It
7152 * seems reasonable to assume that lockable render targets will get
7153 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7154 * creation. However, the other reason we want to do this is that several
7155 * ddraw applications access surface memory while the surface isn't
7156 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7157 * future locks prevents these from crashing. */
7158 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7159 surface->flags |= SFLAG_DYNLOCK;
7161 /* Mark the texture as dirty so that it gets loaded first time around. */
7162 surface_add_dirty_rect(surface, NULL);
7163 list_init(&surface->renderbuffers);
7165 TRACE("surface %p, memory %p, size %u\n",
7166 surface, surface->resource.allocatedMemory, surface->resource.size);
7168 /* Call the private setup routine */
7169 hr = surface->surface_ops->surface_private_setup(surface);
7170 if (FAILED(hr))
7172 ERR("Private setup failed, returning %#x\n", hr);
7173 surface->surface_ops->surface_cleanup(surface);
7174 return hr;
7177 return hr;
7180 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7181 enum wined3d_format_id format_id, BOOL lockable, BOOL discard, UINT level, DWORD usage, WINED3DPOOL pool,
7182 WINED3DMULTISAMPLE_TYPE multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7183 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7185 struct wined3d_surface *object;
7186 HRESULT hr;
7188 TRACE("device %p, width %u, height %u, format %s, lockable %#x, discard %#x, level %u\n",
7189 device, width, height, debug_d3dformat(format_id), lockable, discard, level);
7190 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7191 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7192 TRACE("surface_type %#x, parent %p, parent_ops %p.\n", surface_type, parent, parent_ops);
7194 if (surface_type == SURFACE_OPENGL && !device->adapter)
7196 ERR("OpenGL surfaces are not available without OpenGL.\n");
7197 return WINED3DERR_NOTAVAILABLE;
7200 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7201 if (!object)
7203 ERR("Failed to allocate surface memory.\n");
7204 return WINED3DERR_OUTOFVIDEOMEMORY;
7207 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level, lockable,
7208 discard, multisample_type, multisample_quality, device, usage, format_id, pool, parent, parent_ops);
7209 if (FAILED(hr))
7211 WARN("Failed to initialize surface, returning %#x.\n", hr);
7212 HeapFree(GetProcessHeap(), 0, object);
7213 return hr;
7216 TRACE("Created surface %p.\n", object);
7217 *surface = object;
7219 return hr;