msxml3/mxnamespace: Implement declarePrefix() and getDeclaredPrefix().
[wine/multimedia.git] / dlls / wined3d / surface.c
blob2fc08d8d9f47d673ef591e1bd788cff9739745ed
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_INDRAWABLE, 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;
1688 fallback:
1690 /* Special cases for render targets. */
1691 if ((dst_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
1692 || (src_surface && (src_surface->resource.usage & WINED3DUSAGE_RENDERTARGET)))
1694 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(dst_surface, &dst_rect,
1695 src_surface, &src_rect, flags, fx, filter)))
1696 return WINED3D_OK;
1699 cpu:
1701 /* For the rest call the X11 surface implementation. For render targets
1702 * this should be implemented OpenGL accelerated in BltOverride, other
1703 * blits are rather rare. */
1704 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
1707 /* Do not call while under the GL lock. */
1708 HRESULT CDECL wined3d_surface_bltfast(struct wined3d_surface *dst_surface, DWORD dst_x, DWORD dst_y,
1709 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD trans)
1711 RECT src_rect, dst_rect;
1712 DWORD flags = 0;
1714 TRACE("dst_surface %p, dst_x %u, dst_y %u, src_surface %p, src_rect_in %s, trans %#x.\n",
1715 dst_surface, dst_x, dst_y, src_surface, wine_dbgstr_rect(src_rect_in), trans);
1717 surface_get_rect(src_surface, src_rect_in, &src_rect);
1719 dst_rect.left = dst_x;
1720 dst_rect.top = dst_y;
1721 dst_rect.right = dst_x + src_rect.right - src_rect.left;
1722 dst_rect.bottom = dst_y + src_rect.bottom - src_rect.top;
1724 if (trans & WINEDDBLTFAST_SRCCOLORKEY)
1725 flags |= WINEDDBLT_KEYSRC;
1726 if (trans & WINEDDBLTFAST_DESTCOLORKEY)
1727 flags |= WINEDDBLT_KEYDEST;
1728 if (trans & WINEDDBLTFAST_WAIT)
1729 flags |= WINEDDBLT_WAIT;
1730 if (trans & WINEDDBLTFAST_DONOTWAIT)
1731 flags |= WINEDDBLT_DONOTWAIT;
1733 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, NULL, WINED3DTEXF_POINT);
1736 static HRESULT surface_set_mem(struct wined3d_surface *surface, void *mem)
1738 TRACE("surface %p, mem %p.\n", surface, mem);
1740 if (mem && mem != surface->resource.allocatedMemory)
1742 void *release = NULL;
1744 /* Do I have to copy the old surface content? */
1745 if (surface->flags & SFLAG_DIBSECTION)
1747 SelectObject(surface->hDC, surface->dib.holdbitmap);
1748 DeleteDC(surface->hDC);
1749 /* Release the DIB section. */
1750 DeleteObject(surface->dib.DIBsection);
1751 surface->dib.bitmap_data = NULL;
1752 surface->resource.allocatedMemory = NULL;
1753 surface->hDC = NULL;
1754 surface->flags &= ~SFLAG_DIBSECTION;
1756 else if (!(surface->flags & SFLAG_USERPTR))
1758 release = surface->resource.heapMemory;
1759 surface->resource.heapMemory = NULL;
1761 surface->resource.allocatedMemory = mem;
1762 surface->flags |= SFLAG_USERPTR;
1764 /* Now the surface memory is most up do date. Invalidate drawable and texture. */
1765 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1767 /* For client textures OpenGL has to be notified. */
1768 if (surface->flags & SFLAG_CLIENT)
1769 surface_release_client_storage(surface);
1771 /* Now free the old memory if any. */
1772 HeapFree(GetProcessHeap(), 0, release);
1774 else if (surface->flags & SFLAG_USERPTR)
1776 /* Map and GetDC will re-create the dib section and allocated memory. */
1777 surface->resource.allocatedMemory = NULL;
1778 /* HeapMemory should be NULL already. */
1779 if (surface->resource.heapMemory)
1780 ERR("User pointer surface has heap memory allocated.\n");
1781 surface->flags &= ~(SFLAG_USERPTR | SFLAG_INSYSMEM);
1783 if (surface->flags & SFLAG_CLIENT)
1784 surface_release_client_storage(surface);
1786 surface_prepare_system_memory(surface);
1787 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
1790 return WINED3D_OK;
1793 /* Context activation is done by the caller. */
1794 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1796 if (!surface->resource.heapMemory)
1798 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), 0, surface->resource.size + RESOURCE_ALIGNMENT);
1799 surface->resource.allocatedMemory = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
1800 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1803 ENTER_GL();
1804 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
1805 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, surface->pbo)");
1806 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0,
1807 surface->resource.size, surface->resource.allocatedMemory));
1808 checkGLcall("glGetBufferSubDataARB");
1809 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1810 checkGLcall("glDeleteBuffersARB");
1811 LEAVE_GL();
1813 surface->pbo = 0;
1814 surface->flags &= ~SFLAG_PBO;
1817 /* Do not call while under the GL lock. */
1818 static void surface_unload(struct wined3d_resource *resource)
1820 struct wined3d_surface *surface = surface_from_resource(resource);
1821 struct wined3d_renderbuffer_entry *entry, *entry2;
1822 struct wined3d_device *device = resource->device;
1823 const struct wined3d_gl_info *gl_info;
1824 struct wined3d_context *context;
1826 TRACE("surface %p.\n", surface);
1828 if (resource->pool == WINED3DPOOL_DEFAULT)
1830 /* Default pool resources are supposed to be destroyed before Reset is called.
1831 * Implicit resources stay however. So this means we have an implicit render target
1832 * or depth stencil. The content may be destroyed, but we still have to tear down
1833 * opengl resources, so we cannot leave early.
1835 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1836 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1837 * or the depth stencil into an FBO the texture or render buffer will be removed
1838 * and all flags get lost
1840 surface_init_sysmem(surface);
1842 else
1844 /* Load the surface into system memory */
1845 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
1846 surface_modify_location(surface, SFLAG_INDRAWABLE, FALSE);
1848 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
1849 surface_modify_location(surface, SFLAG_INSRGBTEX, FALSE);
1850 surface->flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1852 context = context_acquire(device, NULL);
1853 gl_info = context->gl_info;
1855 /* Destroy PBOs, but load them into real sysmem before */
1856 if (surface->flags & SFLAG_PBO)
1857 surface_remove_pbo(surface, gl_info);
1859 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1860 * all application-created targets the application has to release the surface
1861 * before calling _Reset
1863 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1865 ENTER_GL();
1866 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1867 LEAVE_GL();
1868 list_remove(&entry->entry);
1869 HeapFree(GetProcessHeap(), 0, entry);
1871 list_init(&surface->renderbuffers);
1872 surface->current_renderbuffer = NULL;
1874 /* If we're in a texture, the texture name belongs to the texture.
1875 * Otherwise, destroy it. */
1876 if (surface->container.type != WINED3D_CONTAINER_TEXTURE)
1878 ENTER_GL();
1879 glDeleteTextures(1, &surface->texture_name);
1880 surface->texture_name = 0;
1881 glDeleteTextures(1, &surface->texture_name_srgb);
1882 surface->texture_name_srgb = 0;
1883 LEAVE_GL();
1886 context_release(context);
1888 resource_unload(resource);
1891 static const struct wined3d_resource_ops surface_resource_ops =
1893 surface_unload,
1896 static const struct wined3d_surface_ops surface_ops =
1898 surface_private_setup,
1899 surface_cleanup,
1900 surface_realize_palette,
1901 surface_draw_overlay,
1902 surface_preload,
1903 surface_map,
1904 surface_unmap,
1905 surface_getdc,
1906 surface_flip,
1907 surface_set_mem,
1910 /*****************************************************************************
1911 * Initializes the GDI surface, aka creates the DIB section we render to
1912 * The DIB section creation is done by calling GetDC, which will create the
1913 * section and releasing the dc to allow the app to use it. The dib section
1914 * will stay until the surface is released
1916 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1917 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1918 * avoid confusion in the shared surface code.
1920 * Returns:
1921 * WINED3D_OK on success
1922 * The return values of called methods on failure
1924 *****************************************************************************/
1925 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1927 HRESULT hr;
1929 TRACE("surface %p.\n", surface);
1931 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1933 ERR("Overlays not yet supported by GDI surfaces.\n");
1934 return WINED3DERR_INVALIDCALL;
1937 /* Sysmem textures have memory already allocated - release it,
1938 * this avoids an unnecessary memcpy. */
1939 hr = surface_create_dib_section(surface);
1940 if (SUCCEEDED(hr))
1942 HeapFree(GetProcessHeap(), 0, surface->resource.heapMemory);
1943 surface->resource.heapMemory = NULL;
1944 surface->resource.allocatedMemory = surface->dib.bitmap_data;
1947 /* We don't mind the nonpow2 stuff in GDI. */
1948 surface->pow2Width = surface->resource.width;
1949 surface->pow2Height = surface->resource.height;
1951 return WINED3D_OK;
1954 static void surface_gdi_cleanup(struct wined3d_surface *surface)
1956 TRACE("surface %p.\n", surface);
1958 if (surface->flags & SFLAG_DIBSECTION)
1960 /* Release the DC. */
1961 SelectObject(surface->hDC, surface->dib.holdbitmap);
1962 DeleteDC(surface->hDC);
1963 /* Release the DIB section. */
1964 DeleteObject(surface->dib.DIBsection);
1965 surface->dib.bitmap_data = NULL;
1966 surface->resource.allocatedMemory = NULL;
1969 if (surface->flags & SFLAG_USERPTR)
1970 wined3d_surface_set_mem(surface, NULL);
1971 if (surface->overlay_dest)
1972 list_remove(&surface->overlay_entry);
1974 HeapFree(GetProcessHeap(), 0, surface->palette9);
1976 resource_cleanup(&surface->resource);
1979 static void gdi_surface_realize_palette(struct wined3d_surface *surface)
1981 struct wined3d_palette *palette = surface->palette;
1983 TRACE("surface %p.\n", surface);
1985 if (!palette) return;
1987 if (surface->flags & SFLAG_DIBSECTION)
1989 RGBQUAD col[256];
1990 unsigned int i;
1992 TRACE("Updating the DC's palette.\n");
1994 for (i = 0; i < 256; ++i)
1996 col[i].rgbRed = palette->palents[i].peRed;
1997 col[i].rgbGreen = palette->palents[i].peGreen;
1998 col[i].rgbBlue = palette->palents[i].peBlue;
1999 col[i].rgbReserved = 0;
2001 SetDIBColorTable(surface->hDC, 0, 256, col);
2004 /* Update the image because of the palette change. Some games like e.g.
2005 * Red Alert call SetEntries a lot to implement fading. */
2006 /* Tell the swapchain to update the screen. */
2007 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2009 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2010 if (surface == swapchain->front_buffer)
2012 x11_copy_to_screen(swapchain, NULL);
2017 static HRESULT gdi_surface_draw_overlay(struct wined3d_surface *surface)
2019 FIXME("GDI surfaces can't draw overlays yet.\n");
2020 return E_FAIL;
2023 static void gdi_surface_preload(struct wined3d_surface *surface)
2025 TRACE("surface %p.\n", surface);
2027 ERR("Preloading GDI surfaces is not supported.\n");
2030 static void gdi_surface_map(struct wined3d_surface *surface, const RECT *rect, DWORD flags)
2032 TRACE("surface %p, rect %s, flags %#x.\n",
2033 surface, wine_dbgstr_rect(rect), flags);
2035 if (!surface->resource.allocatedMemory)
2037 /* This happens on gdi surfaces if the application set a user pointer
2038 * and resets it. Recreate the DIB section. */
2039 surface_create_dib_section(surface);
2040 surface->resource.allocatedMemory = surface->dib.bitmap_data;
2044 static void gdi_surface_unmap(struct wined3d_surface *surface)
2046 TRACE("surface %p.\n", surface);
2048 /* Tell the swapchain to update the screen. */
2049 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
2051 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2052 if (surface == swapchain->front_buffer)
2054 x11_copy_to_screen(swapchain, &surface->lockedRect);
2058 memset(&surface->lockedRect, 0, sizeof(RECT));
2061 static HRESULT gdi_surface_getdc(struct wined3d_surface *surface)
2063 WINED3DLOCKED_RECT lock;
2064 HRESULT hr;
2066 TRACE("surface %p.\n", surface);
2068 /* Should have a DIB section already. */
2069 if (!(surface->flags & SFLAG_DIBSECTION))
2071 WARN("DC not supported on this surface\n");
2072 return WINED3DERR_INVALIDCALL;
2075 /* Map the surface. */
2076 hr = wined3d_surface_map(surface, &lock, NULL, 0);
2077 if (FAILED(hr))
2078 ERR("Map failed, hr %#x.\n", hr);
2080 return hr;
2083 static HRESULT gdi_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override)
2085 TRACE("surface %p, override %p.\n", surface, override);
2087 return WINED3D_OK;
2090 static HRESULT gdi_surface_set_mem(struct wined3d_surface *surface, void *mem)
2092 TRACE("surface %p, mem %p.\n", surface, mem);
2094 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer. */
2095 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
2097 ERR("Not supported on render targets.\n");
2098 return WINED3DERR_INVALIDCALL;
2101 if (mem && mem != surface->resource.allocatedMemory)
2103 void *release = NULL;
2105 /* Do I have to copy the old surface content? */
2106 if (surface->flags & SFLAG_DIBSECTION)
2108 SelectObject(surface->hDC, surface->dib.holdbitmap);
2109 DeleteDC(surface->hDC);
2110 /* Release the DIB section. */
2111 DeleteObject(surface->dib.DIBsection);
2112 surface->dib.bitmap_data = NULL;
2113 surface->resource.allocatedMemory = NULL;
2114 surface->hDC = NULL;
2115 surface->flags &= ~SFLAG_DIBSECTION;
2117 else if (!(surface->flags & SFLAG_USERPTR))
2119 release = surface->resource.allocatedMemory;
2121 surface->resource.allocatedMemory = mem;
2122 surface->flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2124 /* Now free the old memory, if any. */
2125 HeapFree(GetProcessHeap(), 0, release);
2127 else if (surface->flags & SFLAG_USERPTR)
2129 /* Map() and GetDC() will re-create the dib section and allocated memory. */
2130 surface->resource.allocatedMemory = NULL;
2131 surface->flags &= ~SFLAG_USERPTR;
2134 return WINED3D_OK;
2137 static const struct wined3d_surface_ops gdi_surface_ops =
2139 gdi_surface_private_setup,
2140 surface_gdi_cleanup,
2141 gdi_surface_realize_palette,
2142 gdi_surface_draw_overlay,
2143 gdi_surface_preload,
2144 gdi_surface_map,
2145 gdi_surface_unmap,
2146 gdi_surface_getdc,
2147 gdi_surface_flip,
2148 gdi_surface_set_mem,
2151 void surface_set_texture_name(struct wined3d_surface *surface, GLuint new_name, BOOL srgb)
2153 GLuint *name;
2154 DWORD flag;
2156 TRACE("surface %p, new_name %u, srgb %#x.\n", surface, new_name, srgb);
2158 if(srgb)
2160 name = &surface->texture_name_srgb;
2161 flag = SFLAG_INSRGBTEX;
2163 else
2165 name = &surface->texture_name;
2166 flag = SFLAG_INTEXTURE;
2169 if (!*name && new_name)
2171 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
2172 * surface has no texture name yet. See if we can get rid of this. */
2173 if (surface->flags & flag)
2174 ERR("Surface has %s set, but no texture name.\n", debug_surflocation(flag));
2175 surface_modify_location(surface, flag, FALSE);
2178 *name = new_name;
2179 surface_force_reload(surface);
2182 void surface_set_texture_target(struct wined3d_surface *surface, GLenum target)
2184 TRACE("surface %p, target %#x.\n", surface, target);
2186 if (surface->texture_target != target)
2188 if (target == GL_TEXTURE_RECTANGLE_ARB)
2190 surface->flags &= ~SFLAG_NORMCOORD;
2192 else if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2194 surface->flags |= SFLAG_NORMCOORD;
2197 surface->texture_target = target;
2198 surface_force_reload(surface);
2201 /* Context activation is done by the caller. */
2202 void surface_bind(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
2204 TRACE("surface %p, gl_info %p, srgb %#x.\n", surface, gl_info, srgb);
2206 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2208 struct wined3d_texture *texture = surface->container.u.texture;
2210 TRACE("Passing to container (%p).\n", texture);
2211 texture->texture_ops->texture_bind(texture, gl_info, srgb);
2213 else
2215 if (surface->texture_level)
2217 ERR("Standalone surface %p is non-zero texture level %u.\n",
2218 surface, surface->texture_level);
2221 if (srgb)
2222 ERR("Trying to bind standalone surface %p as sRGB.\n", surface);
2224 ENTER_GL();
2226 if (!surface->texture_name)
2228 glGenTextures(1, &surface->texture_name);
2229 checkGLcall("glGenTextures");
2231 TRACE("Surface %p given name %u.\n", surface, surface->texture_name);
2233 glBindTexture(surface->texture_target, surface->texture_name);
2234 checkGLcall("glBindTexture");
2235 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2236 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2237 glTexParameteri(surface->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2238 glTexParameteri(surface->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2239 glTexParameteri(surface->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2240 checkGLcall("glTexParameteri");
2242 else
2244 glBindTexture(surface->texture_target, surface->texture_name);
2245 checkGLcall("glBindTexture");
2248 LEAVE_GL();
2252 /* This call just downloads data, the caller is responsible for binding the
2253 * correct texture. */
2254 /* Context activation is done by the caller. */
2255 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
2257 const struct wined3d_format *format = surface->resource.format;
2259 /* Only support read back of converted P8 surfaces. */
2260 if (surface->flags & SFLAG_CONVERTED && format->id != WINED3DFMT_P8_UINT)
2262 FIXME("Readback conversion not supported for format %s.\n", debug_d3dformat(format->id));
2263 return;
2266 ENTER_GL();
2268 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2270 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
2271 surface, surface->texture_level, format->glFormat, format->glType,
2272 surface->resource.allocatedMemory);
2274 if (surface->flags & SFLAG_PBO)
2276 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2277 checkGLcall("glBindBufferARB");
2278 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
2279 checkGLcall("glGetCompressedTexImageARB");
2280 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2281 checkGLcall("glBindBufferARB");
2283 else
2285 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
2286 surface->texture_level, surface->resource.allocatedMemory));
2287 checkGLcall("glGetCompressedTexImageARB");
2290 LEAVE_GL();
2292 else
2294 void *mem;
2295 GLenum gl_format = format->glFormat;
2296 GLenum gl_type = format->glType;
2297 int src_pitch = 0;
2298 int dst_pitch = 0;
2300 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8. */
2301 if (format->id == WINED3DFMT_P8_UINT && primary_render_target_is_p8(surface->resource.device))
2303 gl_format = GL_ALPHA;
2304 gl_type = GL_UNSIGNED_BYTE;
2307 if (surface->flags & SFLAG_NONPOW2)
2309 unsigned char alignment = surface->resource.device->surface_alignment;
2310 src_pitch = format->byte_count * surface->pow2Width;
2311 dst_pitch = wined3d_surface_get_pitch(surface);
2312 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
2313 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
2315 else
2317 mem = surface->resource.allocatedMemory;
2320 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
2321 surface, surface->texture_level, gl_format, gl_type, mem);
2323 if (surface->flags & SFLAG_PBO)
2325 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
2326 checkGLcall("glBindBufferARB");
2328 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, NULL);
2329 checkGLcall("glGetTexImage");
2331 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2332 checkGLcall("glBindBufferARB");
2334 else
2336 glGetTexImage(surface->texture_target, surface->texture_level, gl_format, gl_type, mem);
2337 checkGLcall("glGetTexImage");
2339 LEAVE_GL();
2341 if (surface->flags & SFLAG_NONPOW2)
2343 const BYTE *src_data;
2344 BYTE *dst_data;
2345 UINT y;
2347 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
2348 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
2349 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
2351 * We're doing this...
2353 * instead of boxing the texture :
2354 * |<-texture width ->| -->pow2width| /\
2355 * |111111111111111111| | |
2356 * |222 Texture 222222| boxed empty | texture height
2357 * |3333 Data 33333333| | |
2358 * |444444444444444444| | \/
2359 * ----------------------------------- |
2360 * | boxed empty | boxed empty | pow2height
2361 * | | | \/
2362 * -----------------------------------
2365 * we're repacking the data to the expected texture width
2367 * |<-texture width ->| -->pow2width| /\
2368 * |111111111111111111222222222222222| |
2369 * |222333333333333333333444444444444| texture height
2370 * |444444 | |
2371 * | | \/
2372 * | | |
2373 * | empty | pow2height
2374 * | | \/
2375 * -----------------------------------
2377 * == is the same as
2379 * |<-texture width ->| /\
2380 * |111111111111111111|
2381 * |222222222222222222|texture height
2382 * |333333333333333333|
2383 * |444444444444444444| \/
2384 * --------------------
2386 * this also means that any references to allocatedMemory should work with the data as if were a
2387 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
2389 * internally the texture is still stored in a boxed format so any references to textureName will
2390 * get a boxed texture with width pow2width and not a texture of width resource.width.
2392 * Performance should not be an issue, because applications normally do not lock the surfaces when
2393 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
2394 * and doesn't have to be re-read. */
2395 src_data = mem;
2396 dst_data = surface->resource.allocatedMemory;
2397 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
2398 for (y = 1; y < surface->resource.height; ++y)
2400 /* skip the first row */
2401 src_data += src_pitch;
2402 dst_data += dst_pitch;
2403 memcpy(dst_data, src_data, dst_pitch);
2406 HeapFree(GetProcessHeap(), 0, mem);
2410 /* Surface has now been downloaded */
2411 surface->flags |= SFLAG_INSYSMEM;
2414 /* This call just uploads data, the caller is responsible for binding the
2415 * correct texture. */
2416 /* Context activation is done by the caller. */
2417 void surface_upload_data(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2418 const struct wined3d_format *format, const RECT *src_rect, UINT src_w, const POINT *dst_point,
2419 BOOL srgb, const struct wined3d_bo_address *data)
2421 UINT update_w = src_rect->right - src_rect->left;
2422 UINT update_h = src_rect->bottom - src_rect->top;
2424 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_w %u, dst_point %p, srgb %#x, data {%#x:%p}.\n",
2425 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_w,
2426 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
2428 if (format->heightscale != 1.0f && format->heightscale != 0.0f)
2429 update_h *= format->heightscale;
2431 ENTER_GL();
2433 if (data->buffer_object)
2435 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
2436 checkGLcall("glBindBufferARB");
2439 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
2441 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1);
2442 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
2443 UINT src_pitch = wined3d_format_calculate_size(format, 1, src_w, 1);
2444 const BYTE *addr = data->addr;
2445 GLenum internal;
2447 addr += (src_rect->top / format->block_height) * src_pitch;
2448 addr += (src_rect->left / format->block_width) * format->block_byte_count;
2450 if (srgb)
2451 internal = format->glGammaInternal;
2452 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2453 internal = format->rtInternal;
2454 else
2455 internal = format->glInternal;
2457 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
2458 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
2459 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
2461 if (row_length == src_pitch)
2463 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2464 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
2466 else
2468 UINT row, y;
2470 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
2471 * can't use the unpack row length like below. */
2472 for (row = 0, y = dst_point->y; row < row_count; ++row)
2474 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
2475 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
2476 y += format->block_height;
2477 addr += src_pitch;
2480 checkGLcall("glCompressedTexSubImage2DARB");
2482 else
2484 const BYTE *addr = data->addr;
2486 addr += src_rect->top * src_w * format->byte_count;
2487 addr += src_rect->left * format->byte_count;
2489 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
2490 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2491 update_w, update_h, format->glFormat, format->glType, addr);
2493 glPixelStorei(GL_UNPACK_ROW_LENGTH, src_w);
2494 glTexSubImage2D(surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
2495 update_w, update_h, format->glFormat, format->glType, addr);
2496 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2497 checkGLcall("glTexSubImage2D");
2500 if (data->buffer_object)
2502 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2503 checkGLcall("glBindBufferARB");
2506 LEAVE_GL();
2508 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
2510 struct wined3d_device *device = surface->resource.device;
2511 unsigned int i;
2513 for (i = 0; i < device->context_count; ++i)
2515 context_surface_update(device->contexts[i], surface);
2520 /* This call just allocates the texture, the caller is responsible for binding
2521 * the correct texture. */
2522 /* Context activation is done by the caller. */
2523 static void surface_allocate_surface(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
2524 const struct wined3d_format *format, BOOL srgb)
2526 BOOL enable_client_storage = FALSE;
2527 GLsizei width = surface->pow2Width;
2528 GLsizei height = surface->pow2Height;
2529 const BYTE *mem = NULL;
2530 GLenum internal;
2532 if (srgb)
2534 internal = format->glGammaInternal;
2536 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(surface))
2538 internal = format->rtInternal;
2540 else
2542 internal = format->glInternal;
2545 if (format->heightscale != 1.0f && format->heightscale != 0.0f) height *= format->heightscale;
2547 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",
2548 surface, surface->texture_target, surface->texture_level, debug_d3dformat(format->id),
2549 internal, width, height, format->glFormat, format->glType);
2551 ENTER_GL();
2553 if (gl_info->supported[APPLE_CLIENT_STORAGE])
2555 if (surface->flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED)
2556 || !surface->resource.allocatedMemory)
2558 /* In some cases we want to disable client storage.
2559 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
2560 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
2561 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
2562 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
2564 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2565 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
2566 surface->flags &= ~SFLAG_CLIENT;
2567 enable_client_storage = TRUE;
2569 else
2571 surface->flags |= SFLAG_CLIENT;
2573 /* Point OpenGL to our allocated texture memory. Do not use
2574 * resource.allocatedMemory here because it might point into a
2575 * PBO. Instead use heapMemory, but get the alignment right. */
2576 mem = (BYTE *)(((ULONG_PTR)surface->resource.heapMemory
2577 + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
2581 if (format->flags & WINED3DFMT_FLAG_COMPRESSED && mem)
2583 GL_EXTCALL(glCompressedTexImage2DARB(surface->texture_target, surface->texture_level,
2584 internal, width, height, 0, surface->resource.size, mem));
2585 checkGLcall("glCompressedTexImage2DARB");
2587 else
2589 glTexImage2D(surface->texture_target, surface->texture_level,
2590 internal, width, height, 0, format->glFormat, format->glType, mem);
2591 checkGLcall("glTexImage2D");
2594 if(enable_client_storage) {
2595 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2596 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
2598 LEAVE_GL();
2601 /* In D3D the depth stencil dimensions have to be greater than or equal to the
2602 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
2603 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
2604 /* GL locking is done by the caller */
2605 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
2607 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
2608 struct wined3d_renderbuffer_entry *entry;
2609 GLuint renderbuffer = 0;
2610 unsigned int src_width, src_height;
2611 unsigned int width, height;
2613 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
2615 width = rt->pow2Width;
2616 height = rt->pow2Height;
2618 else
2620 width = surface->pow2Width;
2621 height = surface->pow2Height;
2624 src_width = surface->pow2Width;
2625 src_height = surface->pow2Height;
2627 /* A depth stencil smaller than the render target is not valid */
2628 if (width > src_width || height > src_height) return;
2630 /* Remove any renderbuffer set if the sizes match */
2631 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2632 || (width == src_width && height == src_height))
2634 surface->current_renderbuffer = NULL;
2635 return;
2638 /* Look if we've already got a renderbuffer of the correct dimensions */
2639 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
2641 if (entry->width == width && entry->height == height)
2643 renderbuffer = entry->id;
2644 surface->current_renderbuffer = entry;
2645 break;
2649 if (!renderbuffer)
2651 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
2652 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
2653 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
2654 surface->resource.format->glInternal, width, height);
2656 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
2657 entry->width = width;
2658 entry->height = height;
2659 entry->id = renderbuffer;
2660 list_add_head(&surface->renderbuffers, &entry->entry);
2662 surface->current_renderbuffer = entry;
2665 checkGLcall("set_compatible_renderbuffer");
2668 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
2670 const struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
2672 TRACE("surface %p.\n", surface);
2674 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
2676 ERR("Surface %p is not on a swapchain.\n", surface);
2677 return GL_NONE;
2680 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
2682 if (swapchain->render_to_fbo)
2684 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
2685 return GL_COLOR_ATTACHMENT0;
2687 TRACE("Returning GL_BACK\n");
2688 return GL_BACK;
2690 else if (surface == swapchain->front_buffer)
2692 TRACE("Returning GL_FRONT\n");
2693 return GL_FRONT;
2696 FIXME("Higher back buffer, returning GL_BACK\n");
2697 return GL_BACK;
2700 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
2701 void surface_add_dirty_rect(struct wined3d_surface *surface, const WINED3DBOX *dirty_rect)
2703 TRACE("surface %p, dirty_rect %p.\n", surface, dirty_rect);
2705 if (!(surface->flags & SFLAG_INSYSMEM) && (surface->flags & SFLAG_INTEXTURE))
2706 /* No partial locking for textures yet. */
2707 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2709 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2710 if (dirty_rect)
2712 surface->dirtyRect.left = min(surface->dirtyRect.left, dirty_rect->Left);
2713 surface->dirtyRect.top = min(surface->dirtyRect.top, dirty_rect->Top);
2714 surface->dirtyRect.right = max(surface->dirtyRect.right, dirty_rect->Right);
2715 surface->dirtyRect.bottom = max(surface->dirtyRect.bottom, dirty_rect->Bottom);
2717 else
2719 surface->dirtyRect.left = 0;
2720 surface->dirtyRect.top = 0;
2721 surface->dirtyRect.right = surface->resource.width;
2722 surface->dirtyRect.bottom = surface->resource.height;
2725 /* if the container is a texture then mark it dirty. */
2726 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
2728 TRACE("Passing to container.\n");
2729 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
2733 HRESULT surface_load(struct wined3d_surface *surface, BOOL srgb)
2735 DWORD flag = srgb ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2736 BOOL ck_changed;
2738 TRACE("surface %p, srgb %#x.\n", surface, srgb);
2740 if (surface->resource.pool == WINED3DPOOL_SCRATCH)
2742 ERR("Not supported on scratch surfaces.\n");
2743 return WINED3DERR_INVALIDCALL;
2746 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->CKeyFlags & WINEDDSD_CKSRCBLT);
2748 /* Reload if either the texture and sysmem have different ideas about the
2749 * color key, or the actual key values changed. */
2750 if (ck_changed || ((surface->CKeyFlags & WINEDDSD_CKSRCBLT)
2751 && (surface->glCKey.dwColorSpaceLowValue != surface->SrcBltCKey.dwColorSpaceLowValue
2752 || surface->glCKey.dwColorSpaceHighValue != surface->SrcBltCKey.dwColorSpaceHighValue)))
2754 TRACE("Reloading because of color keying\n");
2755 /* To perform the color key conversion we need a sysmem copy of
2756 * the surface. Make sure we have it. */
2758 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
2759 /* Make sure the texture is reloaded because of the color key change,
2760 * this kills performance though :( */
2761 /* TODO: This is not necessarily needed with hw palettized texture support. */
2762 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
2763 /* Switching color keying on / off may change the internal format. */
2764 if (ck_changed)
2765 surface_force_reload(surface);
2767 else if (!(surface->flags & flag))
2769 TRACE("Reloading because surface is dirty.\n");
2771 else
2773 TRACE("surface is already in texture\n");
2774 return WINED3D_OK;
2777 /* No partial locking for textures yet. */
2778 surface_load_location(surface, flag, NULL);
2779 surface_evict_sysmem(surface);
2781 return WINED3D_OK;
2784 /* See also float_16_to_32() in wined3d_private.h */
2785 static inline unsigned short float_32_to_16(const float *in)
2787 int exp = 0;
2788 float tmp = fabsf(*in);
2789 unsigned int mantissa;
2790 unsigned short ret;
2792 /* Deal with special numbers */
2793 if (*in == 0.0f)
2794 return 0x0000;
2795 if (isnan(*in))
2796 return 0x7c01;
2797 if (isinf(*in))
2798 return (*in < 0.0f ? 0xfc00 : 0x7c00);
2800 if (tmp < powf(2, 10))
2804 tmp = tmp * 2.0f;
2805 exp--;
2806 } while (tmp < powf(2, 10));
2808 else if (tmp >= powf(2, 11))
2812 tmp /= 2.0f;
2813 exp++;
2814 } while (tmp >= powf(2, 11));
2817 mantissa = (unsigned int)tmp;
2818 if (tmp - mantissa >= 0.5f)
2819 ++mantissa; /* Round to nearest, away from zero. */
2821 exp += 10; /* Normalize the mantissa. */
2822 exp += 15; /* Exponent is encoded with excess 15. */
2824 if (exp > 30) /* too big */
2826 ret = 0x7c00; /* INF */
2828 else if (exp <= 0)
2830 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
2831 while (exp <= 0)
2833 mantissa = mantissa >> 1;
2834 ++exp;
2836 ret = mantissa & 0x3ff;
2838 else
2840 ret = (exp << 10) | (mantissa & 0x3ff);
2843 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
2844 return ret;
2847 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
2849 ULONG refcount;
2851 TRACE("Surface %p, container %p of type %#x.\n",
2852 surface, surface->container.u.base, surface->container.type);
2854 switch (surface->container.type)
2856 case WINED3D_CONTAINER_TEXTURE:
2857 return wined3d_texture_incref(surface->container.u.texture);
2859 case WINED3D_CONTAINER_SWAPCHAIN:
2860 return wined3d_swapchain_incref(surface->container.u.swapchain);
2862 default:
2863 ERR("Unhandled container type %#x.\n", surface->container.type);
2864 case WINED3D_CONTAINER_NONE:
2865 break;
2868 refcount = InterlockedIncrement(&surface->resource.ref);
2869 TRACE("%p increasing refcount to %u.\n", surface, refcount);
2871 return refcount;
2874 /* Do not call while under the GL lock. */
2875 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
2877 ULONG refcount;
2879 TRACE("Surface %p, container %p of type %#x.\n",
2880 surface, surface->container.u.base, surface->container.type);
2882 switch (surface->container.type)
2884 case WINED3D_CONTAINER_TEXTURE:
2885 return wined3d_texture_decref(surface->container.u.texture);
2887 case WINED3D_CONTAINER_SWAPCHAIN:
2888 return wined3d_swapchain_decref(surface->container.u.swapchain);
2890 default:
2891 ERR("Unhandled container type %#x.\n", surface->container.type);
2892 case WINED3D_CONTAINER_NONE:
2893 break;
2896 refcount = InterlockedDecrement(&surface->resource.ref);
2897 TRACE("%p decreasing refcount to %u.\n", surface, refcount);
2899 if (!refcount)
2901 surface->surface_ops->surface_cleanup(surface);
2902 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
2904 TRACE("Destroyed surface %p.\n", surface);
2905 HeapFree(GetProcessHeap(), 0, surface);
2908 return refcount;
2911 DWORD CDECL wined3d_surface_set_priority(struct wined3d_surface *surface, DWORD priority)
2913 return resource_set_priority(&surface->resource, priority);
2916 DWORD CDECL wined3d_surface_get_priority(const struct wined3d_surface *surface)
2918 return resource_get_priority(&surface->resource);
2921 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
2923 TRACE("surface %p.\n", surface);
2925 surface->surface_ops->surface_preload(surface);
2928 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
2930 TRACE("surface %p.\n", surface);
2932 return surface->resource.parent;
2935 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
2937 TRACE("surface %p.\n", surface);
2939 return &surface->resource;
2942 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
2944 TRACE("surface %p, flags %#x.\n", surface, flags);
2946 switch (flags)
2948 case WINEDDGBS_CANBLT:
2949 case WINEDDGBS_ISBLTDONE:
2950 return WINED3D_OK;
2952 default:
2953 return WINED3DERR_INVALIDCALL;
2957 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
2959 TRACE("surface %p, flags %#x.\n", surface, flags);
2961 /* XXX: DDERR_INVALIDSURFACETYPE */
2963 switch (flags)
2965 case WINEDDGFS_CANFLIP:
2966 case WINEDDGFS_ISFLIPDONE:
2967 return WINED3D_OK;
2969 default:
2970 return WINED3DERR_INVALIDCALL;
2974 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
2976 TRACE("surface %p.\n", surface);
2978 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2979 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2982 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2984 TRACE("surface %p.\n", surface);
2986 /* So far we don't lose anything :) */
2987 surface->flags &= ~SFLAG_LOST;
2988 return WINED3D_OK;
2991 HRESULT CDECL wined3d_surface_set_palette(struct wined3d_surface *surface, struct wined3d_palette *palette)
2993 TRACE("surface %p, palette %p.\n", surface, palette);
2995 if (surface->palette == palette)
2997 TRACE("Nop palette change.\n");
2998 return WINED3D_OK;
3001 if (surface->palette && (surface->resource.usage & WINED3DUSAGE_RENDERTARGET))
3002 surface->palette->flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3004 surface->palette = palette;
3006 if (palette)
3008 if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET)
3009 palette->flags |= WINEDDPCAPS_PRIMARYSURFACE;
3011 surface->surface_ops->surface_realize_palette(surface);
3014 return WINED3D_OK;
3017 HRESULT CDECL wined3d_surface_set_color_key(struct wined3d_surface *surface,
3018 DWORD flags, const WINEDDCOLORKEY *color_key)
3020 TRACE("surface %p, flags %#x, color_key %p.\n", surface, flags, color_key);
3022 if (flags & WINEDDCKEY_COLORSPACE)
3024 FIXME(" colorkey value not supported (%08x) !\n", flags);
3025 return WINED3DERR_INVALIDCALL;
3028 /* Dirtify the surface, but only if a key was changed. */
3029 if (color_key)
3031 switch (flags & ~WINEDDCKEY_COLORSPACE)
3033 case WINEDDCKEY_DESTBLT:
3034 surface->DestBltCKey = *color_key;
3035 surface->CKeyFlags |= WINEDDSD_CKDESTBLT;
3036 break;
3038 case WINEDDCKEY_DESTOVERLAY:
3039 surface->DestOverlayCKey = *color_key;
3040 surface->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3041 break;
3043 case WINEDDCKEY_SRCOVERLAY:
3044 surface->SrcOverlayCKey = *color_key;
3045 surface->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3046 break;
3048 case WINEDDCKEY_SRCBLT:
3049 surface->SrcBltCKey = *color_key;
3050 surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3051 break;
3054 else
3056 switch (flags & ~WINEDDCKEY_COLORSPACE)
3058 case WINEDDCKEY_DESTBLT:
3059 surface->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3060 break;
3062 case WINEDDCKEY_DESTOVERLAY:
3063 surface->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3064 break;
3066 case WINEDDCKEY_SRCOVERLAY:
3067 surface->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3068 break;
3070 case WINEDDCKEY_SRCBLT:
3071 surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3072 break;
3076 return WINED3D_OK;
3079 struct wined3d_palette * CDECL wined3d_surface_get_palette(const struct wined3d_surface *surface)
3081 TRACE("surface %p.\n", surface);
3083 return surface->palette;
3086 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
3088 const struct wined3d_format *format = surface->resource.format;
3089 DWORD pitch;
3091 TRACE("surface %p.\n", surface);
3093 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3095 /* Since compressed formats are block based, pitch means the amount of
3096 * bytes to the next row of block rather than the next row of pixels. */
3097 UINT row_block_count = (surface->resource.width + format->block_width - 1) / format->block_width;
3098 pitch = row_block_count * format->block_byte_count;
3100 else
3102 unsigned char alignment = surface->resource.device->surface_alignment;
3103 pitch = surface->resource.format->byte_count * surface->resource.width; /* Bytes / row */
3104 pitch = (pitch + alignment - 1) & ~(alignment - 1);
3107 TRACE("Returning %u.\n", pitch);
3109 return pitch;
3112 HRESULT CDECL wined3d_surface_set_mem(struct wined3d_surface *surface, void *mem)
3114 TRACE("surface %p, mem %p.\n", surface, mem);
3116 if (surface->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
3118 WARN("Surface is locked or the DC is in use.\n");
3119 return WINED3DERR_INVALIDCALL;
3122 return surface->surface_ops->surface_set_mem(surface, mem);
3125 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
3127 LONG w, h;
3129 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
3131 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3133 WARN("Not an overlay surface.\n");
3134 return WINEDDERR_NOTAOVERLAYSURFACE;
3137 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
3138 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
3139 surface->overlay_destrect.left = x;
3140 surface->overlay_destrect.top = y;
3141 surface->overlay_destrect.right = x + w;
3142 surface->overlay_destrect.bottom = y + h;
3144 surface->surface_ops->surface_draw_overlay(surface);
3146 return WINED3D_OK;
3149 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
3151 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
3153 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3155 TRACE("Not an overlay surface.\n");
3156 return WINEDDERR_NOTAOVERLAYSURFACE;
3159 if (!surface->overlay_dest)
3161 TRACE("Overlay not visible.\n");
3162 *x = 0;
3163 *y = 0;
3164 return WINEDDERR_OVERLAYNOTVISIBLE;
3167 *x = surface->overlay_destrect.left;
3168 *y = surface->overlay_destrect.top;
3170 TRACE("Returning position %d, %d.\n", *x, *y);
3172 return WINED3D_OK;
3175 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
3176 DWORD flags, struct wined3d_surface *ref)
3178 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
3180 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3182 TRACE("Not an overlay surface.\n");
3183 return WINEDDERR_NOTAOVERLAYSURFACE;
3186 return WINED3D_OK;
3189 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
3190 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
3192 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
3193 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
3195 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
3197 WARN("Not an overlay surface.\n");
3198 return WINEDDERR_NOTAOVERLAYSURFACE;
3200 else if (!dst_surface)
3202 WARN("Dest surface is NULL.\n");
3203 return WINED3DERR_INVALIDCALL;
3206 if (src_rect)
3208 surface->overlay_srcrect = *src_rect;
3210 else
3212 surface->overlay_srcrect.left = 0;
3213 surface->overlay_srcrect.top = 0;
3214 surface->overlay_srcrect.right = surface->resource.width;
3215 surface->overlay_srcrect.bottom = surface->resource.height;
3218 if (dst_rect)
3220 surface->overlay_destrect = *dst_rect;
3222 else
3224 surface->overlay_destrect.left = 0;
3225 surface->overlay_destrect.top = 0;
3226 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
3227 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
3230 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
3232 list_remove(&surface->overlay_entry);
3235 if (flags & WINEDDOVER_SHOW)
3237 if (surface->overlay_dest != dst_surface)
3239 surface->overlay_dest = dst_surface;
3240 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
3243 else if (flags & WINEDDOVER_HIDE)
3245 /* tests show that the rectangles are erased on hide */
3246 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
3247 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
3248 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
3249 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
3250 surface->overlay_dest = NULL;
3253 surface->surface_ops->surface_draw_overlay(surface);
3255 return WINED3D_OK;
3258 HRESULT CDECL wined3d_surface_set_clipper(struct wined3d_surface *surface, struct wined3d_clipper *clipper)
3260 TRACE("surface %p, clipper %p.\n", surface, clipper);
3262 surface->clipper = clipper;
3264 return WINED3D_OK;
3267 struct wined3d_clipper * CDECL wined3d_surface_get_clipper(const struct wined3d_surface *surface)
3269 TRACE("surface %p.\n", surface);
3271 return surface->clipper;
3274 HRESULT CDECL wined3d_surface_set_format(struct wined3d_surface *surface, enum wined3d_format_id format_id)
3276 const struct wined3d_format *format = wined3d_get_format(&surface->resource.device->adapter->gl_info, format_id);
3278 TRACE("surface %p, format %s.\n", surface, debug_d3dformat(format_id));
3280 if (surface->resource.format->id != WINED3DFMT_UNKNOWN)
3282 FIXME("The format of the surface must be WINED3DFORMAT_UNKNOWN.\n");
3283 return WINED3DERR_INVALIDCALL;
3286 surface->resource.size = wined3d_format_calculate_size(format, surface->resource.device->surface_alignment,
3287 surface->pow2Width, surface->pow2Height);
3288 surface->flags |= (WINED3DFMT_D16_LOCKABLE == format_id) ? SFLAG_LOCKABLE : 0;
3289 surface->resource.format = format;
3291 TRACE("size %u, byte_count %u\n", surface->resource.size, format->byte_count);
3292 TRACE("glFormat %#x, glInternal %#x, glType %#x.\n",
3293 format->glFormat, format->glInternal, format->glType);
3295 return WINED3D_OK;
3298 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
3299 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3301 unsigned short *dst_s;
3302 const float *src_f;
3303 unsigned int x, y;
3305 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3307 for (y = 0; y < h; ++y)
3309 src_f = (const float *)(src + y * pitch_in);
3310 dst_s = (unsigned short *) (dst + y * pitch_out);
3311 for (x = 0; x < w; ++x)
3313 dst_s[x] = float_32_to_16(src_f + x);
3318 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
3319 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3321 static const unsigned char convert_5to8[] =
3323 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
3324 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
3325 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
3326 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
3328 static const unsigned char convert_6to8[] =
3330 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
3331 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
3332 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
3333 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
3334 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
3335 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
3336 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
3337 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
3339 unsigned int x, y;
3341 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3343 for (y = 0; y < h; ++y)
3345 const WORD *src_line = (const WORD *)(src + y * pitch_in);
3346 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3347 for (x = 0; x < w; ++x)
3349 WORD pixel = src_line[x];
3350 dst_line[x] = 0xff000000
3351 | convert_5to8[(pixel & 0xf800) >> 11] << 16
3352 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
3353 | convert_5to8[(pixel & 0x001f)];
3358 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
3359 * in both cases we're just setting the X / Alpha channel to 0xff. */
3360 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
3361 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3363 unsigned int x, y;
3365 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3367 for (y = 0; y < h; ++y)
3369 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
3370 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3372 for (x = 0; x < w; ++x)
3374 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
3379 static inline BYTE cliptobyte(int x)
3381 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
3384 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
3385 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3387 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3388 unsigned int x, y;
3390 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
3392 for (y = 0; y < h; ++y)
3394 const BYTE *src_line = src + y * pitch_in;
3395 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
3396 for (x = 0; x < w; ++x)
3398 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3399 * C = Y - 16; D = U - 128; E = V - 128;
3400 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3401 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3402 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3403 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3404 * U and V are shared between the pixels. */
3405 if (!(x & 1)) /* For every even pixel, read new U and V. */
3407 d = (int) src_line[1] - 128;
3408 e = (int) src_line[3] - 128;
3409 r2 = 409 * e + 128;
3410 g2 = - 100 * d - 208 * e + 128;
3411 b2 = 516 * d + 128;
3413 c2 = 298 * ((int) src_line[0] - 16);
3414 dst_line[x] = 0xff000000
3415 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
3416 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
3417 | cliptobyte((c2 + b2) >> 8); /* blue */
3418 /* Scale RGB values to 0..255 range,
3419 * then clip them if still not in range (may be negative),
3420 * then shift them within DWORD if necessary. */
3421 src_line += 2;
3426 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
3427 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
3429 unsigned int x, y;
3430 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
3432 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
3434 for (y = 0; y < h; ++y)
3436 const BYTE *src_line = src + y * pitch_in;
3437 WORD *dst_line = (WORD *)(dst + y * pitch_out);
3438 for (x = 0; x < w; ++x)
3440 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
3441 * C = Y - 16; D = U - 128; E = V - 128;
3442 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
3443 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
3444 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
3445 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
3446 * U and V are shared between the pixels. */
3447 if (!(x & 1)) /* For every even pixel, read new U and V. */
3449 d = (int) src_line[1] - 128;
3450 e = (int) src_line[3] - 128;
3451 r2 = 409 * e + 128;
3452 g2 = - 100 * d - 208 * e + 128;
3453 b2 = 516 * d + 128;
3455 c2 = 298 * ((int) src_line[0] - 16);
3456 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
3457 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
3458 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
3459 /* Scale RGB values to 0..255 range,
3460 * then clip them if still not in range (may be negative),
3461 * then shift them within DWORD if necessary. */
3462 src_line += 2;
3467 struct d3dfmt_convertor_desc
3469 enum wined3d_format_id from, to;
3470 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
3473 static const struct d3dfmt_convertor_desc convertors[] =
3475 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
3476 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
3477 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3478 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
3479 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
3480 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
3483 static inline const struct d3dfmt_convertor_desc *find_convertor(enum wined3d_format_id from,
3484 enum wined3d_format_id to)
3486 unsigned int i;
3488 for (i = 0; i < (sizeof(convertors) / sizeof(*convertors)); ++i)
3490 if (convertors[i].from == from && convertors[i].to == to)
3491 return &convertors[i];
3494 return NULL;
3497 /*****************************************************************************
3498 * surface_convert_format
3500 * Creates a duplicate of a surface in a different format. Is used by Blt to
3501 * blit between surfaces with different formats.
3503 * Parameters
3504 * source: Source surface
3505 * fmt: Requested destination format
3507 *****************************************************************************/
3508 static struct wined3d_surface *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
3510 const struct d3dfmt_convertor_desc *conv;
3511 WINED3DLOCKED_RECT lock_src, lock_dst;
3512 struct wined3d_surface *ret = NULL;
3513 HRESULT hr;
3515 conv = find_convertor(source->resource.format->id, to_fmt);
3516 if (!conv)
3518 FIXME("Cannot find a conversion function from format %s to %s.\n",
3519 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
3520 return NULL;
3523 wined3d_surface_create(source->resource.device, source->resource.width,
3524 source->resource.height, to_fmt, TRUE /* lockable */, TRUE /* discard */, 0 /* level */,
3525 0 /* usage */, WINED3DPOOL_SCRATCH, WINED3DMULTISAMPLE_NONE /* TODO: Multisampled conversion */,
3526 0 /* MultiSampleQuality */, source->surface_type, NULL /* parent */, &wined3d_null_parent_ops, &ret);
3527 if (!ret)
3529 ERR("Failed to create a destination surface for conversion.\n");
3530 return NULL;
3533 memset(&lock_src, 0, sizeof(lock_src));
3534 memset(&lock_dst, 0, sizeof(lock_dst));
3536 hr = wined3d_surface_map(source, &lock_src, NULL, WINED3DLOCK_READONLY);
3537 if (FAILED(hr))
3539 ERR("Failed to lock the source surface.\n");
3540 wined3d_surface_decref(ret);
3541 return NULL;
3543 hr = wined3d_surface_map(ret, &lock_dst, NULL, WINED3DLOCK_READONLY);
3544 if (FAILED(hr))
3546 ERR("Failed to lock the destination surface.\n");
3547 wined3d_surface_unmap(source);
3548 wined3d_surface_decref(ret);
3549 return NULL;
3552 conv->convert(lock_src.pBits, lock_dst.pBits, lock_src.Pitch, lock_dst.Pitch,
3553 source->resource.width, source->resource.height);
3555 wined3d_surface_unmap(ret);
3556 wined3d_surface_unmap(source);
3558 return ret;
3561 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
3562 unsigned int bpp, UINT pitch, DWORD color)
3564 BYTE *first;
3565 int x, y;
3567 /* Do first row */
3569 #define COLORFILL_ROW(type) \
3570 do { \
3571 type *d = (type *)buf; \
3572 for (x = 0; x < width; ++x) \
3573 d[x] = (type)color; \
3574 } while(0)
3576 switch (bpp)
3578 case 1:
3579 COLORFILL_ROW(BYTE);
3580 break;
3582 case 2:
3583 COLORFILL_ROW(WORD);
3584 break;
3586 case 3:
3588 BYTE *d = buf;
3589 for (x = 0; x < width; ++x, d += 3)
3591 d[0] = (color ) & 0xFF;
3592 d[1] = (color >> 8) & 0xFF;
3593 d[2] = (color >> 16) & 0xFF;
3595 break;
3597 case 4:
3598 COLORFILL_ROW(DWORD);
3599 break;
3601 default:
3602 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
3603 return WINED3DERR_NOTAVAILABLE;
3606 #undef COLORFILL_ROW
3608 /* Now copy first row. */
3609 first = buf;
3610 for (y = 1; y < height; ++y)
3612 buf += pitch;
3613 memcpy(buf, first, width * bpp);
3616 return WINED3D_OK;
3619 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
3621 TRACE("surface %p.\n", surface);
3623 if (!(surface->flags & SFLAG_LOCKED))
3625 WARN("Trying to unmap unmapped surface.\n");
3626 return WINEDDERR_NOTLOCKED;
3628 surface->flags &= ~SFLAG_LOCKED;
3630 surface->surface_ops->surface_unmap(surface);
3632 return WINED3D_OK;
3635 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
3636 WINED3DLOCKED_RECT *locked_rect, const RECT *rect, DWORD flags)
3638 TRACE("surface %p, locked_rect %p, rect %s, flags %#x.\n",
3639 surface, locked_rect, wine_dbgstr_rect(rect), flags);
3641 if (surface->flags & SFLAG_LOCKED)
3643 WARN("Surface is already mapped.\n");
3644 return WINED3DERR_INVALIDCALL;
3646 surface->flags |= SFLAG_LOCKED;
3648 if (!(surface->flags & SFLAG_LOCKABLE))
3649 WARN("Trying to lock unlockable surface.\n");
3651 surface->surface_ops->surface_map(surface, rect, flags);
3653 locked_rect->Pitch = wined3d_surface_get_pitch(surface);
3655 if (!rect)
3657 locked_rect->pBits = surface->resource.allocatedMemory;
3658 surface->lockedRect.left = 0;
3659 surface->lockedRect.top = 0;
3660 surface->lockedRect.right = surface->resource.width;
3661 surface->lockedRect.bottom = surface->resource.height;
3663 else
3665 const struct wined3d_format *format = surface->resource.format;
3667 if ((format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_COMPRESSED)
3669 /* Compressed textures are block based, so calculate the offset of
3670 * the block that contains the top-left pixel of the locked rectangle. */
3671 locked_rect->pBits = surface->resource.allocatedMemory
3672 + ((rect->top / format->block_height) * locked_rect->Pitch)
3673 + ((rect->left / format->block_width) * format->block_byte_count);
3675 else
3677 locked_rect->pBits = surface->resource.allocatedMemory
3678 + (locked_rect->Pitch * rect->top)
3679 + (rect->left * format->byte_count);
3681 surface->lockedRect.left = rect->left;
3682 surface->lockedRect.top = rect->top;
3683 surface->lockedRect.right = rect->right;
3684 surface->lockedRect.bottom = rect->bottom;
3687 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
3688 TRACE("Returning memory %p, pitch %u.\n", locked_rect->pBits, locked_rect->Pitch);
3690 return WINED3D_OK;
3693 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
3695 HRESULT hr;
3697 TRACE("surface %p, dc %p.\n", surface, dc);
3699 if (surface->flags & SFLAG_USERPTR)
3701 ERR("Not supported on surfaces with application-provided memory.\n");
3702 return WINEDDERR_NODC;
3705 /* Give more detailed info for ddraw. */
3706 if (surface->flags & SFLAG_DCINUSE)
3707 return WINEDDERR_DCALREADYCREATED;
3709 /* Can't GetDC if the surface is locked. */
3710 if (surface->flags & SFLAG_LOCKED)
3711 return WINED3DERR_INVALIDCALL;
3713 hr = surface->surface_ops->surface_getdc(surface);
3714 if (FAILED(hr))
3715 return hr;
3717 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3718 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3720 /* GetDC on palettized formats is unsupported in D3D9, and the method
3721 * is missing in D3D8, so this should only be used for DX <=7
3722 * surfaces (with non-device palettes). */
3723 const PALETTEENTRY *pal = NULL;
3725 if (surface->palette)
3727 pal = surface->palette->palents;
3729 else
3731 struct wined3d_swapchain *swapchain = surface->resource.device->swapchains[0];
3732 struct wined3d_surface *dds_primary = swapchain->front_buffer;
3734 if (dds_primary && dds_primary->palette)
3735 pal = dds_primary->palette->palents;
3738 if (pal)
3740 RGBQUAD col[256];
3741 unsigned int i;
3743 for (i = 0; i < 256; ++i)
3745 col[i].rgbRed = pal[i].peRed;
3746 col[i].rgbGreen = pal[i].peGreen;
3747 col[i].rgbBlue = pal[i].peBlue;
3748 col[i].rgbReserved = 0;
3750 SetDIBColorTable(surface->hDC, 0, 256, col);
3754 surface->flags |= SFLAG_DCINUSE;
3756 *dc = surface->hDC;
3757 TRACE("Returning dc %p.\n", *dc);
3759 return WINED3D_OK;
3762 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
3764 TRACE("surface %p, dc %p.\n", surface, dc);
3766 if (!(surface->flags & SFLAG_DCINUSE))
3767 return WINEDDERR_NODC;
3769 if (surface->hDC != dc)
3771 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
3772 dc, surface->hDC);
3773 return WINEDDERR_NODC;
3776 if ((surface->flags & SFLAG_PBO) && surface->resource.allocatedMemory)
3778 /* Copy the contents of the DIB over to the PBO. */
3779 memcpy(surface->resource.allocatedMemory, surface->dib.bitmap_data, surface->dib.bitmap_size);
3782 /* We locked first, so unlock now. */
3783 wined3d_surface_unmap(surface);
3785 surface->flags &= ~SFLAG_DCINUSE;
3787 return WINED3D_OK;
3790 HRESULT CDECL wined3d_surface_flip(struct wined3d_surface *surface, struct wined3d_surface *override, DWORD flags)
3792 struct wined3d_swapchain *swapchain;
3793 HRESULT hr;
3795 TRACE("surface %p, override %p, flags %#x.\n", surface, override, flags);
3797 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
3799 ERR("Flipped surface is not on a swapchain.\n");
3800 return WINEDDERR_NOTFLIPPABLE;
3802 swapchain = surface->container.u.swapchain;
3804 hr = surface->surface_ops->surface_flip(surface, override);
3805 if (FAILED(hr))
3806 return hr;
3808 /* Just overwrite the swapchain presentation interval. This is ok because
3809 * only ddraw apps can call Flip, and only d3d8 and d3d9 applications
3810 * specify the presentation interval. */
3811 if (!(flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)))
3812 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
3813 else if (flags & WINEDDFLIP_NOVSYNC)
3814 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3815 else if (flags & WINEDDFLIP_INTERVAL2)
3816 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
3817 else if (flags & WINEDDFLIP_INTERVAL3)
3818 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
3819 else
3820 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3822 return wined3d_swapchain_present(swapchain, NULL, NULL, swapchain->win_handle, NULL, 0);
3825 /* Do not call while under the GL lock. */
3826 void surface_internal_preload(struct wined3d_surface *surface, enum WINED3DSRGB srgb)
3828 struct wined3d_device *device = surface->resource.device;
3830 TRACE("iface %p, srgb %#x.\n", surface, srgb);
3832 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
3834 struct wined3d_texture *texture = surface->container.u.texture;
3836 TRACE("Passing to container (%p).\n", texture);
3837 texture->texture_ops->texture_preload(texture, srgb);
3839 else
3841 struct wined3d_context *context = NULL;
3843 TRACE("(%p) : About to load surface\n", surface);
3845 if (!device->isInDraw) context = context_acquire(device, NULL);
3847 if (surface->resource.format->id == WINED3DFMT_P8_UINT
3848 || surface->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
3850 if (palette9_changed(surface))
3852 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
3853 /* TODO: This is not necessarily needed with hw palettized texture support */
3854 surface_load_location(surface, SFLAG_INSYSMEM, NULL);
3855 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
3856 surface_modify_location(surface, SFLAG_INTEXTURE, FALSE);
3860 surface_load(surface, srgb == SRGB_SRGB ? TRUE : FALSE);
3862 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
3864 /* Tell opengl to try and keep this texture in video ram (well mostly) */
3865 GLclampf tmp;
3866 tmp = 0.9f;
3867 ENTER_GL();
3868 glPrioritizeTextures(1, &surface->texture_name, &tmp);
3869 LEAVE_GL();
3872 if (context) context_release(context);
3876 BOOL surface_init_sysmem(struct wined3d_surface *surface)
3878 if (!surface->resource.allocatedMemory)
3880 surface->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
3881 surface->resource.size + RESOURCE_ALIGNMENT);
3882 if (!surface->resource.heapMemory)
3884 ERR("Out of memory\n");
3885 return FALSE;
3887 surface->resource.allocatedMemory =
3888 (BYTE *)(((ULONG_PTR)surface->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
3890 else
3892 memset(surface->resource.allocatedMemory, 0, surface->resource.size);
3895 surface_modify_location(surface, SFLAG_INSYSMEM, TRUE);
3897 return TRUE;
3900 /* Read the framebuffer back into the surface */
3901 static void read_from_framebuffer(struct wined3d_surface *surface, const RECT *rect, void *dest, UINT pitch)
3903 struct wined3d_device *device = surface->resource.device;
3904 const struct wined3d_gl_info *gl_info;
3905 struct wined3d_context *context;
3906 BYTE *mem;
3907 GLint fmt;
3908 GLint type;
3909 BYTE *row, *top, *bottom;
3910 int i;
3911 BOOL bpp;
3912 RECT local_rect;
3913 BOOL srcIsUpsideDown;
3914 GLint rowLen = 0;
3915 GLint skipPix = 0;
3916 GLint skipRow = 0;
3918 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
3919 static BOOL warned = FALSE;
3920 if(!warned) {
3921 ERR("The application tries to lock the render target, but render target locking is disabled\n");
3922 warned = TRUE;
3924 return;
3927 context = context_acquire(device, surface);
3928 context_apply_blit_state(context, device);
3929 gl_info = context->gl_info;
3931 ENTER_GL();
3933 /* Select the correct read buffer, and give some debug output.
3934 * There is no need to keep track of the current read buffer or reset it, every part of the code
3935 * that reads sets the read buffer as desired.
3937 if (surface_is_offscreen(surface))
3939 /* Mapping the primary render target which is not on a swapchain.
3940 * Read from the back buffer. */
3941 TRACE("Mapping offscreen render target.\n");
3942 glReadBuffer(device->offscreenBuffer);
3943 srcIsUpsideDown = TRUE;
3945 else
3947 /* Onscreen surfaces are always part of a swapchain */
3948 GLenum buffer = surface_get_gl_buffer(surface);
3949 TRACE("Mapping %#x buffer.\n", buffer);
3950 glReadBuffer(buffer);
3951 checkGLcall("glReadBuffer");
3952 srcIsUpsideDown = FALSE;
3955 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
3956 if (!rect)
3958 local_rect.left = 0;
3959 local_rect.top = 0;
3960 local_rect.right = surface->resource.width;
3961 local_rect.bottom = surface->resource.height;
3963 else
3965 local_rect = *rect;
3967 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
3969 switch (surface->resource.format->id)
3971 case WINED3DFMT_P8_UINT:
3973 if (primary_render_target_is_p8(device))
3975 /* In case of P8 render targets the index is stored in the alpha component */
3976 fmt = GL_ALPHA;
3977 type = GL_UNSIGNED_BYTE;
3978 mem = dest;
3979 bpp = surface->resource.format->byte_count;
3981 else
3983 /* GL can't return palettized data, so read ARGB pixels into a
3984 * separate block of memory and convert them into palettized format
3985 * in software. Slow, but if the app means to use palettized render
3986 * targets and locks it...
3988 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
3989 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
3990 * for the color channels when palettizing the colors.
3992 fmt = GL_RGB;
3993 type = GL_UNSIGNED_BYTE;
3994 pitch *= 3;
3995 mem = HeapAlloc(GetProcessHeap(), 0, surface->resource.size * 3);
3996 if (!mem)
3998 ERR("Out of memory\n");
3999 LEAVE_GL();
4000 return;
4002 bpp = surface->resource.format->byte_count * 3;
4005 break;
4007 default:
4008 mem = dest;
4009 fmt = surface->resource.format->glFormat;
4010 type = surface->resource.format->glType;
4011 bpp = surface->resource.format->byte_count;
4014 if (surface->flags & SFLAG_PBO)
4016 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, surface->pbo));
4017 checkGLcall("glBindBufferARB");
4018 if (mem)
4020 ERR("mem not null for pbo -- unexpected\n");
4021 mem = NULL;
4025 /* Save old pixel store pack state */
4026 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
4027 checkGLcall("glGetIntegerv");
4028 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
4029 checkGLcall("glGetIntegerv");
4030 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
4031 checkGLcall("glGetIntegerv");
4033 /* Setup pixel store pack state -- to glReadPixels into the correct place */
4034 glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
4035 checkGLcall("glPixelStorei");
4036 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
4037 checkGLcall("glPixelStorei");
4038 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
4039 checkGLcall("glPixelStorei");
4041 glReadPixels(local_rect.left, !srcIsUpsideDown ? (surface->resource.height - local_rect.bottom) : local_rect.top,
4042 local_rect.right - local_rect.left,
4043 local_rect.bottom - local_rect.top,
4044 fmt, type, mem);
4045 checkGLcall("glReadPixels");
4047 /* Reset previous pixel store pack state */
4048 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
4049 checkGLcall("glPixelStorei");
4050 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
4051 checkGLcall("glPixelStorei");
4052 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
4053 checkGLcall("glPixelStorei");
4055 if (surface->flags & SFLAG_PBO)
4057 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4058 checkGLcall("glBindBufferARB");
4060 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
4061 * to get a pointer to it and perform the flipping in software. This is a lot
4062 * faster than calling glReadPixels for each line. In case we want more speed
4063 * we should rerender it flipped in a FBO and read the data back from the FBO. */
4064 if (!srcIsUpsideDown)
4066 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4067 checkGLcall("glBindBufferARB");
4069 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
4070 checkGLcall("glMapBufferARB");
4074 /* TODO: Merge this with the palettization loop below for P8 targets */
4075 if(!srcIsUpsideDown) {
4076 UINT len, off;
4077 /* glReadPixels returns the image upside down, and there is no way to prevent this.
4078 Flip the lines in software */
4079 len = (local_rect.right - local_rect.left) * bpp;
4080 off = local_rect.left * bpp;
4082 row = HeapAlloc(GetProcessHeap(), 0, len);
4083 if(!row) {
4084 ERR("Out of memory\n");
4085 if (surface->resource.format->id == WINED3DFMT_P8_UINT)
4086 HeapFree(GetProcessHeap(), 0, mem);
4087 LEAVE_GL();
4088 return;
4091 top = mem + pitch * local_rect.top;
4092 bottom = mem + pitch * (local_rect.bottom - 1);
4093 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
4094 memcpy(row, top + off, len);
4095 memcpy(top + off, bottom + off, len);
4096 memcpy(bottom + off, row, len);
4097 top += pitch;
4098 bottom -= pitch;
4100 HeapFree(GetProcessHeap(), 0, row);
4102 /* Unmap the temp PBO buffer */
4103 if (surface->flags & SFLAG_PBO)
4105 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
4106 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4110 LEAVE_GL();
4111 context_release(context);
4113 /* For P8 textures we need to perform an inverse palette lookup. This is
4114 * done by searching for a palette index which matches the RGB value.
4115 * Note this isn't guaranteed to work when there are multiple entries for
4116 * the same color but we have no choice. In case of P8 render targets,
4117 * the index is stored in the alpha component so no conversion is needed. */
4118 if (surface->resource.format->id == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
4120 const PALETTEENTRY *pal = NULL;
4121 DWORD width = pitch / 3;
4122 int x, y, c;
4124 if (surface->palette)
4126 pal = surface->palette->palents;
4128 else
4130 ERR("Palette is missing, cannot perform inverse palette lookup\n");
4131 HeapFree(GetProcessHeap(), 0, mem);
4132 return;
4135 for(y = local_rect.top; y < local_rect.bottom; y++) {
4136 for(x = local_rect.left; x < local_rect.right; x++) {
4137 /* start lines pixels */
4138 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
4139 const BYTE *green = blue + 1;
4140 const BYTE *red = green + 1;
4142 for(c = 0; c < 256; c++) {
4143 if(*red == pal[c].peRed &&
4144 *green == pal[c].peGreen &&
4145 *blue == pal[c].peBlue)
4147 *((BYTE *) dest + y * width + x) = c;
4148 break;
4153 HeapFree(GetProcessHeap(), 0, mem);
4157 /* Read the framebuffer contents into a texture */
4158 static void read_from_framebuffer_texture(struct wined3d_surface *surface, BOOL srgb)
4160 struct wined3d_device *device = surface->resource.device;
4161 const struct wined3d_gl_info *gl_info;
4162 struct wined3d_context *context;
4164 if (!surface_is_offscreen(surface))
4166 /* We would need to flip onscreen surfaces, but there's no efficient
4167 * way to do that here. It makes more sense for the caller to
4168 * explicitly go through sysmem. */
4169 ERR("Not supported for onscreen targets.\n");
4170 return;
4173 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
4174 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
4175 * states in the stateblock, and no driver was found yet that had bugs in that regard.
4177 context = context_acquire(device, surface);
4178 gl_info = context->gl_info;
4179 device_invalidate_state(device, STATE_FRAMEBUFFER);
4181 surface_prepare_texture(surface, gl_info, srgb);
4182 surface_bind_and_dirtify(surface, gl_info, srgb);
4184 TRACE("Reading back offscreen render target %p.\n", surface);
4186 ENTER_GL();
4188 glReadBuffer(device->offscreenBuffer);
4189 checkGLcall("glReadBuffer");
4191 glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
4192 0, 0, 0, 0, surface->resource.width, surface->resource.height);
4193 checkGLcall("glCopyTexSubImage2D");
4195 LEAVE_GL();
4197 context_release(context);
4200 /* Context activation is done by the caller. */
4201 static void surface_prepare_texture_internal(struct wined3d_surface *surface,
4202 const struct wined3d_gl_info *gl_info, BOOL srgb)
4204 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4205 CONVERT_TYPES convert;
4206 struct wined3d_format format;
4208 if (surface->flags & alloc_flag) return;
4210 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &convert);
4211 if (convert != NO_CONVERSION || format.convert) surface->flags |= SFLAG_CONVERTED;
4212 else surface->flags &= ~SFLAG_CONVERTED;
4214 surface_bind_and_dirtify(surface, gl_info, srgb);
4215 surface_allocate_surface(surface, gl_info, &format, srgb);
4216 surface->flags |= alloc_flag;
4219 /* Context activation is done by the caller. */
4220 void surface_prepare_texture(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
4222 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
4224 struct wined3d_texture *texture = surface->container.u.texture;
4225 UINT sub_count = texture->level_count * texture->layer_count;
4226 UINT i;
4228 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
4230 for (i = 0; i < sub_count; ++i)
4232 struct wined3d_surface *s = surface_from_resource(texture->sub_resources[i]);
4233 surface_prepare_texture_internal(s, gl_info, srgb);
4236 return;
4239 surface_prepare_texture_internal(surface, gl_info, srgb);
4242 static void flush_to_framebuffer_drawpixels(struct wined3d_surface *surface,
4243 const RECT *rect, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem)
4245 struct wined3d_device *device = surface->resource.device;
4246 UINT pitch = wined3d_surface_get_pitch(surface);
4247 const struct wined3d_gl_info *gl_info;
4248 struct wined3d_context *context;
4249 RECT local_rect;
4250 UINT w, h;
4252 surface_get_rect(surface, rect, &local_rect);
4254 mem += local_rect.top * pitch + local_rect.left * bpp;
4255 w = local_rect.right - local_rect.left;
4256 h = local_rect.bottom - local_rect.top;
4258 /* Activate the correct context for the render target */
4259 context = context_acquire(device, surface);
4260 context_apply_blit_state(context, device);
4261 gl_info = context->gl_info;
4263 ENTER_GL();
4265 if (!surface_is_offscreen(surface))
4267 GLenum buffer = surface_get_gl_buffer(surface);
4268 TRACE("Unlocking %#x buffer.\n", buffer);
4269 context_set_draw_buffer(context, buffer);
4271 surface_translate_drawable_coords(surface, context->win_handle, &local_rect);
4272 glPixelZoom(1.0f, -1.0f);
4274 else
4276 /* Primary offscreen render target */
4277 TRACE("Offscreen render target.\n");
4278 context_set_draw_buffer(context, device->offscreenBuffer);
4280 glPixelZoom(1.0f, 1.0f);
4283 glRasterPos3i(local_rect.left, local_rect.top, 1);
4284 checkGLcall("glRasterPos3i");
4286 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
4287 glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->resource.width);
4289 if (surface->flags & SFLAG_PBO)
4291 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
4292 checkGLcall("glBindBufferARB");
4295 glDrawPixels(w, h, fmt, type, mem);
4296 checkGLcall("glDrawPixels");
4298 if (surface->flags & SFLAG_PBO)
4300 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4301 checkGLcall("glBindBufferARB");
4304 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4305 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)");
4307 LEAVE_GL();
4309 if (wined3d_settings.strict_draw_ordering
4310 || (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
4311 && surface->container.u.swapchain->front_buffer == surface))
4312 wglFlush();
4314 context_release(context);
4317 HRESULT d3dfmt_get_conv(const struct wined3d_surface *surface, BOOL need_alpha_ck,
4318 BOOL use_texturing, struct wined3d_format *format, CONVERT_TYPES *convert)
4320 BOOL colorkey_active = need_alpha_ck && (surface->CKeyFlags & WINEDDSD_CKSRCBLT);
4321 const struct wined3d_device *device = surface->resource.device;
4322 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4323 BOOL blit_supported = FALSE;
4325 /* Copy the default values from the surface. Below we might perform fixups */
4326 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
4327 *format = *surface->resource.format;
4328 *convert = NO_CONVERSION;
4330 /* Ok, now look if we have to do any conversion */
4331 switch (surface->resource.format->id)
4333 case WINED3DFMT_P8_UINT:
4334 /* Below the call to blit_supported is disabled for Wine 1.2
4335 * because the function isn't operating correctly yet. At the
4336 * moment 8-bit blits are handled in software and if certain GL
4337 * extensions are around, surface conversion is performed at
4338 * upload time. The blit_supported call recognizes it as a
4339 * destination fixup. This type of upload 'fixup' and 8-bit to
4340 * 8-bit blits need to be handled by the blit_shader.
4341 * TODO: get rid of this #if 0. */
4342 #if 0
4343 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4344 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format,
4345 &rect, surface->resource.usage, surface->resource.pool, surface->resource.format);
4346 #endif
4347 blit_supported = gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM];
4349 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
4350 * texturing. Further also use conversion in case of color keying.
4351 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
4352 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
4353 * conflicts with this.
4355 if (!((blit_supported && device->fb.render_targets && surface == device->fb.render_targets[0]))
4356 || colorkey_active || !use_texturing)
4358 format->glFormat = GL_RGBA;
4359 format->glInternal = GL_RGBA;
4360 format->glType = GL_UNSIGNED_BYTE;
4361 format->conv_byte_count = 4;
4362 if (colorkey_active)
4363 *convert = CONVERT_PALETTED_CK;
4364 else
4365 *convert = CONVERT_PALETTED;
4367 break;
4369 case WINED3DFMT_B2G3R3_UNORM:
4370 /* **********************
4371 GL_UNSIGNED_BYTE_3_3_2
4372 ********************** */
4373 if (colorkey_active) {
4374 /* This texture format will never be used.. So do not care about color keying
4375 up until the point in time it will be needed :-) */
4376 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
4378 break;
4380 case WINED3DFMT_B5G6R5_UNORM:
4381 if (colorkey_active)
4383 *convert = CONVERT_CK_565;
4384 format->glFormat = GL_RGBA;
4385 format->glInternal = GL_RGB5_A1;
4386 format->glType = GL_UNSIGNED_SHORT_5_5_5_1;
4387 format->conv_byte_count = 2;
4389 break;
4391 case WINED3DFMT_B5G5R5X1_UNORM:
4392 if (colorkey_active)
4394 *convert = CONVERT_CK_5551;
4395 format->glFormat = GL_BGRA;
4396 format->glInternal = GL_RGB5_A1;
4397 format->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
4398 format->conv_byte_count = 2;
4400 break;
4402 case WINED3DFMT_B8G8R8_UNORM:
4403 if (colorkey_active)
4405 *convert = CONVERT_CK_RGB24;
4406 format->glFormat = GL_RGBA;
4407 format->glInternal = GL_RGBA8;
4408 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4409 format->conv_byte_count = 4;
4411 break;
4413 case WINED3DFMT_B8G8R8X8_UNORM:
4414 if (colorkey_active)
4416 *convert = CONVERT_RGB32_888;
4417 format->glFormat = GL_RGBA;
4418 format->glInternal = GL_RGBA8;
4419 format->glType = GL_UNSIGNED_INT_8_8_8_8;
4420 format->conv_byte_count = 4;
4422 break;
4424 default:
4425 break;
4428 return WINED3D_OK;
4431 void d3dfmt_p8_init_palette(const struct wined3d_surface *surface, BYTE table[256][4], BOOL colorkey)
4433 const struct wined3d_device *device = surface->resource.device;
4434 const struct wined3d_palette *pal = surface->palette;
4435 BOOL index_in_alpha = FALSE;
4436 unsigned int i;
4438 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
4439 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
4440 * is slow. Further RGB->P8 conversion is not possible because palettes can have
4441 * duplicate entries. Store the color key in the unused alpha component to speed the
4442 * download up and to make conversion unneeded. */
4443 index_in_alpha = primary_render_target_is_p8(device);
4445 if (!pal)
4447 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
4448 if (device->wined3d->flags & WINED3D_PALETTE_PER_SURFACE)
4450 ERR("This code should never get entered for DirectDraw!, expect problems\n");
4451 if (index_in_alpha)
4453 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
4454 * there's no palette at this time. */
4455 for (i = 0; i < 256; i++) table[i][3] = i;
4458 else
4460 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
4461 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
4462 * capability flag is present (wine does advertise this capability) */
4463 for (i = 0; i < 256; ++i)
4465 table[i][0] = device->palettes[device->currentPalette][i].peRed;
4466 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
4467 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
4468 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
4472 else
4474 TRACE("Using surface palette %p\n", pal);
4475 /* Get the surface's palette */
4476 for (i = 0; i < 256; ++i)
4478 table[i][0] = pal->palents[i].peRed;
4479 table[i][1] = pal->palents[i].peGreen;
4480 table[i][2] = pal->palents[i].peBlue;
4482 /* When index_in_alpha is set the palette index is stored in the
4483 * alpha component. In case of a readback we can then read
4484 * GL_ALPHA. Color keying is handled in BltOverride using a
4485 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
4486 * color key itself is passed to glAlphaFunc in other cases the
4487 * alpha component of pixels that should be masked away is set to 0. */
4488 if (index_in_alpha)
4490 table[i][3] = i;
4492 else if (colorkey && (i >= surface->SrcBltCKey.dwColorSpaceLowValue)
4493 && (i <= surface->SrcBltCKey.dwColorSpaceHighValue))
4495 table[i][3] = 0x00;
4497 else if (pal->flags & WINEDDPCAPS_ALPHA)
4499 table[i][3] = pal->palents[i].peFlags;
4501 else
4503 table[i][3] = 0xFF;
4509 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
4510 UINT height, UINT outpitch, CONVERT_TYPES convert, struct wined3d_surface *surface)
4512 const BYTE *source;
4513 BYTE *dest;
4514 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surface);
4516 switch (convert) {
4517 case NO_CONVERSION:
4519 memcpy(dst, src, pitch * height);
4520 break;
4522 case CONVERT_PALETTED:
4523 case CONVERT_PALETTED_CK:
4525 BYTE table[256][4];
4526 unsigned int x, y;
4528 d3dfmt_p8_init_palette(surface, table, (convert == CONVERT_PALETTED_CK));
4530 for (y = 0; y < height; y++)
4532 source = src + pitch * y;
4533 dest = dst + outpitch * y;
4534 /* This is an 1 bpp format, using the width here is fine */
4535 for (x = 0; x < width; x++) {
4536 BYTE color = *source++;
4537 *dest++ = table[color][0];
4538 *dest++ = table[color][1];
4539 *dest++ = table[color][2];
4540 *dest++ = table[color][3];
4544 break;
4546 case CONVERT_CK_565:
4548 /* Converting the 565 format in 5551 packed to emulate color-keying.
4550 Note : in all these conversion, it would be best to average the averaging
4551 pixels to get the color of the pixel that will be color-keyed to
4552 prevent 'color bleeding'. This will be done later on if ever it is
4553 too visible.
4555 Note2: Nvidia documents say that their driver does not support alpha + color keying
4556 on the same surface and disables color keying in such a case
4558 unsigned int x, y;
4559 const WORD *Source;
4560 WORD *Dest;
4562 TRACE("Color keyed 565\n");
4564 for (y = 0; y < height; y++) {
4565 Source = (const WORD *)(src + y * pitch);
4566 Dest = (WORD *) (dst + y * outpitch);
4567 for (x = 0; x < width; x++ ) {
4568 WORD color = *Source++;
4569 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
4570 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4571 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4572 *Dest |= 0x0001;
4573 Dest++;
4577 break;
4579 case CONVERT_CK_5551:
4581 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
4582 unsigned int x, y;
4583 const WORD *Source;
4584 WORD *Dest;
4585 TRACE("Color keyed 5551\n");
4586 for (y = 0; y < height; y++) {
4587 Source = (const WORD *)(src + y * pitch);
4588 Dest = (WORD *) (dst + y * outpitch);
4589 for (x = 0; x < width; x++ ) {
4590 WORD color = *Source++;
4591 *Dest = color;
4592 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4593 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4594 *Dest |= (1 << 15);
4595 else
4596 *Dest &= ~(1 << 15);
4597 Dest++;
4601 break;
4603 case CONVERT_CK_RGB24:
4605 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
4606 unsigned int x, y;
4607 for (y = 0; y < height; y++)
4609 source = src + pitch * y;
4610 dest = dst + outpitch * y;
4611 for (x = 0; x < width; x++) {
4612 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
4613 DWORD dstcolor = color << 8;
4614 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4615 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4616 dstcolor |= 0xff;
4617 *(DWORD*)dest = dstcolor;
4618 source += 3;
4619 dest += 4;
4623 break;
4625 case CONVERT_RGB32_888:
4627 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
4628 unsigned int x, y;
4629 for (y = 0; y < height; y++)
4631 source = src + pitch * y;
4632 dest = dst + outpitch * y;
4633 for (x = 0; x < width; x++) {
4634 DWORD color = 0xffffff & *(const DWORD*)source;
4635 DWORD dstcolor = color << 8;
4636 if ((color < surface->SrcBltCKey.dwColorSpaceLowValue)
4637 || (color > surface->SrcBltCKey.dwColorSpaceHighValue))
4638 dstcolor |= 0xff;
4639 *(DWORD*)dest = dstcolor;
4640 source += 4;
4641 dest += 4;
4645 break;
4647 default:
4648 ERR("Unsupported conversion type %#x.\n", convert);
4650 return WINED3D_OK;
4653 BOOL palette9_changed(struct wined3d_surface *surface)
4655 struct wined3d_device *device = surface->resource.device;
4657 if (surface->palette || (surface->resource.format->id != WINED3DFMT_P8_UINT
4658 && surface->resource.format->id != WINED3DFMT_P8_UINT_A8_UNORM))
4660 /* If a ddraw-style palette is attached assume no d3d9 palette change.
4661 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
4663 return FALSE;
4666 if (surface->palette9)
4668 if (!memcmp(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
4670 return FALSE;
4673 else
4675 surface->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
4677 memcpy(surface->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
4679 return TRUE;
4682 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
4684 /* Flip the surface contents */
4685 /* Flip the DC */
4687 HDC tmp;
4688 tmp = front->hDC;
4689 front->hDC = back->hDC;
4690 back->hDC = tmp;
4693 /* Flip the DIBsection */
4695 HBITMAP tmp;
4696 BOOL hasDib = front->flags & SFLAG_DIBSECTION;
4697 tmp = front->dib.DIBsection;
4698 front->dib.DIBsection = back->dib.DIBsection;
4699 back->dib.DIBsection = tmp;
4701 if (back->flags & SFLAG_DIBSECTION) front->flags |= SFLAG_DIBSECTION;
4702 else front->flags &= ~SFLAG_DIBSECTION;
4703 if (hasDib) back->flags |= SFLAG_DIBSECTION;
4704 else back->flags &= ~SFLAG_DIBSECTION;
4707 /* Flip the surface data */
4709 void* tmp;
4711 tmp = front->dib.bitmap_data;
4712 front->dib.bitmap_data = back->dib.bitmap_data;
4713 back->dib.bitmap_data = tmp;
4715 tmp = front->resource.allocatedMemory;
4716 front->resource.allocatedMemory = back->resource.allocatedMemory;
4717 back->resource.allocatedMemory = tmp;
4719 tmp = front->resource.heapMemory;
4720 front->resource.heapMemory = back->resource.heapMemory;
4721 back->resource.heapMemory = tmp;
4724 /* Flip the PBO */
4726 GLuint tmp_pbo = front->pbo;
4727 front->pbo = back->pbo;
4728 back->pbo = tmp_pbo;
4731 /* client_memory should not be different, but just in case */
4733 BOOL tmp;
4734 tmp = front->dib.client_memory;
4735 front->dib.client_memory = back->dib.client_memory;
4736 back->dib.client_memory = tmp;
4739 /* Flip the opengl texture */
4741 GLuint tmp;
4743 tmp = back->texture_name;
4744 back->texture_name = front->texture_name;
4745 front->texture_name = tmp;
4747 tmp = back->texture_name_srgb;
4748 back->texture_name_srgb = front->texture_name_srgb;
4749 front->texture_name_srgb = tmp;
4751 resource_unload(&back->resource);
4752 resource_unload(&front->resource);
4756 DWORD tmp_flags = back->flags;
4757 back->flags = front->flags;
4758 front->flags = tmp_flags;
4762 /* Does a direct frame buffer -> texture copy. Stretching is done with single
4763 * pixel copy calls. */
4764 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4765 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4767 struct wined3d_device *device = dst_surface->resource.device;
4768 float xrel, yrel;
4769 UINT row;
4770 struct wined3d_context *context;
4771 BOOL upsidedown = FALSE;
4772 RECT dst_rect = *dst_rect_in;
4774 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4775 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4777 if(dst_rect.top > dst_rect.bottom) {
4778 UINT tmp = dst_rect.bottom;
4779 dst_rect.bottom = dst_rect.top;
4780 dst_rect.top = tmp;
4781 upsidedown = TRUE;
4784 context = context_acquire(device, src_surface);
4785 context_apply_blit_state(context, device);
4786 surface_internal_preload(dst_surface, SRGB_RGB);
4787 ENTER_GL();
4789 /* Bind the target texture */
4790 glBindTexture(dst_surface->texture_target, dst_surface->texture_name);
4791 checkGLcall("glBindTexture");
4792 if (surface_is_offscreen(src_surface))
4794 TRACE("Reading from an offscreen target\n");
4795 upsidedown = !upsidedown;
4796 glReadBuffer(device->offscreenBuffer);
4798 else
4800 glReadBuffer(surface_get_gl_buffer(src_surface));
4802 checkGLcall("glReadBuffer");
4804 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
4805 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
4807 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4809 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
4811 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
4812 ERR("Texture filtering not supported in direct blit\n");
4815 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
4816 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4818 ERR("Texture filtering not supported in direct blit\n");
4821 if (upsidedown
4822 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4823 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
4825 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
4827 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4828 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
4829 src_rect->left, src_surface->resource.height - src_rect->bottom,
4830 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
4832 else
4834 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
4835 /* I have to process this row by row to swap the image,
4836 * otherwise it would be upside down, so stretching in y direction
4837 * doesn't cost extra time
4839 * However, stretching in x direction can be avoided if not necessary
4841 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
4842 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
4844 /* Well, that stuff works, but it's very slow.
4845 * find a better way instead
4847 UINT col;
4849 for (col = dst_rect.left; col < dst_rect.right; ++col)
4851 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4852 dst_rect.left + col /* x offset */, row /* y offset */,
4853 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
4856 else
4858 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
4859 dst_rect.left /* x offset */, row /* y offset */,
4860 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
4864 checkGLcall("glCopyTexSubImage2D");
4866 LEAVE_GL();
4867 context_release(context);
4869 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
4870 * path is never entered
4872 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
4875 /* Uses the hardware to stretch and flip the image */
4876 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
4877 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
4879 struct wined3d_device *device = dst_surface->resource.device;
4880 struct wined3d_swapchain *src_swapchain = NULL;
4881 GLuint src, backup = 0;
4882 float left, right, top, bottom; /* Texture coordinates */
4883 UINT fbwidth = src_surface->resource.width;
4884 UINT fbheight = src_surface->resource.height;
4885 struct wined3d_context *context;
4886 GLenum drawBuffer = GL_BACK;
4887 GLenum texture_target;
4888 BOOL noBackBufferBackup;
4889 BOOL src_offscreen;
4890 BOOL upsidedown = FALSE;
4891 RECT dst_rect = *dst_rect_in;
4893 TRACE("Using hwstretch blit\n");
4894 /* Activate the Proper context for reading from the source surface, set it up for blitting */
4895 context = context_acquire(device, src_surface);
4896 context_apply_blit_state(context, device);
4897 surface_internal_preload(dst_surface, SRGB_RGB);
4899 src_offscreen = surface_is_offscreen(src_surface);
4900 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
4901 if (!noBackBufferBackup && !src_surface->texture_name)
4903 /* Get it a description */
4904 surface_internal_preload(src_surface, SRGB_RGB);
4906 ENTER_GL();
4908 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
4909 * This way we don't have to wait for the 2nd readback to finish to leave this function.
4911 if (context->aux_buffers >= 2)
4913 /* Got more than one aux buffer? Use the 2nd aux buffer */
4914 drawBuffer = GL_AUX1;
4916 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
4918 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
4919 drawBuffer = GL_AUX0;
4922 if(noBackBufferBackup) {
4923 glGenTextures(1, &backup);
4924 checkGLcall("glGenTextures");
4925 glBindTexture(GL_TEXTURE_2D, backup);
4926 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
4927 texture_target = GL_TEXTURE_2D;
4928 } else {
4929 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
4930 * we are reading from the back buffer, the backup can be used as source texture
4932 texture_target = src_surface->texture_target;
4933 glBindTexture(texture_target, src_surface->texture_name);
4934 checkGLcall("glBindTexture(texture_target, src_surface->texture_name)");
4935 glEnable(texture_target);
4936 checkGLcall("glEnable(texture_target)");
4938 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
4939 src_surface->flags &= ~SFLAG_INTEXTURE;
4942 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
4943 * glCopyTexSubImage is a bit picky about the parameters we pass to it
4945 if(dst_rect.top > dst_rect.bottom) {
4946 UINT tmp = dst_rect.bottom;
4947 dst_rect.bottom = dst_rect.top;
4948 dst_rect.top = tmp;
4949 upsidedown = TRUE;
4952 if (src_offscreen)
4954 TRACE("Reading from an offscreen target\n");
4955 upsidedown = !upsidedown;
4956 glReadBuffer(device->offscreenBuffer);
4958 else
4960 glReadBuffer(surface_get_gl_buffer(src_surface));
4963 /* TODO: Only back up the part that will be overwritten */
4964 glCopyTexSubImage2D(texture_target, 0,
4965 0, 0 /* read offsets */,
4966 0, 0,
4967 fbwidth,
4968 fbheight);
4970 checkGLcall("glCopyTexSubImage2D");
4972 /* No issue with overriding these - the sampler is dirty due to blit usage */
4973 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
4974 wined3d_gl_mag_filter(magLookup, Filter));
4975 checkGLcall("glTexParameteri");
4976 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
4977 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
4978 checkGLcall("glTexParameteri");
4980 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
4981 src_swapchain = src_surface->container.u.swapchain;
4982 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
4984 src = backup ? backup : src_surface->texture_name;
4986 else
4988 glReadBuffer(GL_FRONT);
4989 checkGLcall("glReadBuffer(GL_FRONT)");
4991 glGenTextures(1, &src);
4992 checkGLcall("glGenTextures(1, &src)");
4993 glBindTexture(GL_TEXTURE_2D, src);
4994 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
4996 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
4997 * out for power of 2 sizes
4999 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
5000 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
5001 checkGLcall("glTexImage2D");
5002 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
5003 0, 0 /* read offsets */,
5004 0, 0,
5005 fbwidth,
5006 fbheight);
5008 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5009 checkGLcall("glTexParameteri");
5010 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5011 checkGLcall("glTexParameteri");
5013 glReadBuffer(GL_BACK);
5014 checkGLcall("glReadBuffer(GL_BACK)");
5016 if(texture_target != GL_TEXTURE_2D) {
5017 glDisable(texture_target);
5018 glEnable(GL_TEXTURE_2D);
5019 texture_target = GL_TEXTURE_2D;
5022 checkGLcall("glEnd and previous");
5024 left = src_rect->left;
5025 right = src_rect->right;
5027 if (!upsidedown)
5029 top = src_surface->resource.height - src_rect->top;
5030 bottom = src_surface->resource.height - src_rect->bottom;
5032 else
5034 top = src_surface->resource.height - src_rect->bottom;
5035 bottom = src_surface->resource.height - src_rect->top;
5038 if (src_surface->flags & SFLAG_NORMCOORD)
5040 left /= src_surface->pow2Width;
5041 right /= src_surface->pow2Width;
5042 top /= src_surface->pow2Height;
5043 bottom /= src_surface->pow2Height;
5046 /* draw the source texture stretched and upside down. The correct surface is bound already */
5047 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
5048 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
5050 context_set_draw_buffer(context, drawBuffer);
5051 glReadBuffer(drawBuffer);
5053 glBegin(GL_QUADS);
5054 /* bottom left */
5055 glTexCoord2f(left, bottom);
5056 glVertex2i(0, 0);
5058 /* top left */
5059 glTexCoord2f(left, top);
5060 glVertex2i(0, dst_rect.bottom - dst_rect.top);
5062 /* top right */
5063 glTexCoord2f(right, top);
5064 glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5066 /* bottom right */
5067 glTexCoord2f(right, bottom);
5068 glVertex2i(dst_rect.right - dst_rect.left, 0);
5069 glEnd();
5070 checkGLcall("glEnd and previous");
5072 if (texture_target != dst_surface->texture_target)
5074 glDisable(texture_target);
5075 glEnable(dst_surface->texture_target);
5076 texture_target = dst_surface->texture_target;
5079 /* Now read the stretched and upside down image into the destination texture */
5080 glBindTexture(texture_target, dst_surface->texture_name);
5081 checkGLcall("glBindTexture");
5082 glCopyTexSubImage2D(texture_target,
5084 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
5085 0, 0, /* We blitted the image to the origin */
5086 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
5087 checkGLcall("glCopyTexSubImage2D");
5089 if(drawBuffer == GL_BACK) {
5090 /* Write the back buffer backup back */
5091 if(backup) {
5092 if(texture_target != GL_TEXTURE_2D) {
5093 glDisable(texture_target);
5094 glEnable(GL_TEXTURE_2D);
5095 texture_target = GL_TEXTURE_2D;
5097 glBindTexture(GL_TEXTURE_2D, backup);
5098 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
5100 else
5102 if (texture_target != src_surface->texture_target)
5104 glDisable(texture_target);
5105 glEnable(src_surface->texture_target);
5106 texture_target = src_surface->texture_target;
5108 glBindTexture(src_surface->texture_target, src_surface->texture_name);
5109 checkGLcall("glBindTexture(src_surface->texture_target, src_surface->texture_name)");
5112 glBegin(GL_QUADS);
5113 /* top left */
5114 glTexCoord2f(0.0f, 0.0f);
5115 glVertex2i(0, fbheight);
5117 /* bottom left */
5118 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
5119 glVertex2i(0, 0);
5121 /* bottom right */
5122 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
5123 (float)fbheight / (float)src_surface->pow2Height);
5124 glVertex2i(fbwidth, 0);
5126 /* top right */
5127 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
5128 glVertex2i(fbwidth, fbheight);
5129 glEnd();
5131 glDisable(texture_target);
5132 checkGLcall("glDisable(texture_target)");
5134 /* Cleanup */
5135 if (src != src_surface->texture_name && src != backup)
5137 glDeleteTextures(1, &src);
5138 checkGLcall("glDeleteTextures(1, &src)");
5140 if(backup) {
5141 glDeleteTextures(1, &backup);
5142 checkGLcall("glDeleteTextures(1, &backup)");
5145 LEAVE_GL();
5147 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5149 context_release(context);
5151 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
5152 * path is never entered
5154 surface_modify_location(dst_surface, SFLAG_INTEXTURE, TRUE);
5157 /* Front buffer coordinates are always full screen coordinates, but our GL
5158 * drawable is limited to the window's client area. The sysmem and texture
5159 * copies do have the full screen size. Note that GL has a bottom-left
5160 * origin, while D3D has a top-left origin. */
5161 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
5163 UINT drawable_height;
5165 if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5166 && surface == surface->container.u.swapchain->front_buffer)
5168 POINT offset = {0, 0};
5169 RECT windowsize;
5171 ScreenToClient(window, &offset);
5172 OffsetRect(rect, offset.x, offset.y);
5174 GetClientRect(window, &windowsize);
5175 drawable_height = windowsize.bottom - windowsize.top;
5177 else
5179 drawable_height = surface->resource.height;
5182 rect->top = drawable_height - rect->top;
5183 rect->bottom = drawable_height - rect->bottom;
5186 static void surface_blt_to_drawable(struct wined3d_device *device,
5187 WINED3DTEXTUREFILTERTYPE filter, BOOL color_key,
5188 struct wined3d_surface *src_surface, const RECT *src_rect_in,
5189 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
5191 struct wined3d_context *context;
5192 RECT src_rect, dst_rect;
5194 src_rect = *src_rect_in;
5195 dst_rect = *dst_rect_in;
5197 /* Make sure the surface is up-to-date. This should probably use
5198 * surface_load_location() and worry about the destination surface too,
5199 * unless we're overwriting it completely. */
5200 surface_internal_preload(src_surface, SRGB_RGB);
5202 /* Activate the destination context, set it up for blitting */
5203 context = context_acquire(device, dst_surface);
5204 context_apply_blit_state(context, device);
5206 if (!surface_is_offscreen(dst_surface))
5207 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
5209 device->blitter->set_shader(device->blit_priv, context->gl_info, src_surface);
5211 ENTER_GL();
5213 if (color_key)
5215 glEnable(GL_ALPHA_TEST);
5216 checkGLcall("glEnable(GL_ALPHA_TEST)");
5218 /* When the primary render target uses P8, the alpha component
5219 * contains the palette index. Which means that the colorkey is one of
5220 * the palette entries. In other cases pixels that should be masked
5221 * away have alpha set to 0. */
5222 if (primary_render_target_is_p8(device))
5223 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
5224 else
5225 glAlphaFunc(GL_NOTEQUAL, 0.0f);
5226 checkGLcall("glAlphaFunc");
5228 else
5230 glDisable(GL_ALPHA_TEST);
5231 checkGLcall("glDisable(GL_ALPHA_TEST)");
5234 draw_textured_quad(src_surface, &src_rect, &dst_rect, filter);
5236 if (color_key)
5238 glDisable(GL_ALPHA_TEST);
5239 checkGLcall("glDisable(GL_ALPHA_TEST)");
5242 LEAVE_GL();
5244 /* Leave the opengl state valid for blitting */
5245 device->blitter->unset_shader(context->gl_info);
5247 if (wined3d_settings.strict_draw_ordering
5248 || (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN
5249 && (dst_surface->container.u.swapchain->front_buffer == dst_surface)))
5250 wglFlush(); /* Flush to ensure ordering across contexts. */
5252 context_release(context);
5255 /* Do not call while under the GL lock. */
5256 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const WINED3DCOLORVALUE *color)
5258 struct wined3d_device *device = s->resource.device;
5259 const struct blit_shader *blitter;
5261 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
5262 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
5263 if (!blitter)
5265 FIXME("No blitter is capable of performing the requested color fill operation.\n");
5266 return WINED3DERR_INVALIDCALL;
5269 return blitter->color_fill(device, s, rect, color);
5272 /* Do not call while under the GL lock. */
5273 static HRESULT IWineD3DSurfaceImpl_BltOverride(struct wined3d_surface *dst_surface, const RECT *dst_rect,
5274 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
5275 WINED3DTEXTUREFILTERTYPE Filter)
5277 struct wined3d_device *device = dst_surface->resource.device;
5278 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5279 struct wined3d_swapchain *srcSwapchain = NULL, *dstSwapchain = NULL;
5281 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
5282 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
5283 flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
5285 /* Get the swapchain. One of the surfaces has to be a primary surface */
5286 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5288 WARN("Destination is in sysmem, rejecting gl blt\n");
5289 return WINED3DERR_INVALIDCALL;
5292 if (dst_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5293 dstSwapchain = dst_surface->container.u.swapchain;
5295 if (src_surface)
5297 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
5299 WARN("Src is in sysmem, rejecting gl blt\n");
5300 return WINED3DERR_INVALIDCALL;
5303 if (src_surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
5304 srcSwapchain = src_surface->container.u.swapchain;
5307 /* Early sort out of cases where no render target is used */
5308 if (!dstSwapchain && !srcSwapchain
5309 && src_surface != device->fb.render_targets[0]
5310 && dst_surface != device->fb.render_targets[0])
5312 TRACE("No surface is render target, not using hardware blit.\n");
5313 return WINED3DERR_INVALIDCALL;
5316 /* No destination color keying supported */
5317 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
5319 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
5320 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
5321 return WINED3DERR_INVALIDCALL;
5324 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
5325 if (dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->back_buffers
5326 && dst_surface == dstSwapchain->front_buffer
5327 && src_surface == dstSwapchain->back_buffers[0])
5329 /* Half-Life does a Blt from the back buffer to the front buffer,
5330 * Full surface size, no flags... Use present instead
5332 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
5335 /* Check rects - wined3d_swapchain_present() doesn't handle them. */
5336 for (;;)
5338 TRACE("Looking if a Present can be done...\n");
5339 /* Source Rectangle must be full surface */
5340 if (src_rect->left || src_rect->top
5341 || src_rect->right != src_surface->resource.width
5342 || src_rect->bottom != src_surface->resource.height)
5344 TRACE("No, Source rectangle doesn't match\n");
5345 break;
5348 /* No stretching may occur */
5349 if (src_rect->right != dst_rect->right - dst_rect->left
5350 || src_rect->bottom != dst_rect->bottom - dst_rect->top)
5352 TRACE("No, stretching is done\n");
5353 break;
5356 /* Destination must be full surface or match the clipping rectangle */
5357 if (dst_surface->clipper && dst_surface->clipper->hWnd)
5359 RECT cliprect;
5360 POINT pos[2];
5361 GetClientRect(dst_surface->clipper->hWnd, &cliprect);
5362 pos[0].x = dst_rect->left;
5363 pos[0].y = dst_rect->top;
5364 pos[1].x = dst_rect->right;
5365 pos[1].y = dst_rect->bottom;
5366 MapWindowPoints(GetDesktopWindow(), dst_surface->clipper->hWnd, pos, 2);
5368 if (pos[0].x != cliprect.left || pos[0].y != cliprect.top
5369 || pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
5371 TRACE("No, dest rectangle doesn't match(clipper)\n");
5372 TRACE("Clip rect at %s\n", wine_dbgstr_rect(&cliprect));
5373 TRACE("Blt dest: %s\n", wine_dbgstr_rect(dst_rect));
5374 break;
5377 else if (dst_rect->left || dst_rect->top
5378 || dst_rect->right != dst_surface->resource.width
5379 || dst_rect->bottom != dst_surface->resource.height)
5381 TRACE("No, dest rectangle doesn't match(surface size)\n");
5382 break;
5385 TRACE("Yes\n");
5387 /* These flags are unimportant for the flag check, remove them */
5388 if (!(flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)))
5390 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
5392 /* The idea behind this is that a glReadPixels and a glDrawPixels call
5393 * take very long, while a flip is fast.
5394 * This applies to Half-Life, which does such Blts every time it finished
5395 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
5396 * menu. This is also used by all apps when they do windowed rendering
5398 * The problem is that flipping is not really the same as copying. After a
5399 * Blt the front buffer is a copy of the back buffer, and the back buffer is
5400 * untouched. Therefore it's necessary to override the swap effect
5401 * and to set it back after the flip.
5403 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
5404 * testcases.
5407 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
5408 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
5410 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead.\n");
5411 wined3d_swapchain_present(dstSwapchain, NULL, NULL, dstSwapchain->win_handle, NULL, 0);
5413 dstSwapchain->presentParms.SwapEffect = orig_swap;
5415 return WINED3D_OK;
5417 break;
5420 TRACE("Unsupported blit between buffers on the same swapchain\n");
5421 return WINED3DERR_INVALIDCALL;
5422 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
5423 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
5424 return WINED3DERR_INVALIDCALL;
5425 } else if(dstSwapchain && srcSwapchain) {
5426 FIXME("Implement hardware blit between two different swapchains\n");
5427 return WINED3DERR_INVALIDCALL;
5429 else if (dstSwapchain)
5431 /* Handled with regular texture -> swapchain blit */
5432 if (src_surface == device->fb.render_targets[0])
5433 TRACE("Blit from active render target to a swapchain\n");
5435 else if (srcSwapchain && dst_surface == device->fb.render_targets[0])
5437 FIXME("Implement blit from a swapchain to the active render target\n");
5438 return WINED3DERR_INVALIDCALL;
5441 if ((srcSwapchain || src_surface == device->fb.render_targets[0]) && !dstSwapchain)
5443 /* Blit from render target to texture */
5444 BOOL stretchx;
5446 /* P8 read back is not implemented */
5447 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
5448 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
5450 TRACE("P8 read back not supported by frame buffer to texture blit\n");
5451 return WINED3DERR_INVALIDCALL;
5454 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5456 TRACE("Color keying not supported by frame buffer to texture blit\n");
5457 return WINED3DERR_INVALIDCALL;
5458 /* Destination color key is checked above */
5461 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
5462 stretchx = TRUE;
5463 else
5464 stretchx = FALSE;
5466 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
5467 * flip the image nor scale it.
5469 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
5470 * -> If the app wants a image width an unscaled width, copy it line per line
5471 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
5472 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
5473 * back buffer. This is slower than reading line per line, thus not used for flipping
5474 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
5475 * pixel by pixel. */
5476 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
5477 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
5479 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
5480 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, Filter);
5481 } else {
5482 TRACE("Using hardware stretching to flip / stretch the texture\n");
5483 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, Filter);
5486 if (!(dst_surface->flags & SFLAG_DONOTFREE))
5488 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
5489 dst_surface->resource.allocatedMemory = NULL;
5490 dst_surface->resource.heapMemory = NULL;
5492 else
5494 dst_surface->flags &= ~SFLAG_INSYSMEM;
5497 return WINED3D_OK;
5499 else if (src_surface)
5501 /* Blit from offscreen surface to render target */
5502 DWORD oldCKeyFlags = src_surface->CKeyFlags;
5503 WINEDDCOLORKEY oldBltCKey = src_surface->SrcBltCKey;
5505 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
5507 if (!(flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
5508 && arbfp_blit.blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5509 src_rect, src_surface->resource.usage, src_surface->resource.pool,
5510 src_surface->resource.format,
5511 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool,
5512 dst_surface->resource.format))
5513 return arbfp_blit_surface(device, Filter, src_surface, src_rect, dst_surface, dst_rect);
5515 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5516 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5517 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5519 FIXME("Unsupported blit operation falling back to software\n");
5520 return WINED3DERR_INVALIDCALL;
5523 /* Color keying: Check if we have to do a color keyed blt,
5524 * and if not check if a color key is activated.
5526 * Just modify the color keying parameters in the surface and restore them afterwards
5527 * The surface keeps track of the color key last used to load the opengl surface.
5528 * PreLoad will catch the change to the flags and color key and reload if necessary.
5530 if (flags & WINEDDBLT_KEYSRC)
5532 /* Use color key from surface */
5534 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5536 /* Use color key from DDBltFx */
5537 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
5538 src_surface->SrcBltCKey = DDBltFx->ddckSrcColorkey;
5540 else
5542 /* Do not use color key */
5543 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
5546 surface_blt_to_drawable(device, Filter, flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE),
5547 src_surface, src_rect, dst_surface, dst_rect);
5549 /* Restore the color key parameters */
5550 src_surface->CKeyFlags = oldCKeyFlags;
5551 src_surface->SrcBltCKey = oldBltCKey;
5553 surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE);
5555 return WINED3D_OK;
5558 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
5559 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
5560 return WINED3DERR_INVALIDCALL;
5563 /* GL locking is done by the caller */
5564 static void surface_depth_blt(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
5565 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
5567 struct wined3d_device *device = surface->resource.device;
5568 GLint compare_mode = GL_NONE;
5569 struct blt_info info;
5570 GLint old_binding = 0;
5571 RECT rect;
5573 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
5575 glDisable(GL_CULL_FACE);
5576 glDisable(GL_BLEND);
5577 glDisable(GL_ALPHA_TEST);
5578 glDisable(GL_SCISSOR_TEST);
5579 glDisable(GL_STENCIL_TEST);
5580 glEnable(GL_DEPTH_TEST);
5581 glDepthFunc(GL_ALWAYS);
5582 glDepthMask(GL_TRUE);
5583 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
5584 glViewport(x, y, w, h);
5586 SetRect(&rect, 0, h, w, 0);
5587 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
5588 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
5589 glGetIntegerv(info.binding, &old_binding);
5590 glBindTexture(info.bind_target, texture);
5591 if (gl_info->supported[ARB_SHADOW])
5593 glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
5594 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
5597 device->shader_backend->shader_select_depth_blt(device->shader_priv,
5598 gl_info, info.tex_type, &surface->ds_current_size);
5600 glBegin(GL_TRIANGLE_STRIP);
5601 glTexCoord3fv(info.coords[0]);
5602 glVertex2f(-1.0f, -1.0f);
5603 glTexCoord3fv(info.coords[1]);
5604 glVertex2f(1.0f, -1.0f);
5605 glTexCoord3fv(info.coords[2]);
5606 glVertex2f(-1.0f, 1.0f);
5607 glTexCoord3fv(info.coords[3]);
5608 glVertex2f(1.0f, 1.0f);
5609 glEnd();
5611 if (compare_mode != GL_NONE) glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
5612 glBindTexture(info.bind_target, old_binding);
5614 glPopAttrib();
5616 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
5619 void surface_modify_ds_location(struct wined3d_surface *surface,
5620 DWORD location, UINT w, UINT h)
5622 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
5624 if (location & ~SFLAG_DS_LOCATIONS)
5625 FIXME("Invalid location (%#x) specified.\n", location);
5627 surface->ds_current_size.cx = w;
5628 surface->ds_current_size.cy = h;
5629 surface->flags &= ~SFLAG_DS_LOCATIONS;
5630 surface->flags |= location;
5633 /* Context activation is done by the caller. */
5634 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
5636 struct wined3d_device *device = surface->resource.device;
5637 const struct wined3d_gl_info *gl_info = context->gl_info;
5638 GLsizei w, h;
5640 TRACE("surface %p, new location %#x.\n", surface, location);
5642 /* TODO: Make this work for modes other than FBO */
5643 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
5645 if (!(surface->flags & location))
5647 w = surface->ds_current_size.cx;
5648 h = surface->ds_current_size.cy;
5649 surface->ds_current_size.cx = 0;
5650 surface->ds_current_size.cy = 0;
5652 else
5654 w = surface->resource.width;
5655 h = surface->resource.height;
5658 if (surface->ds_current_size.cx == surface->resource.width
5659 && surface->ds_current_size.cy == surface->resource.height)
5661 TRACE("Location (%#x) is already up to date.\n", location);
5662 return;
5665 if (surface->current_renderbuffer)
5667 FIXME("Not supported with fixed up depth stencil.\n");
5668 return;
5671 if (!(surface->flags & SFLAG_DS_LOCATIONS))
5673 /* This mostly happens when a depth / stencil is used without being
5674 * cleared first. In principle we could upload from sysmem, or
5675 * explicitly clear before first usage. For the moment there don't
5676 * appear to be a lot of applications depending on this, so a FIXME
5677 * should do. */
5678 FIXME("No up to date depth stencil location.\n");
5679 surface->flags |= location;
5680 surface->ds_current_size.cx = surface->resource.width;
5681 surface->ds_current_size.cy = surface->resource.height;
5682 return;
5685 if (location == SFLAG_DS_OFFSCREEN)
5687 GLint old_binding = 0;
5688 GLenum bind_target;
5690 /* The render target is allowed to be smaller than the depth/stencil
5691 * buffer, so the onscreen depth/stencil buffer is potentially smaller
5692 * than the offscreen surface. Don't overwrite the offscreen surface
5693 * with undefined data. */
5694 w = min(w, context->swapchain->presentParms.BackBufferWidth);
5695 h = min(h, context->swapchain->presentParms.BackBufferHeight);
5697 TRACE("Copying onscreen depth buffer to depth texture.\n");
5699 ENTER_GL();
5701 if (!device->depth_blt_texture)
5703 glGenTextures(1, &device->depth_blt_texture);
5706 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
5707 * directly on the FBO texture. That's because we need to flip. */
5708 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5709 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5710 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
5712 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
5713 bind_target = GL_TEXTURE_RECTANGLE_ARB;
5715 else
5717 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
5718 bind_target = GL_TEXTURE_2D;
5720 glBindTexture(bind_target, device->depth_blt_texture);
5721 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
5722 * internal format, because the internal format might include stencil
5723 * data. In principle we should copy stencil data as well, but unless
5724 * the driver supports stencil export it's hard to do, and doesn't
5725 * seem to be needed in practice. If the hardware doesn't support
5726 * writing stencil data, the glCopyTexImage2D() call might trigger
5727 * software fallbacks. */
5728 glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
5729 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
5730 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
5731 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
5732 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
5733 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
5734 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
5735 glBindTexture(bind_target, old_binding);
5737 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5738 NULL, surface, SFLAG_INTEXTURE);
5739 context_set_draw_buffer(context, GL_NONE);
5740 glReadBuffer(GL_NONE);
5742 /* Do the actual blit */
5743 surface_depth_blt(surface, gl_info, device->depth_blt_texture, 0, 0, w, h, bind_target);
5744 checkGLcall("depth_blt");
5746 context_invalidate_state(context, STATE_FRAMEBUFFER);
5748 LEAVE_GL();
5750 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5752 else if (location == SFLAG_DS_ONSCREEN)
5754 TRACE("Copying depth texture to onscreen depth buffer.\n");
5756 ENTER_GL();
5758 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
5759 context->swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
5760 surface_depth_blt(surface, gl_info, surface->texture_name,
5761 0, surface->pow2Height - h, w, h, surface->texture_target);
5762 checkGLcall("depth_blt");
5764 context_invalidate_state(context, STATE_FRAMEBUFFER);
5766 LEAVE_GL();
5768 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
5770 else
5772 ERR("Invalid location (%#x) specified.\n", location);
5775 surface->flags |= location;
5776 surface->ds_current_size.cx = surface->resource.width;
5777 surface->ds_current_size.cy = surface->resource.height;
5780 void surface_modify_location(struct wined3d_surface *surface, DWORD location, BOOL persistent)
5782 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
5783 struct wined3d_surface *overlay;
5785 TRACE("surface %p, location %s, persistent %#x.\n",
5786 surface, debug_surflocation(location), persistent);
5788 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
5790 if (surface_is_offscreen(surface))
5792 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same
5793 * for offscreen targets. */
5794 if (location & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))
5795 location |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
5797 else
5799 TRACE("Surface %p is an onscreen surface.\n", surface);
5803 if (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
5804 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
5805 location |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
5807 if (persistent)
5809 if (((surface->flags & SFLAG_INTEXTURE) && !(location & SFLAG_INTEXTURE))
5810 || ((surface->flags & SFLAG_INSRGBTEX) && !(location & SFLAG_INSRGBTEX)))
5812 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5814 TRACE("Passing to container.\n");
5815 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5818 surface->flags &= ~SFLAG_LOCATIONS;
5819 surface->flags |= location;
5821 /* Redraw emulated overlays, if any */
5822 if (location & SFLAG_INDRAWABLE && !list_empty(&surface->overlays))
5824 LIST_FOR_EACH_ENTRY(overlay, &surface->overlays, struct wined3d_surface, overlay_entry)
5826 overlay->surface_ops->surface_draw_overlay(overlay);
5830 else
5832 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (location & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)))
5834 if (surface->container.type == WINED3D_CONTAINER_TEXTURE)
5836 TRACE("Passing to container\n");
5837 wined3d_texture_set_dirty(surface->container.u.texture, TRUE);
5840 surface->flags &= ~location;
5843 if (!(surface->flags & SFLAG_LOCATIONS))
5845 ERR("Surface %p does not have any up to date location.\n", surface);
5849 static DWORD resource_access_from_location(DWORD location)
5851 switch (location)
5853 case SFLAG_INSYSMEM:
5854 return WINED3D_RESOURCE_ACCESS_CPU;
5856 case SFLAG_INDRAWABLE:
5857 case SFLAG_INSRGBTEX:
5858 case SFLAG_INTEXTURE:
5859 return WINED3D_RESOURCE_ACCESS_GPU;
5861 default:
5862 FIXME("Unhandled location %#x.\n", location);
5863 return 0;
5867 static void surface_load_sysmem(struct wined3d_surface *surface,
5868 const struct wined3d_gl_info *gl_info, const RECT *rect)
5870 surface_prepare_system_memory(surface);
5872 /* Download the surface to system memory. */
5873 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
5875 struct wined3d_device *device = surface->resource.device;
5876 struct wined3d_context *context = NULL;
5878 if (!device->isInDraw)
5879 context = context_acquire(device, NULL);
5881 surface_bind_and_dirtify(surface, gl_info, !(surface->flags & SFLAG_INTEXTURE));
5882 surface_download_data(surface, gl_info);
5884 if (context)
5885 context_release(context);
5887 return;
5890 /* Note: It might be faster to download into a texture first. */
5891 read_from_framebuffer(surface, rect, surface->resource.allocatedMemory,
5892 wined3d_surface_get_pitch(surface));
5895 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
5896 const struct wined3d_gl_info *gl_info, const RECT *rect)
5898 struct wined3d_device *device = surface->resource.device;
5899 struct wined3d_format format;
5900 CONVERT_TYPES convert;
5901 UINT byte_count;
5902 BYTE *mem;
5904 if (wined3d_settings.rendertargetlock_mode == RTL_READTEX)
5905 surface_load_location(surface, SFLAG_INTEXTURE, NULL);
5907 if (surface->flags & SFLAG_INTEXTURE)
5909 RECT r;
5911 surface_get_rect(surface, rect, &r);
5912 surface_blt_to_drawable(device, WINED3DTEXF_POINT, FALSE, surface, &r, surface, &r);
5914 return WINED3D_OK;
5917 if ((surface->flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX)
5919 /* This needs colorspace conversion from sRGB to RGB. We take the slow
5920 * path through sysmem. */
5921 surface_load_location(surface, SFLAG_INSYSMEM, rect);
5924 d3dfmt_get_conv(surface, FALSE, FALSE, &format, &convert);
5926 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
5927 * SFLAG_CONVERTED but it isn't set (yet) in all cases where it is getting
5928 * called. */
5929 if ((convert != NO_CONVERSION) && (surface->flags & SFLAG_PBO))
5931 struct wined3d_context *context = NULL;
5933 TRACE("Removing the pbo attached to surface %p.\n", surface);
5935 if (!device->isInDraw)
5936 context = context_acquire(device, NULL);
5938 surface_remove_pbo(surface, gl_info);
5940 if (context)
5941 context_release(context);
5944 if ((convert != NO_CONVERSION) && surface->resource.allocatedMemory)
5946 UINT height = surface->resource.height;
5947 UINT width = surface->resource.width;
5948 UINT src_pitch, dst_pitch;
5950 byte_count = format.conv_byte_count;
5951 src_pitch = wined3d_surface_get_pitch(surface);
5953 /* Stick to the alignment for the converted surface too, makes it
5954 * easier to load the surface. */
5955 dst_pitch = width * byte_count;
5956 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5958 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
5960 ERR("Out of memory (%u).\n", dst_pitch * height);
5961 return E_OUTOFMEMORY;
5964 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem,
5965 src_pitch, width, height, dst_pitch, convert, surface);
5967 surface->flags |= SFLAG_CONVERTED;
5969 else
5971 surface->flags &= ~SFLAG_CONVERTED;
5972 mem = surface->resource.allocatedMemory;
5973 byte_count = format.byte_count;
5976 flush_to_framebuffer_drawpixels(surface, rect, format.glFormat, format.glType, byte_count, mem);
5978 /* Don't delete PBO memory. */
5979 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
5980 HeapFree(GetProcessHeap(), 0, mem);
5982 return WINED3D_OK;
5985 static HRESULT surface_load_texture(struct wined3d_surface *surface,
5986 const struct wined3d_gl_info *gl_info, const RECT *rect, BOOL srgb)
5988 const DWORD attach_flags = WINED3DFMT_FLAG_FBO_ATTACHABLE | WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB;
5989 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
5990 struct wined3d_device *device = surface->resource.device;
5991 struct wined3d_context *context = NULL;
5992 UINT width, src_pitch, dst_pitch;
5993 struct wined3d_bo_address data;
5994 struct wined3d_format format;
5995 POINT dst_point = {0, 0};
5996 CONVERT_TYPES convert;
5997 BYTE *mem;
5999 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
6000 && surface_is_offscreen(surface)
6001 && (surface->flags & SFLAG_INDRAWABLE))
6003 read_from_framebuffer_texture(surface, srgb);
6005 return WINED3D_OK;
6008 if (surface->flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
6009 && (surface->resource.format->flags & attach_flags) == attach_flags
6010 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
6011 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
6012 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
6014 if (srgb)
6015 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INTEXTURE,
6016 &src_rect, surface, SFLAG_INSRGBTEX, &src_rect);
6017 else
6018 surface_blt_fbo(device, WINED3DTEXF_POINT, surface, SFLAG_INSRGBTEX,
6019 &src_rect, surface, SFLAG_INTEXTURE, &src_rect);
6021 return WINED3D_OK;
6024 /* Upload from system memory */
6026 d3dfmt_get_conv(surface, TRUE /* We need color keying */,
6027 TRUE /* We will use textures */, &format, &convert);
6029 if (srgb)
6031 if ((surface->flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE)
6033 /* Performance warning... */
6034 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
6035 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6038 else
6040 if ((surface->flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX)
6042 /* Performance warning... */
6043 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
6044 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6048 if (!(surface->flags & SFLAG_INSYSMEM))
6050 WARN("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set.\n");
6051 /* Lets hope we get it from somewhere... */
6052 surface_load_location(surface, SFLAG_INSYSMEM, rect);
6055 if (!device->isInDraw)
6056 context = context_acquire(device, NULL);
6058 surface_prepare_texture(surface, gl_info, srgb);
6059 surface_bind_and_dirtify(surface, gl_info, srgb);
6061 if (surface->CKeyFlags & WINEDDSD_CKSRCBLT)
6063 surface->flags |= SFLAG_GLCKEY;
6064 surface->glCKey = surface->SrcBltCKey;
6066 else surface->flags &= ~SFLAG_GLCKEY;
6068 width = surface->resource.width;
6069 src_pitch = wined3d_surface_get_pitch(surface);
6071 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
6072 * SFLAG_CONVERTED but it isn't set (yet) in all cases it is getting
6073 * called. */
6074 if ((convert != NO_CONVERSION || format.convert) && (surface->flags & SFLAG_PBO))
6076 TRACE("Removing the pbo attached to surface %p.\n", surface);
6077 surface_remove_pbo(surface, gl_info);
6080 if (format.convert)
6082 /* This code is entered for texture formats which need a fixup. */
6083 UINT height = surface->resource.height;
6085 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6086 dst_pitch = width * format.conv_byte_count;
6087 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6089 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6091 ERR("Out of memory (%u).\n", dst_pitch * height);
6092 if (context)
6093 context_release(context);
6094 return E_OUTOFMEMORY;
6096 format.convert(surface->resource.allocatedMemory, mem, src_pitch, width, height);
6098 else if (convert != NO_CONVERSION && surface->resource.allocatedMemory)
6100 /* This code is only entered for color keying fixups */
6101 UINT height = surface->resource.height;
6103 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
6104 dst_pitch = width * format.conv_byte_count;
6105 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
6107 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
6109 ERR("Out of memory (%u).\n", dst_pitch * height);
6110 if (context)
6111 context_release(context);
6112 return E_OUTOFMEMORY;
6114 d3dfmt_convert_surface(surface->resource.allocatedMemory, mem, src_pitch,
6115 width, height, dst_pitch, convert, surface);
6117 else
6119 mem = surface->resource.allocatedMemory;
6122 data.buffer_object = surface->flags & SFLAG_PBO ? surface->pbo : 0;
6123 data.addr = mem;
6124 surface_upload_data(surface, gl_info, &format, &src_rect, width, &dst_point, srgb, &data);
6126 if (context)
6127 context_release(context);
6129 /* Don't delete PBO memory. */
6130 if ((mem != surface->resource.allocatedMemory) && !(surface->flags & SFLAG_PBO))
6131 HeapFree(GetProcessHeap(), 0, mem);
6133 return WINED3D_OK;
6136 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location, const RECT *rect)
6138 struct wined3d_device *device = surface->resource.device;
6139 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
6140 BOOL in_fbo = FALSE;
6141 HRESULT hr;
6143 TRACE("surface %p, location %s, rect %s.\n", surface, debug_surflocation(location), wine_dbgstr_rect(rect));
6145 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
6147 if (location == SFLAG_INTEXTURE)
6149 struct wined3d_context *context = context_acquire(device, NULL);
6150 surface_load_ds_location(surface, context, SFLAG_DS_OFFSCREEN);
6151 context_release(context);
6152 return WINED3D_OK;
6154 else
6156 FIXME("Unimplemented location %s for depth/stencil buffers.\n", debug_surflocation(location));
6157 return WINED3DERR_INVALIDCALL;
6161 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6163 if (surface_is_offscreen(surface))
6165 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same
6166 * for offscreen targets. Prefer SFLAG_INTEXTURE. */
6167 if (location == SFLAG_INDRAWABLE)
6168 location = SFLAG_INTEXTURE;
6169 in_fbo = TRUE;
6171 else
6173 TRACE("Surface %p is an onscreen surface.\n", surface);
6177 if (location == SFLAG_INSRGBTEX && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6178 location = SFLAG_INTEXTURE;
6180 if (surface->flags & location)
6182 TRACE("Location already up to date.\n");
6183 return WINED3D_OK;
6186 if (WARN_ON(d3d_surface))
6188 DWORD required_access = resource_access_from_location(location);
6189 if ((surface->resource.access_flags & required_access) != required_access)
6190 WARN("Operation requires %#x access, but surface only has %#x.\n",
6191 required_access, surface->resource.access_flags);
6194 if (!(surface->flags & SFLAG_LOCATIONS))
6196 ERR("Surface %p does not have any up to date location.\n", surface);
6197 surface->flags |= SFLAG_LOST;
6198 return WINED3DERR_DEVICELOST;
6201 switch (location)
6203 case SFLAG_INSYSMEM:
6204 surface_load_sysmem(surface, gl_info, rect);
6205 break;
6207 case SFLAG_INDRAWABLE:
6208 if (FAILED(hr = surface_load_drawable(surface, gl_info, rect)))
6209 return hr;
6210 break;
6212 case SFLAG_INTEXTURE:
6213 case SFLAG_INSRGBTEX:
6214 if (FAILED(hr = surface_load_texture(surface, gl_info, rect, location == SFLAG_INSRGBTEX)))
6215 return hr;
6216 break;
6218 default:
6219 ERR("Don't know how to handle location %#x.\n", location);
6220 break;
6223 if (!rect)
6225 surface->flags |= location;
6227 if (location != SFLAG_INSYSMEM && (surface->flags & SFLAG_INSYSMEM))
6228 surface_evict_sysmem(surface);
6231 if (in_fbo && (surface->flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)))
6233 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
6234 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
6237 if (surface->flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)
6238 && gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
6240 surface->flags |= (SFLAG_INTEXTURE | SFLAG_INSRGBTEX);
6243 return WINED3D_OK;
6246 BOOL surface_is_offscreen(const struct wined3d_surface *surface)
6248 struct wined3d_swapchain *swapchain = surface->container.u.swapchain;
6250 /* Not on a swapchain - must be offscreen */
6251 if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN) return TRUE;
6253 /* The front buffer is always onscreen */
6254 if (surface == swapchain->front_buffer) return FALSE;
6256 /* If the swapchain is rendered to an FBO, the backbuffer is
6257 * offscreen, otherwise onscreen */
6258 return swapchain->render_to_fbo;
6261 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
6262 /* Context activation is done by the caller. */
6263 static void ffp_blit_free(struct wined3d_device *device) { }
6265 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
6266 /* Context activation is done by the caller. */
6267 static void ffp_blit_p8_upload_palette(const struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
6269 BYTE table[256][4];
6270 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
6272 d3dfmt_p8_init_palette(surface, table, colorkey_active);
6274 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
6275 ENTER_GL();
6276 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
6277 LEAVE_GL();
6280 /* Context activation is done by the caller. */
6281 static HRESULT ffp_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, struct wined3d_surface *surface)
6283 enum complex_fixup fixup = get_complex_fixup(surface->resource.format->color_fixup);
6285 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
6286 * else the surface is converted in software at upload time in LoadLocation.
6288 if(fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6289 ffp_blit_p8_upload_palette(surface, gl_info);
6291 ENTER_GL();
6292 glEnable(surface->texture_target);
6293 checkGLcall("glEnable(surface->texture_target)");
6294 LEAVE_GL();
6295 return WINED3D_OK;
6298 /* Context activation is done by the caller. */
6299 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
6301 ENTER_GL();
6302 glDisable(GL_TEXTURE_2D);
6303 checkGLcall("glDisable(GL_TEXTURE_2D)");
6304 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
6306 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
6307 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
6309 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
6311 glDisable(GL_TEXTURE_RECTANGLE_ARB);
6312 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
6314 LEAVE_GL();
6317 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6318 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6319 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6321 enum complex_fixup src_fixup;
6323 switch (blit_op)
6325 case WINED3D_BLIT_OP_COLOR_BLIT:
6326 src_fixup = get_complex_fixup(src_format->color_fixup);
6327 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
6329 TRACE("Checking support for fixup:\n");
6330 dump_color_fixup_desc(src_format->color_fixup);
6333 if (!is_identity_fixup(dst_format->color_fixup))
6335 TRACE("Destination fixups are not supported\n");
6336 return FALSE;
6339 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
6341 TRACE("P8 fixup supported\n");
6342 return TRUE;
6345 /* We only support identity conversions. */
6346 if (is_identity_fixup(src_format->color_fixup))
6348 TRACE("[OK]\n");
6349 return TRUE;
6352 TRACE("[FAILED]\n");
6353 return FALSE;
6355 case WINED3D_BLIT_OP_COLOR_FILL:
6356 if (dst_pool == WINED3DPOOL_SYSTEMMEM)
6357 return FALSE;
6359 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
6361 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
6362 return FALSE;
6364 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
6366 TRACE("Color fill not supported\n");
6367 return FALSE;
6370 /* FIXME: We should reject color fills on formats with fixups,
6371 * but this would break P8 color fills for example. */
6373 return TRUE;
6375 case WINED3D_BLIT_OP_DEPTH_FILL:
6376 return TRUE;
6378 default:
6379 TRACE("Unsupported blit_op=%d\n", blit_op);
6380 return FALSE;
6384 /* Do not call while under the GL lock. */
6385 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
6386 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
6388 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
6389 struct wined3d_fb_state fb = {&dst_surface, NULL};
6391 return device_clear_render_targets(device, 1, &fb,
6392 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
6395 /* Do not call while under the GL lock. */
6396 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
6397 struct wined3d_surface *surface, const RECT *rect, float depth)
6399 const RECT draw_rect = {0, 0, surface->resource.width, surface->resource.height};
6400 struct wined3d_fb_state fb = {NULL, surface};
6402 return device_clear_render_targets(device, 0, &fb,
6403 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
6406 const struct blit_shader ffp_blit = {
6407 ffp_blit_alloc,
6408 ffp_blit_free,
6409 ffp_blit_set,
6410 ffp_blit_unset,
6411 ffp_blit_supported,
6412 ffp_blit_color_fill,
6413 ffp_blit_depth_fill,
6416 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
6418 return WINED3D_OK;
6421 /* Context activation is done by the caller. */
6422 static void cpu_blit_free(struct wined3d_device *device)
6426 /* Context activation is done by the caller. */
6427 static HRESULT cpu_blit_set(void *blit_priv, const struct wined3d_gl_info *gl_info, struct wined3d_surface *surface)
6429 return WINED3D_OK;
6432 /* Context activation is done by the caller. */
6433 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
6437 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
6438 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool, const struct wined3d_format *src_format,
6439 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool, const struct wined3d_format *dst_format)
6441 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
6443 return TRUE;
6446 return FALSE;
6449 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
6450 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
6451 const WINEDDBLTFX *fx, WINED3DTEXTUREFILTERTYPE filter)
6453 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
6454 const struct wined3d_format *src_format, *dst_format;
6455 struct wined3d_surface *orig_src = src_surface;
6456 WINED3DLOCKED_RECT dlock, slock;
6457 HRESULT hr = WINED3D_OK;
6458 const BYTE *sbuf;
6459 RECT xdst,xsrc;
6460 BYTE *dbuf;
6461 int x, y;
6463 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
6464 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
6465 flags, fx, debug_d3dtexturefiltertype(filter));
6467 xsrc = *src_rect;
6469 if (!src_surface)
6471 RECT full_rect;
6473 full_rect.left = 0;
6474 full_rect.top = 0;
6475 full_rect.right = dst_surface->resource.width;
6476 full_rect.bottom = dst_surface->resource.height;
6477 IntersectRect(&xdst, &full_rect, dst_rect);
6479 else
6481 BOOL clip_horiz, clip_vert;
6483 xdst = *dst_rect;
6484 clip_horiz = xdst.left < 0 || xdst.right > (int)dst_surface->resource.width;
6485 clip_vert = xdst.top < 0 || xdst.bottom > (int)dst_surface->resource.height;
6487 if (clip_vert || clip_horiz)
6489 /* Now check if this is a special case or not... */
6490 if ((flags & WINEDDBLT_DDFX)
6491 || (clip_horiz && xdst.right - xdst.left != xsrc.right - xsrc.left)
6492 || (clip_vert && xdst.bottom - xdst.top != xsrc.bottom - xsrc.top))
6494 WARN("Out of screen rectangle in special case. Not handled right now.\n");
6495 return WINED3D_OK;
6498 if (clip_horiz)
6500 if (xdst.left < 0)
6502 xsrc.left -= xdst.left;
6503 xdst.left = 0;
6505 if (xdst.right > dst_surface->resource.width)
6507 xsrc.right -= (xdst.right - (int)dst_surface->resource.width);
6508 xdst.right = (int)dst_surface->resource.width;
6512 if (clip_vert)
6514 if (xdst.top < 0)
6516 xsrc.top -= xdst.top;
6517 xdst.top = 0;
6519 if (xdst.bottom > dst_surface->resource.height)
6521 xsrc.bottom -= (xdst.bottom - (int)dst_surface->resource.height);
6522 xdst.bottom = (int)dst_surface->resource.height;
6526 /* And check if after clipping something is still to be done... */
6527 if ((xdst.right <= 0) || (xdst.bottom <= 0)
6528 || (xdst.left >= (int)dst_surface->resource.width)
6529 || (xdst.top >= (int)dst_surface->resource.height)
6530 || (xsrc.right <= 0) || (xsrc.bottom <= 0)
6531 || (xsrc.left >= (int)src_surface->resource.width)
6532 || (xsrc.top >= (int)src_surface->resource.height))
6534 TRACE("Nothing to be done after clipping.\n");
6535 return WINED3D_OK;
6540 if (src_surface == dst_surface)
6542 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6543 slock = dlock;
6544 src_format = dst_surface->resource.format;
6545 dst_format = src_format;
6547 else
6549 dst_format = dst_surface->resource.format;
6550 if (src_surface)
6552 if (dst_surface->resource.format->id != src_surface->resource.format->id)
6554 src_surface = surface_convert_format(src_surface, dst_format->id);
6555 if (!src_surface)
6557 /* The conv function writes a FIXME */
6558 WARN("Cannot convert source surface format to dest format.\n");
6559 goto release;
6562 wined3d_surface_map(src_surface, &slock, NULL, WINED3DLOCK_READONLY);
6563 src_format = src_surface->resource.format;
6565 else
6567 src_format = dst_format;
6569 if (dst_rect)
6570 wined3d_surface_map(dst_surface, &dlock, &xdst, 0);
6571 else
6572 wined3d_surface_map(dst_surface, &dlock, NULL, 0);
6575 bpp = dst_surface->resource.format->byte_count;
6576 srcheight = xsrc.bottom - xsrc.top;
6577 srcwidth = xsrc.right - xsrc.left;
6578 dstheight = xdst.bottom - xdst.top;
6579 dstwidth = xdst.right - xdst.left;
6580 width = (xdst.right - xdst.left) * bpp;
6582 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_COMPRESSED)
6584 UINT row_block_count;
6586 if (flags || src_surface == dst_surface)
6588 FIXME("Only plain blits supported on compressed surfaces.\n");
6589 hr = E_NOTIMPL;
6590 goto release;
6593 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
6595 if (srcheight != dstheight || srcwidth != dstwidth)
6597 WARN("Stretching not supported on compressed surfaces.\n");
6598 hr = WINED3DERR_INVALIDCALL;
6599 goto release;
6602 dbuf = dlock.pBits;
6603 sbuf = slock.pBits;
6605 row_block_count = (dstwidth + dst_format->block_width - 1) / dst_format->block_width;
6606 for (y = 0; y < dstheight; y += dst_format->block_height)
6608 memcpy(dbuf, sbuf, row_block_count * dst_format->block_byte_count);
6609 dbuf += dlock.Pitch;
6610 sbuf += slock.Pitch;
6613 goto release;
6616 if (dst_rect && src_surface != dst_surface)
6617 dbuf = dlock.pBits;
6618 else
6619 dbuf = (BYTE*)dlock.pBits+(xdst.top*dlock.Pitch)+(xdst.left*bpp);
6621 /* First, all the 'source-less' blits */
6622 if (flags & WINEDDBLT_COLORFILL)
6624 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dlock.Pitch, fx->u5.dwFillColor);
6625 flags &= ~WINEDDBLT_COLORFILL;
6628 if (flags & WINEDDBLT_DEPTHFILL)
6630 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
6632 if (flags & WINEDDBLT_ROP)
6634 /* Catch some degenerate cases here. */
6635 switch (fx->dwROP)
6637 case BLACKNESS:
6638 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,0);
6639 break;
6640 case 0xAA0029: /* No-op */
6641 break;
6642 case WHITENESS:
6643 hr = _Blt_ColorFill(dbuf,dstwidth,dstheight,bpp,dlock.Pitch,~0);
6644 break;
6645 case SRCCOPY: /* Well, we do that below? */
6646 break;
6647 default:
6648 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
6649 goto error;
6651 flags &= ~WINEDDBLT_ROP;
6653 if (flags & WINEDDBLT_DDROPS)
6655 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
6657 /* Now the 'with source' blits. */
6658 if (src_surface)
6660 const BYTE *sbase;
6661 int sx, xinc, sy, yinc;
6663 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
6664 goto release;
6666 if (filter != WINED3DTEXF_NONE && filter != WINED3DTEXF_POINT
6667 && (srcwidth != dstwidth || srcheight != dstheight))
6669 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
6670 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
6673 sbase = (BYTE*)slock.pBits+(xsrc.top*slock.Pitch)+xsrc.left*bpp;
6674 xinc = (srcwidth << 16) / dstwidth;
6675 yinc = (srcheight << 16) / dstheight;
6677 if (!flags)
6679 /* No effects, we can cheat here. */
6680 if (dstwidth == srcwidth)
6682 if (dstheight == srcheight)
6684 /* No stretching in either direction. This needs to be as
6685 * fast as possible. */
6686 sbuf = sbase;
6688 /* Check for overlapping surfaces. */
6689 if (src_surface != dst_surface || xdst.top < xsrc.top
6690 || xdst.right <= xsrc.left || xsrc.right <= xdst.left)
6692 /* No overlap, or dst above src, so copy from top downwards. */
6693 for (y = 0; y < dstheight; ++y)
6695 memcpy(dbuf, sbuf, width);
6696 sbuf += slock.Pitch;
6697 dbuf += dlock.Pitch;
6700 else if (xdst.top > xsrc.top)
6702 /* Copy from bottom upwards. */
6703 sbuf += (slock.Pitch*dstheight);
6704 dbuf += (dlock.Pitch*dstheight);
6705 for (y = 0; y < dstheight; ++y)
6707 sbuf -= slock.Pitch;
6708 dbuf -= dlock.Pitch;
6709 memcpy(dbuf, sbuf, width);
6712 else
6714 /* Src and dst overlapping on the same line, use memmove. */
6715 for (y = 0; y < dstheight; ++y)
6717 memmove(dbuf, sbuf, width);
6718 sbuf += slock.Pitch;
6719 dbuf += dlock.Pitch;
6723 else
6725 /* Stretching in y direction only. */
6726 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6728 sbuf = sbase + (sy >> 16) * slock.Pitch;
6729 memcpy(dbuf, sbuf, width);
6730 dbuf += dlock.Pitch;
6734 else
6736 /* Stretching in X direction. */
6737 int last_sy = -1;
6738 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6740 sbuf = sbase + (sy >> 16) * slock.Pitch;
6742 if ((sy >> 16) == (last_sy >> 16))
6744 /* This source row is the same as last source row -
6745 * Copy the already stretched row. */
6746 memcpy(dbuf, dbuf - dlock.Pitch, width);
6748 else
6750 #define STRETCH_ROW(type) \
6751 do { \
6752 const type *s = (const type *)sbuf; \
6753 type *d = (type *)dbuf; \
6754 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6755 d[x] = s[sx >> 16]; \
6756 } while(0)
6758 switch(bpp)
6760 case 1:
6761 STRETCH_ROW(BYTE);
6762 break;
6763 case 2:
6764 STRETCH_ROW(WORD);
6765 break;
6766 case 4:
6767 STRETCH_ROW(DWORD);
6768 break;
6769 case 3:
6771 const BYTE *s;
6772 BYTE *d = dbuf;
6773 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
6775 DWORD pixel;
6777 s = sbuf + 3 * (sx >> 16);
6778 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6779 d[0] = (pixel ) & 0xff;
6780 d[1] = (pixel >> 8) & 0xff;
6781 d[2] = (pixel >> 16) & 0xff;
6782 d += 3;
6784 break;
6786 default:
6787 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
6788 hr = WINED3DERR_NOTAVAILABLE;
6789 goto error;
6791 #undef STRETCH_ROW
6793 dbuf += dlock.Pitch;
6794 last_sy = sy;
6798 else
6800 LONG dstyinc = dlock.Pitch, dstxinc = bpp;
6801 DWORD keylow = 0xFFFFFFFF, keyhigh = 0, keymask = 0xFFFFFFFF;
6802 DWORD destkeylow = 0x0, destkeyhigh = 0xFFFFFFFF, destkeymask = 0xFFFFFFFF;
6803 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
6805 /* The color keying flags are checked for correctness in ddraw */
6806 if (flags & WINEDDBLT_KEYSRC)
6808 keylow = src_surface->SrcBltCKey.dwColorSpaceLowValue;
6809 keyhigh = src_surface->SrcBltCKey.dwColorSpaceHighValue;
6811 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
6813 keylow = fx->ddckSrcColorkey.dwColorSpaceLowValue;
6814 keyhigh = fx->ddckSrcColorkey.dwColorSpaceHighValue;
6817 if (flags & WINEDDBLT_KEYDEST)
6819 /* Destination color keys are taken from the source surface! */
6820 destkeylow = src_surface->DestBltCKey.dwColorSpaceLowValue;
6821 destkeyhigh = src_surface->DestBltCKey.dwColorSpaceHighValue;
6823 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
6825 destkeylow = fx->ddckDestColorkey.dwColorSpaceLowValue;
6826 destkeyhigh = fx->ddckDestColorkey.dwColorSpaceHighValue;
6829 if (bpp == 1)
6831 keymask = 0xff;
6833 else
6835 keymask = src_format->red_mask
6836 | src_format->green_mask
6837 | src_format->blue_mask;
6839 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
6842 if (flags & WINEDDBLT_DDFX)
6844 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
6845 LONG tmpxy;
6846 dTopLeft = dbuf;
6847 dTopRight = dbuf + ((dstwidth - 1) * bpp);
6848 dBottomLeft = dTopLeft + ((dstheight - 1) * dlock.Pitch);
6849 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
6851 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
6853 /* I don't think we need to do anything about this flag */
6854 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
6856 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
6858 tmp = dTopRight;
6859 dTopRight = dTopLeft;
6860 dTopLeft = tmp;
6861 tmp = dBottomRight;
6862 dBottomRight = dBottomLeft;
6863 dBottomLeft = tmp;
6864 dstxinc = dstxinc * -1;
6866 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
6868 tmp = dTopLeft;
6869 dTopLeft = dBottomLeft;
6870 dBottomLeft = tmp;
6871 tmp = dTopRight;
6872 dTopRight = dBottomRight;
6873 dBottomRight = tmp;
6874 dstyinc = dstyinc * -1;
6876 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
6878 /* I don't think we need to do anything about this flag */
6879 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
6881 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
6883 tmp = dBottomRight;
6884 dBottomRight = dTopLeft;
6885 dTopLeft = tmp;
6886 tmp = dBottomLeft;
6887 dBottomLeft = dTopRight;
6888 dTopRight = tmp;
6889 dstxinc = dstxinc * -1;
6890 dstyinc = dstyinc * -1;
6892 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
6894 tmp = dTopLeft;
6895 dTopLeft = dBottomLeft;
6896 dBottomLeft = dBottomRight;
6897 dBottomRight = dTopRight;
6898 dTopRight = tmp;
6899 tmpxy = dstxinc;
6900 dstxinc = dstyinc;
6901 dstyinc = tmpxy;
6902 dstxinc = dstxinc * -1;
6904 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
6906 tmp = dTopLeft;
6907 dTopLeft = dTopRight;
6908 dTopRight = dBottomRight;
6909 dBottomRight = dBottomLeft;
6910 dBottomLeft = tmp;
6911 tmpxy = dstxinc;
6912 dstxinc = dstyinc;
6913 dstyinc = tmpxy;
6914 dstyinc = dstyinc * -1;
6916 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
6918 /* I don't think we need to do anything about this flag */
6919 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
6921 dbuf = dTopLeft;
6922 flags &= ~(WINEDDBLT_DDFX);
6925 #define COPY_COLORKEY_FX(type) \
6926 do { \
6927 const type *s; \
6928 type *d = (type *)dbuf, *dx, tmp; \
6929 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
6931 s = (const type *)(sbase + (sy >> 16) * slock.Pitch); \
6932 dx = d; \
6933 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
6935 tmp = s[sx >> 16]; \
6936 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
6937 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
6939 dx[0] = tmp; \
6941 dx = (type *)(((BYTE *)dx) + dstxinc); \
6943 d = (type *)(((BYTE *)d) + dstyinc); \
6945 } while(0)
6947 switch (bpp)
6949 case 1:
6950 COPY_COLORKEY_FX(BYTE);
6951 break;
6952 case 2:
6953 COPY_COLORKEY_FX(WORD);
6954 break;
6955 case 4:
6956 COPY_COLORKEY_FX(DWORD);
6957 break;
6958 case 3:
6960 const BYTE *s;
6961 BYTE *d = dbuf, *dx;
6962 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
6964 sbuf = sbase + (sy >> 16) * slock.Pitch;
6965 dx = d;
6966 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
6968 DWORD pixel, dpixel = 0;
6969 s = sbuf + 3 * (sx>>16);
6970 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
6971 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
6972 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
6973 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
6975 dx[0] = (pixel ) & 0xff;
6976 dx[1] = (pixel >> 8) & 0xff;
6977 dx[2] = (pixel >> 16) & 0xff;
6979 dx += dstxinc;
6981 d += dstyinc;
6983 break;
6985 default:
6986 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
6987 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
6988 hr = WINED3DERR_NOTAVAILABLE;
6989 goto error;
6990 #undef COPY_COLORKEY_FX
6995 error:
6996 if (flags && FIXME_ON(d3d_surface))
6998 FIXME("\tUnsupported flags: %#x.\n", flags);
7001 release:
7002 wined3d_surface_unmap(dst_surface);
7003 if (src_surface && src_surface != dst_surface)
7004 wined3d_surface_unmap(src_surface);
7005 /* Release the converted surface, if any. */
7006 if (src_surface && src_surface != orig_src)
7007 wined3d_surface_decref(src_surface);
7009 return hr;
7012 /* Do not call while under the GL lock. */
7013 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
7014 const RECT *dst_rect, const WINED3DCOLORVALUE *color)
7016 static const RECT src_rect;
7017 WINEDDBLTFX BltFx;
7019 memset(&BltFx, 0, sizeof(BltFx));
7020 BltFx.dwSize = sizeof(BltFx);
7021 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
7022 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
7023 WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
7026 /* Do not call while under the GL lock. */
7027 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
7028 struct wined3d_surface *surface, const RECT *rect, float depth)
7030 FIXME("Depth filling not implemented by cpu_blit.\n");
7031 return WINED3DERR_INVALIDCALL;
7034 const struct blit_shader cpu_blit = {
7035 cpu_blit_alloc,
7036 cpu_blit_free,
7037 cpu_blit_set,
7038 cpu_blit_unset,
7039 cpu_blit_supported,
7040 cpu_blit_color_fill,
7041 cpu_blit_depth_fill,
7044 static HRESULT surface_init(struct wined3d_surface *surface, WINED3DSURFTYPE surface_type, UINT alignment,
7045 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
7046 UINT multisample_quality, struct wined3d_device *device, DWORD usage, enum wined3d_format_id format_id,
7047 WINED3DPOOL pool, void *parent, const struct wined3d_parent_ops *parent_ops)
7049 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
7050 const struct wined3d_format *format = wined3d_get_format(gl_info, format_id);
7051 unsigned int resource_size;
7052 HRESULT hr;
7054 if (multisample_quality > 0)
7056 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
7057 multisample_quality = 0;
7060 /* Quick lockable sanity check.
7061 * TODO: remove this after surfaces, usage and lockability have been debugged properly
7062 * this function is too deep to need to care about things like this.
7063 * Levels need to be checked too, since they all affect what can be done. */
7064 switch (pool)
7066 case WINED3DPOOL_SCRATCH:
7067 if (!lockable)
7069 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
7070 "which are mutually exclusive, setting lockable to TRUE.\n");
7071 lockable = TRUE;
7073 break;
7075 case WINED3DPOOL_SYSTEMMEM:
7076 if (!lockable)
7077 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
7078 break;
7080 case WINED3DPOOL_MANAGED:
7081 if (usage & WINED3DUSAGE_DYNAMIC)
7082 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
7083 break;
7085 case WINED3DPOOL_DEFAULT:
7086 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
7087 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
7088 break;
7090 default:
7091 FIXME("Unknown pool %#x.\n", pool);
7092 break;
7095 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
7096 FIXME("Trying to create a render target that isn't in the default pool.\n");
7098 /* FIXME: Check that the format is supported by the device. */
7100 resource_size = wined3d_format_calculate_size(format, alignment, width, height);
7101 if (!resource_size)
7102 return WINED3DERR_INVALIDCALL;
7104 surface->surface_type = surface_type;
7106 switch (surface_type)
7108 case SURFACE_OPENGL:
7109 surface->surface_ops = &surface_ops;
7110 break;
7112 case SURFACE_GDI:
7113 surface->surface_ops = &gdi_surface_ops;
7114 break;
7116 default:
7117 ERR("Requested unknown surface implementation %#x.\n", surface_type);
7118 return WINED3DERR_INVALIDCALL;
7121 hr = resource_init(&surface->resource, device, WINED3DRTYPE_SURFACE, format,
7122 multisample_type, multisample_quality, usage, pool, width, height, 1,
7123 resource_size, parent, parent_ops, &surface_resource_ops);
7124 if (FAILED(hr))
7126 WARN("Failed to initialize resource, returning %#x.\n", hr);
7127 return hr;
7130 /* "Standalone" surface. */
7131 surface_set_container(surface, WINED3D_CONTAINER_NONE, NULL);
7133 surface->texture_level = level;
7134 list_init(&surface->overlays);
7136 /* Flags */
7137 surface->flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
7138 if (discard)
7139 surface->flags |= SFLAG_DISCARD;
7140 if (lockable || format_id == WINED3DFMT_D16_LOCKABLE)
7141 surface->flags |= SFLAG_LOCKABLE;
7142 /* I'm not sure if this qualifies as a hack or as an optimization. It
7143 * seems reasonable to assume that lockable render targets will get
7144 * locked, so we might as well set SFLAG_DYNLOCK right at surface
7145 * creation. However, the other reason we want to do this is that several
7146 * ddraw applications access surface memory while the surface isn't
7147 * mapped. The SFLAG_DYNLOCK behaviour of keeping SYSMEM around for
7148 * future locks prevents these from crashing. */
7149 if (lockable && (usage & WINED3DUSAGE_RENDERTARGET))
7150 surface->flags |= SFLAG_DYNLOCK;
7152 /* Mark the texture as dirty so that it gets loaded first time around. */
7153 surface_add_dirty_rect(surface, NULL);
7154 list_init(&surface->renderbuffers);
7156 TRACE("surface %p, memory %p, size %u\n",
7157 surface, surface->resource.allocatedMemory, surface->resource.size);
7159 /* Call the private setup routine */
7160 hr = surface->surface_ops->surface_private_setup(surface);
7161 if (FAILED(hr))
7163 ERR("Private setup failed, returning %#x\n", hr);
7164 surface->surface_ops->surface_cleanup(surface);
7165 return hr;
7168 return hr;
7171 HRESULT CDECL wined3d_surface_create(struct wined3d_device *device, UINT width, UINT height,
7172 enum wined3d_format_id format_id, BOOL lockable, BOOL discard, UINT level, DWORD usage, WINED3DPOOL pool,
7173 WINED3DMULTISAMPLE_TYPE multisample_type, DWORD multisample_quality, WINED3DSURFTYPE surface_type,
7174 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_surface **surface)
7176 struct wined3d_surface *object;
7177 HRESULT hr;
7179 TRACE("device %p, width %u, height %u, format %s, lockable %#x, discard %#x, level %u\n",
7180 device, width, height, debug_d3dformat(format_id), lockable, discard, level);
7181 TRACE("surface %p, usage %s (%#x), pool %s, multisample_type %#x, multisample_quality %u\n",
7182 surface, debug_d3dusage(usage), usage, debug_d3dpool(pool), multisample_type, multisample_quality);
7183 TRACE("surface_type %#x, parent %p, parent_ops %p.\n", surface_type, parent, parent_ops);
7185 if (surface_type == SURFACE_OPENGL && !device->adapter)
7187 ERR("OpenGL surfaces are not available without OpenGL.\n");
7188 return WINED3DERR_NOTAVAILABLE;
7191 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
7192 if (!object)
7194 ERR("Failed to allocate surface memory.\n");
7195 return WINED3DERR_OUTOFVIDEOMEMORY;
7198 hr = surface_init(object, surface_type, device->surface_alignment, width, height, level, lockable,
7199 discard, multisample_type, multisample_quality, device, usage, format_id, pool, parent, parent_ops);
7200 if (FAILED(hr))
7202 WARN("Failed to initialize surface, returning %#x.\n", hr);
7203 HeapFree(GetProcessHeap(), 0, object);
7204 return hr;
7207 TRACE("Created surface %p.\n", object);
7208 *surface = object;
7210 return hr;